Making a bootable image
Re: Making a bootable image
The first sector of a FAT disk/partition is a mix of code and file system data.
1.) In the code you must skip over the data (the BIOS Parameter block) and continue after the BPB.
2.) In the script/makefile you must write only the code to the disk image. Otherwise you erase the FAT file system data.
1.) In the code you must skip over the data (the BIOS Parameter block) and continue after the BPB.
2.) In the script/makefile you must write only the code to the disk image. Otherwise you erase the FAT file system data.
Re: Making a bootable image
How large is the BPB? does it depends on the file system? From where should I should start adding code?PeterX wrote:In the code you must skip over the data (the BIOS Parameter block) and continue after the BPB
Is this also in the BPB or is some other part of the disk to not override?PeterX wrote:Otherwise you erase the FAT file system data.
Regards, Bonfra.
Re: Making a bootable image
Yes, it depends on the file system. Here is a list of the different DOS versions and their BPB:Bonfra wrote:How large is the BPB? does it depends on the file system? From where should I should start adding code?PeterX wrote:In the code you must skip over the data (the BIOS Parameter block) and continue after the BPB
Is this also in the BPB or is some other part of the disk to not override?PeterX wrote:Otherwise you erase the FAT file system data.
https://en.wikipedia.org/wiki/BIOS_parameter_block
I suggest you take the DOS 7.1 FAT32 structure which goes from offset 3 to offset 95. So from offset 96 on it's ok to write code.
No, there is no other data that should not be overwritten. When I talk about FAT file system data and BPB I mean the same.
Re: Making a bootable image
7.1 Is for FAT32, by now I'm using FAT12. Do you think I should just switch to FAT32 or use a FAT12 BPB for now?PeterX wrote:I suggest you take the DOS 7.1 FAT32 structure which goes from offset 3 to offset 95. So from offset 96 on it's ok to write code.
Or I just ignore that part of the disk to leave compatibiliy for FAT32 and continue using FAT12?
Regards, Bonfra.
Re: Making a bootable image
I suggest yes. But it's up to you. All three options should work (somehow).Bonfra wrote:Or I just ignore that part of the disk to leave compatibiliy for FAT32 and continue using FAT12?
Re: Making a bootable image
So in the bootloader file I do something like this:
And I need to copy the jump and the boot in the partition.dd excluding the biosParameterBlock.
The problem is that even if I copy only the first byte the partition is no more recognized:
This is the partition before copy:
This is the partition after copy:
Code: Select all
jmp 0x7C00 : boot
biosParameterBlock:
times 0x52-($-$$) db 0 ; BPB 7.1 FAT32 ends at 0x52.
boot:
; Actual boot code
The problem is that even if I copy only the first byte the partition is no more recognized:
Code: Select all
dd if=bin/boot/boot.bin of=partition.dd seek=0 count=1 conv=notrunc bs=1
Code: Select all
EB 3C 90 6D 6B 66 73 2E-66 61 74 00 02 04 04 00 [...]
Code: Select all
EA 3C 90 6D 6B 66 73 2E-66 61 74 00 02 04 04 00 [...]
Regards, Bonfra.
Re: Making a bootable image
Code: Select all
jmp 0x7C00 : boot
Code: Select all
jmp boot
Code: Select all
jmp short boot
nop
Re: Making a bootable image
In the bootloader:
In the terminal:
And the image results in:
But the image is still marked as unknown
Code: Select all
jmp short boot
Code: Select all
dd if=bin/boot/boot.bin of=partition.dd seek=0 count=1 conv=notrunc bs=3
Code: Select all
EB 5E 00 6D 6B 66 73 2E-66 61 74 00 02 04 04 00 [...]
Regards, Bonfra.
Re: Making a bootable image
Add the "nop". Some BIOS check for that NOP.Bonfra wrote:In the bootloader:In the terminal:Code: Select all
jmp short boot
And the image results in:Code: Select all
dd if=bin/boot/boot.bin of=partition.dd seek=0 count=1 conv=notrunc bs=3
Code: Select all
EB 5E 00 6D 6B 66 73 2E-66 61 74 00 02 04 04 00 [...]
Re: Making a bootable image
It was this! Now the image is valid. I'll do some testing to load the bootloader and I'll let you know. ThanksPeterX wrote:Add the "nop". Some BIOS check for that NOP.
Regards, Bonfra.
Re: Making a bootable image
Ok the partition works perfectly. It is created with this script:
Then I Create the disk image and copy the partition in it:
If I boot just the partition.dd with:
It prints "Hello, World!" but if i boot the disk.dd it prints nothing, instead it clears the screen (which I think is not a good thing).
Is it something with the disk reading part?
This is the code for the MBR:
Code: Select all
#Create the partition
dd if=/dev/zero of=partition.dd bs=512 count=65536 # count = [ K = megabyte; K*(1024)^2/512 ]
mkfs.vfat -F 16 partition.dd
#Add file to the partition
mcopy -i partition.dd path/to/file.eg ::/
#Add the bootloader to the partition
dd if=path/to/boot.bin of=partition.dd seek=0 count=1 conv=notrunc bs=3
dd if=path/to/boot.bin of=partition.dd seek=83 seek=83 skip=83 count=$[512-83] conv=notrunc bs=1
Code: Select all
#Create the Disk image
dd if=/dev/zero of=disk.dd bs=512 count=$[2048+65536]
echo -e "n \n p \n \n \n \n t \n 6\n a \n w" | fdisk disk.dd
#Load the MBR
dd if=bin/boot/mbr.bin of=disk.dd seek=0 count=1 conv=notrunc bs=436
#Add the partition to the disk
dd if=partition.dd of=disk.dd conv=notrunc bs=512 seek=2048
Code: Select all
qemu-system-x86_64 partition.dd
Is it something with the disk reading part?
This is the code for the MBR:
Code: Select all
bits 16
org 0x0600
start:
cli ; We do not want to be interrupted
xor ax, ax ; 0 AX
mov ds, ax ; Set Data Segment to 0
mov es, ax ; Set Extra Segment to 0
mov ss, ax ; Set Stack Segment to 0
mov sp, ax ; Set Stack Pointer to 0
.CopyLower:
mov cx, 0x0100 ; 256 WORDs in MBR
mov si, 0x7C00 ; Current MBR Address
mov di, 0x0600 ; New MBR Address
rep movsw ; Copy MBR
jmp 0:LowStart ; Jump to new Address
LowStart:
sti ; Start interrupts
mov BYTE [bootDrive], dl ; Save BootDrive
.CheckPartitions: ; Check Partition Table For Bootable Partition
mov bx, PT1 ; Base = Partition Table Entry 1
mov cx, 4 ; There are 4 Partition Table Entries
.CKPTloop:
mov al, BYTE [bx] ; Get Boot indicator bit flag
test al, 0x80 ; Check For Active Bit
jnz .CKPTFound ; We Found an Active Partition
add bx, 0x10 ; Partition Table Entry is 16 Bytes
dec cx ; Decrement Counter
jnz .CKPTloop ; Loop
jmp error.noBootablePartition ; ERROR!
.CKPTFound:
mov WORD [PToff], bx ; Save Offset
add bx, 8 ; Increment Base to LBA Address
.ReadVBR:
mov EBX, DWORD [bx] ; Start LBA of Active Partition
mov di, 0x7C00 ; We Are Loading VBR to 0x07C0:0x0000
mov cx, 1 ; Only one sector
call ReadSectors ; Read Sector
.jumpToVBR:
cmp WORD [0x7DFE], 0xAA55 ; Check Boot Signature
jne error ; Error if not Boot Signature
mov si, WORD [PToff] ; Set DS:SI to Partition Table Entry
mov dl, BYTE [bootDrive] ; Set DL to Drive Number
jmp 0x7C00 ; Jump To VBR
error:
jmp hang
.noBootablePartition:
mov si, Message.Error.NoBootablePartition
call BiosPrint
jmp hang
hang:
cli
hlt
jmp hang
absoluteSector db 0x00
absoluteHead db 0x00
absoluteTrack db 0x00
datasector dw 0x0000
cluster dw 0x0000
bpbSectorsPerCluster: DB 1
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbBytesPerSector: DW 512
;********************;
; Convert CHS to LBA ;
; Parameters: ;
; ax => CHS ;
; Returns: ;
; ax => LBA ;
;********************;
CHSLBA:
sub ax, 0x0002 ; zero base cluster number
xor cx, cx
mov cl, BYTE [bpbSectorsPerCluster] ; convert byte to word
mul cx
add ax, WORD [datasector] ; base data sector
ret
;********************;
; Convert LBA to CHS ;
; Parameters: ;
; ax => LBA ;
; Returns: ;
; ax => CHS ;
;********************;
LBACHS:
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbSectorsPerTrack] ; calculate
inc dl ; adjust for sector 0
mov BYTE [absoluteSector], dl
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbHeadsPerCylinder] ; calculate
mov BYTE [absoluteHead], dl
mov BYTE [absoluteTrack], al
ret
;***********************************;
; Reads a series of sectors ;
; Parameters: ;
; cx => Number of sectors to read ;
; ax => Starting sector ;
; es:ebx => Buffer to read to ;
;***********************************;
ReadSectors:
pusha
.start:
mov di, 0x0005 ; five retries for error
.sectorloop:
call LBACHS ; convert starting sector to CHS
mov ah, 0x02 ; BIOS read sector
mov al, 0x01 ; read one sector
mov ch, BYTE [absoluteTrack] ; track
mov cl, BYTE [absoluteSector] ; sector
mov dh, BYTE [absoluteHead] ; head
mov dl, BYTE [bootDrive] ; drive
int 0x13 ; invoke BIOS
jnc .success ; test for read error
xor ax, ax ; BIOS reset disk
int 0x13 ; invoke BIOS
dec di ; decrement error counter
jnz .sectorloop ; attempt to read again
.success:
push cx ; queue next segment
mov cx, WORD [bpbBytesPerSector]
shr cx, 4
mov dx, es
add dx, cx
mov es, dx
pop cx
inc ax ; queue next sector
loop .start ; read next sector
popa
ret
;************************;
; Parameters: ;
; si => string pointer ;
;************************;
BiosPrint:
pusha
.loop:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp .loop
.done:
popa
ret
Message.Error.NoBootablePartition db "No bootable partition found.", 13, 10, 0
Message.cazzo db "CAZZO", 13, 10, 0
bootDrive db 0 ; Our Drive Number Variable
PToff dw 0 ; Our Partition Table Entry Offset
times (0x1b4 - ($-$$)) nop ; Pad For MBR Partition Table
UID times 10 db 0 ; Unique Disk ID
PT1 times 16 db 0 ; First Partition Entry
PT2 times 16 db 0 ; Second Partition Entry
PT3 times 16 db 0 ; Third Partition Entry
PT4 times 16 db 0 ; Fourth Partition Entry
dw 0xAA55 ; Boot Signature
Regards, Bonfra.
Re: Making a bootable image
I don't know if this is the reason, but it is wrong:
This means, it counts down (via DI) and if it still fails, it continues with success.
There should be some error output and halt between "jnz .sectorloop" and ".success"
Also I have the impression you take too advanced code. You kind of copy code from somewhere without truely understanding it. It would be better for your coding success if you try something "smaller" first.
For example you can assume that partition bootsector is at sector 2048 (because FDISK does so). This is not good for a real OS, but you can concentrate more on the disk read subroutine.
Have you looked at Ralph Brown's Interrupt List for disk read routines?
Greetings
Peter
Code: Select all
int 0x13 ; invoke BIOS
jnc .success ; test for read error
xor ax, ax ; BIOS reset disk
int 0x13 ; invoke BIOS
dec di ; decrement error counter
jnz .sectorloop ; attempt to read again
.success:
There should be some error output and halt between "jnz .sectorloop" and ".success"
Also I have the impression you take too advanced code. You kind of copy code from somewhere without truely understanding it. It would be better for your coding success if you try something "smaller" first.
For example you can assume that partition bootsector is at sector 2048 (because FDISK does so). This is not good for a real OS, but you can concentrate more on the disk read subroutine.
Have you looked at Ralph Brown's Interrupt List for disk read routines?
Greetings
Peter
Re: Making a bootable image
I am going to agree with Peter here.PeterX wrote:Also I have the impression you take too advanced code. You kind of copy code from somewhere without truly understanding it.
The FAT file system occupies certain parts of the partition. This is a must. Therefore, you *must* preserve these areas when writing boot code to the partition.
The first sector of the partition, sometimes called a Logical Sector Number of zero, must contain a valid BPB for the FAT type it uses. The FAT-12 and FAT-16 use the same data region, while FAT-32 occupies a little more of the first sector. As long as you preserve these areas, you are free to add code to the rest.
Please note that the utility that is writing the code to LSN 0 will already know if it is a FAT-12, -16, or -32 file system and will act accordingly.
For FAT-12 and -16 file systems, as long as you use a short jump plus a NOP as the first three bytes of LSN 0, and skip over the BPB, you can use the remaining part of the sector as code, minus the last two bytes, which must be the 0xAA55 signature. In fact, the FAT-12 and -16 file systems allow you to use additional sectors for code starting at LSN 1. However, since the MBR code only loaded 1 sector, your code within the LSN 0 *must* load any remaining sectors to memory.
For FAT-32, LSN 1 through x are used for the file system and must be preserved. This includes a INFO sector.
However, with all three types, as long as you preserved these areas, you can have many sectors worth of code for your boot code.
As with Peter's suggestion, I suggest that you study how the FAT file system works *before* you try to write boot code for it.
As for the code you posted, it makes a lot of assumptions that a newbie will completely miss. One huge example will be the ORG 0x0600 line. Yes, this is correct, given the code doesn't make any memory references until after it moves to 0x00600, but a quick glance over and one might assume too much about this assumption.
Ben
- http://www.fysnet.net/osdesign_book_series.htm
Re: Making a bootable image
PeterX and BenLunt gave good advice. However I would also recommend to try it with a known to work MBR code first, before you write your own MBR code.Bonfra wrote:It prints "Hello, World!" but if i boot the disk.dd it prints nothing, instead it clears the screen (which I think is not a good thing).
Is it something with the disk reading part?
This is the code for the MBR
Under Windows, just use "FDISK.EXE /MBR". Under Linux, use "ms-sys --mbrdos". The code for this MBR sector can be found here (sadly just a byte array), but there's a detailed disassembly with fully commented source code here.
In theory the MBR code does nothing else than chainloading the VBR sector to 0:7C00h. If your image works with the standard code, then the problem is with your MBR code. If it still doesn't work, then the problem is with your disk image creation. FYI this site also contains all MS boot sectors (MBR and VBR alike). You can find a FAT boot sector for floppies as well as for exFAT disks.
Cheers,
bzt
Re: Making a bootable image
Yea you are right, I should start easy implementing my own stuffs.
I tried implementing this ReadSector function:
Which relies on this datas:
But the carry flag is setted by the BIOS function so it fails and I don't understand the reason
I tried implementing this ReadSector function:
Code: Select all
ReadSectors:
mov bx, 0x7C00 ; address
mov al, 1 ; Number of Sectors
mov dl, [bootDrive]
mov ch, 0x00 ; cylinder 0
mov dh, 0x20 ; head 0
mov cl, 0x21 ; disk sector number
mov ah, 0x02
int 0x13 ; Invoke BIOS
ret
But the carry flag is setted by the BIOS function so it fails and I don't understand the reason
Regards, Bonfra.