~
Joined: Sun Sep 19, 2010 9:45 am Posts: 29
|
![Post Post](./styles/serenity/imageset/icon_topic_latest.gif) Testing Non-Emulated Boot from CD/DVD
I have now what can be the start of investigating and building and easy-to-understand document about how to boot from CD/DVD. For this I have used Nero (or another burning program of your choice) that can produce a bootable ISO image. I have used also the code of John Fine's bootf02.zip code, but more heavily documented by me and all the code put in one single file. This is for a boot record for a floppy disk. The trick here is to use this floppy sector as the boot record to burn into the ISO image.
This code originally detects the drive we booted from, and thus I have made a slight modification here, in which we set DL to 0:Code: ;> cs = 0 ;>> dl = drive we were booted from ;INIT: enable protected mode (time 1 of 2) to load GDT ;INIT: enable protected mode (time 1 of 2) to load GDT ;INIT: enable protected mode (time 1 of 2) to load GDT
;Force floppy A (THIS IS THE TRICK TO BOOT FROM CD/DVD ;BUT LOAD THE KERNEL FROM THE FLOPPY): ;; xor dx,dx
With this, we can boot and execute this code from a CD/DVD, which in turn will parse the floppy drive A looking for the KERNEL.BIN file.All of the FAT12 fields used are read from the boot record in the CD/DVD, so with this method we can only use 1.44 MB standard floppies, as long as we use the unmodified fields in the code below. Interesting, isn't it? Code: %define resbytes 510-(unused-_00h_jmp)
ORG 7C00h
_00h_jmp: db 0EBh ;jmps 003E db 03Ch ;2 (2) _02h_nop: nop ;1 (3)
_03h_OEMid db "OEMIdent" ;8 (11) _0Bh_bytesPerSect dw 0200h ;2 (13) _0Dh_sectsPerClus db 001h ;1 (14) _0Eh_resrvedSects dw 0001h ;2 (16) _10h_numOfFATs db 002h ;1 (17) _11h_numRootDirEntries dw 00E0h ;2 (19) _13h_numSectors dw 0B40h ;2 (21) _15h_mediaType db 0F0h ;1 (22) _16h_numFATsectors dw 0009h ;2 (24) _18h_sectorsPerTrack dw 0012h ;2 (26) _1Ah_numHeads dw 0002h ;2 (28) _1Ch_numHiddenSects dd 00000000h ;4 (32) _20h_numSectorsHuge dd 00000000h ;4 (36) _24h_driveNumber db 00h ;1 (37) _25h_reserved db 00h ;1 (38) _26h_signature db 29h ;1 (39) _27h_volumeID db "????" ;4 (43) _28h_volumeLabel db "VolumeLabel" ;11 (54) _36h_FSType db "FAT12 " ;8 (62)
;INIT: 448 free bytes: ;INIT: 448 free bytes: ;INIT: 448 free bytes: ; pusha
; mov al,18 ;640x480x16 mode ; mov ah,0 ;service ; int 10h
; popa
;> cs = 0 ;>> dl = drive we were booted from ;INIT: enable protected mode (time 1 of 2) to load GDT ;INIT: enable protected mode (time 1 of 2) to load GDT ;INIT: enable protected mode (time 1 of 2) to load GDT
;Force floppy A: ;; xor dx,dx
start: cli ;{0} lgdt [cs:GDT] ;Load GDT. This is how we would ;exactly access the GDT in ;real mode mov ecx, CR0 ;Switch to protected mode inc cx ;set PE bit mov CR0, ecx ;{5} here we activate protected mode ;END: enable protected mode (time 1 of 2) to load GDT ;END: enable protected mode (time 1 of 2) to load GDT ;END: enable protected mode (time 1 of 2) to load GDT
;;INIT: enable A20 ;;INIT: enable A20 ;;INIT: enable A20 ;registers modified in this INIT--END portion: ;AX (EAX) ;registers with values to reuse in the next INIT--END portion: ;AH (cleared to 0)
.5: in al, 0x64 ;Enable A20 {4A}. Port 0x64 is the ;KBC port at the motherboard.
test al, 2 ;See if bit 2 at this port is 1, which means ;that he KBC is not ready.
jnz .5 ;Repeat until but 2 of port 0x64 is 0, which means ;that the KBC is ready for commands.
mov al, 0xD1 ;This command is to write the status byte. ;This is the so-called *WRITE OUTPUT PORT*. out 0x64, al ;Here we send it and it gets executed.
.6: in al, 0x64 ;Read the byte at port 0x64. and ax, byte 2 ;See if this bit is 0. ;NOTE: this will leave AL to 0 at once, which ; will be used in the next ; INIT--END block. jnz .6 ;Repeat until the KBC is ready ;(bit 2 to 0)
mov al, 0xDF ;Set the configuration bits to send.
out 0x60, al ;Send this parameter to the data port ;of the KBC. At this point is where the ;A20 line is enabled. ;;END: enable A20 ;;END: enable A20 ;;END: enable A20
;;INIT: configuration of memory and register parameters ;;INIT: configuration of memory and register parameters ;;INIT: configuration of memory and register parameters ;registers that are modified in this INIT--END portion: ;AX (EAX) 0 ;DS 0 ;ES 0x800 ;SS 0 ;SP 0x800
;registers with values to reuse in the next INIT--END portion: ;apparently ALL of the previous ones
;> ah = 0 ;> dl = drive we were booted from
mov al, SELDat32 ;Selector for 4Gb data seg. What we do here ;is to take the address of the second selector ;of the GDT. ;We are using the 8-bit register AL to hold the ;16-bit data selector number because AH is ;already set to 0, and with this we save 1 byte ;or so of boot space.
mov ds, ax ;{2} Extend limit for ds. Typically ;would get the value 0010h. (16) mov es, ax ;Extend limit for es. ES would also ;be set to 16.
dec cx ;Switch back to real mode. From the start, ECX ;contained the value of CR0 with PE ;bit set. Here we disable the PE bit again, ;and we put it into CR0 in the next ;instruction. mov CR0, ecx ;{5} sti ;v2012-05-31
;Here we are in Real Mode: ;Here we are in Real Mode: ;Here we are in Real Mode: mov [_00h_jmp], dl ;Save drive number we came from. Since ;we won't use again the first byte of code from ;the boot, the byte of *jmp 03Eh* instruction, ;we will use that byte as the variable space for ;the disk number. We must recognize that this is ;pretty clever.
mov sp, 0x800 ;{1B}. Configure the end of stack at 0x800
xor eax, eax ;Segment. 32 bits set to 0 mov ds, ax ;DS (Data Segment) set to 0 mov ss, ax ;SS (Stack Segment) set to 0 mov es, sp ;Read directory at 800:0 {1C}. ES ;(Extra Segment) 0x800. Apparently we use ;ES:DS or ES:SS to access the disk. ;By now, the conflict that seems to be here ;with the sack doesn't matter much, since we ;won't save (much) data nor make calls to ;routines. ;;END: configuration of memory and register parameters ;;END: configuration of memory and register parameters ;;END: configuration of memory and register parameters
;;INIT: Configure disk information ;;INIT: Configure disk information ;;INIT: Configure disk information ;> eax = 00000000
;Here we are still in Real Mode since the previous INIT--END: ;Here we are still in Real Mode since the previous INIT--END: ;Here we are still in Real Mode since the previous INIT--END: mov al, [_10h_numOfFATs] ;Number of FATs mul byte [_16h_numFATsectors] ;Times size of FAT |(in sectors?) add ax, [_0Eh_resrvedSects] ;Plus Sectors before first FAT ;(_10h_numOfFATs*_16h_numFATsectors)+_0Eh_resrvedSects ;eax = LBN of Root directory
movzx edi,word [_11h_numRootDirEntries] ;Root directory entries push di ; used again later dec di ;Convert to number of sectors ;Then, this means that ;_11h_numRootDirEntries-1 is equals to ;the number of sectors. But exactly why? ;Maybe every sector represents an entry.
shr di, 4 ;16 directory entries per sector inc di
call read_sectors ;Call to ahead address ;;END: Configure disk information ;;END: Configure disk information ;;END: Configure disk information
;;INIT: Search file name in the root directory ;;INIT: Search file name in the root directory ;;INIT: Search file name in the root directory ;> eax = LBN of root directory ;> edi = length of root directory in sectors ;> [sp] = length of root directory in entries ;> esi = 00000000
;Here we are still in Real Mode since the past 2 INIT--END blocks: ;Here we are still in Real Mode since the past 2 INIT--END blocks: ;Here we are still in Real Mode since the past 2 INIT--END blocks: lea ebp, [eax+edi] ;ebp = LBN of cluster 2
pop bx ;Root directory entries. Recover the value ;of DI stored in BX xor di, di ;Point at directory {1C} .20: mov si, kernelFile ;Name of file we want. We take the 16-bit ;address of the string. xor ecx, ecx mov cl, 11 ;Number of bytes to read (length of the string ;"KERNEL BIN") a32 rep cmpsb ;Found the file?. Here is where we use ;the 11 in CL je found ;Yes add cl, 21 ;Offset to next directory entry add edi, ecx ;Advance to next entry dec bx ;Loop through all entries jnz .20 ;According to the result of a bit in FLAGS, produced by ;*dec bx*. Concretely the bit that indicates that the operation ;has set the value of the operand down to 0.
;Couldn't find file in directory boot_error: disk_error: mov ax, 0xE07 ;{3} int 10h ;Video service. Here we will "ring" the ;bell. jmp short $ ;Since this in an error, we create a loop that stops ;the operation of the machine. ;;END: Search file name in the root directory ;;END: Search file name in the root directory ;;END: Search file name in the root directory
;;INIT: Read the file according to what the previous INIT--END found ;;INIT: Read the file according to what the previous INIT--END found ;;INIT: Read the file according to what the previous INIT--END found ;>> ecx = 00000000 ;> es = 800 ;> es:edi = Directory entry of file ;> ebp = LBN of cluster 2 ;> eax = 0000????
found: push word [es:edi+0xF] ;Starting cluster of file mov di, [_16h_numFATsectors] ;Size of FAT (in sectors) mov ax, [_0Eh_resrvedSects] ;LBN of FAT call read_sectors ;Call to ahead address
mov bx, 0x4000 mov es, bx ;es = 0x4000 mov edi, 0x100000-0x40000 ;{1D}{4B} One megabyte minus ES base ;;END: Read the file according to what the previous INIT--END found ;;END: Read the file according to what the previous INIT--END found ;;END: Read the file according to what the previous INIT--END found
;;INIT: Final processes, configuration of paging and pass control to the kernel ;;INIT: Final processes, configuration of paging and pass control to the kernel ;;INIT: Final processes, configuration of paging and pass control to the kernel .10:
;>> ecx = 0000???? ;> [sp] = Next cluster of file ;> esi = 0000???? ;>> edx = 0000???? ;> es:edi = Destination address ;> ebp = LBN of cluster 2 ;> ds = 0
xor eax, eax pop si ;Next cluster of file mov bx, si cmp si, 0xFF8 ;Valid cluster? jae eof ;No: assume end of file ;Yes: (c-bit set) rcr bx, 1 ;bx = 0x8000 + cluster/2 mov bx, [bx+si] ;Get word containing FAT entry jnc .11 ;Entry is low 12 bits shr bx, 4 ;Entry was high 12 bits .11: and bh, 0xF ;Mask to just 12 bits push bx ;Save cluster after next push di ;Save destination address {7} mov al, [_0Dh_sectsPerClus] ;Size of each cluster mov di, ax ; (in sectors) dec si dec si mul esi ;Times cluster number minus 2 add eax, ebp ;Plus LBN of cluster 2 call read_sectors ;Read that cluster
;> ecx = 0000???? ;>> edx = 0000???? ;> di = Clustersize in sectors ;> esi = 0 ;>> ebp = LBN of cluster 2 ;> [sp] = Bottom 16-bits of destination address {7} ;>> [sp+2] = Following cluster ;> ds = 0 ;> es = 4000
mov cx, di ;Cluster size in sectors xchg ch, cl ;Cluster size in words pop di ;Restore destination address {7} es a32 rep movsw jmp short .10 ;Loop until end of file
;;;INIT: paging ;;;INIT: paging ;;;INIT: paging ;> eax = 0 ;> edx = 0000???? ;> bx = 0FF?
eof: mov dx, 0x9C00 mov es, dx ;es = 9C00 xor di, di ;{1E} Address of page tables WRT es mov dh, 4096/256 ;edx = 4096 .10: mov cx, 1024 mov al, 00000111b ;7 .20: stosd add eax, edx int 8 ;{8} loop .20 shr eax, 2 ;{4C} (first time only) 4Mb / 4 = 1Mb neg bl ;Done just one page? jns .10 ;Yes: do one more
cli ;{6}
mov eax, 0x9C007 ;First page tbl pointer in page dir stosd ;{1H} mov ax, (1024-3)*2 xchg ax, cx rep stosw mov ax, 0xD007 ;0FF800000 page tbl pointer stosd ;{1F} mov ah, 0xE0 ;Page directory self pointer stosd ;{1G} mov al, 0 mov CR3, eax ;Set up page directory mov eax, CR0 ;Turn on paging and protected mode ;; or eax, 0x80000001 or eax, 0x00000001 ;v2012-05-31 cli ;v2012-05-31 mov CR0, eax mov cl, SELDat32 ;Setup ds and es push cx ;{5} pop ds mov es, cx ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; jmp dword SELCod32:0xFF800000 ;Go a32 jmp dword SELCod32:0x100000 ;Go ;;;END: paging ;;;END: paging ;;;END: paging
;;END: Final processes, configuration of paging and pass control to the kernel ;;END: Final processes, configuration of paging and pass control to the kernel ;;END: Final processes, configuration of paging and pass control to the kernel
;;INIT: read_sectors ;;INIT: read_sectors ;;INIT: read_sectors read_sectors: ; Input: ; EAX = LBN ; DI = sector count ; ES = segment ; Output: ; EBX high half cleared ; DL = drive # ; EDX high half cleared ; ESI = 0 ; Clobbered: ; BX, CX, DH
push eax push di push es
.10: push eax ;LBN
cdq ;edx = 0 movzx ebx, byte [_18h_sectorsPerTrack] div ebx ;EAX=track ;EDX=sector-1 mov cx, dx ;CL=sector-1 ;CH=0 sub bl, dl ;BX = max transfer before end of track cmp di, bx ;Do we want more than that? ja .20 ;Yes, do just this much now mov bx, di ;No, do it all now .20: mov esi, ebx ;Save count for this transfer.
inc cx ;CL=Sector number xor dx, dx mov bl, [_1Ah_numHeads] div ebx ;EAX=cylinder ;EDX=head
mov dh, dl ;Head mov dl, [_00h_jmp] ;Drive number xchg ch, al ;CH=Low 8 bits of cylinder number; AL=0 shr ax, 2 ;AL[6:7]=High two bits of cylinder or cl, al ;CX = Cylinder and sector
mov ax, si ;Sector count mov ah, 2 ;Read xor bx, bx push ax int 13h pop ax jnc .30
int 13h ;If at second you don't succeed, give up jc near disk_error
.30: pop eax add eax, esi ;Advance LBN
push si shl si, 5 mov bx, es add bx, si ;Advance segment mov es, bx pop si
sub di, si ja .10
pop es pop di pop eax xor si, si ret ;;END: read_sectors ;;END: read_sectors ;;END: read_sectors
kernelFile db 'KERNEL BIN'
;INIT: GDT ;INIT: GDT ;INIT: GDT GDT: _SELNull equ 0 ;WARNING: This selector, besides being the pointer to the GDT GDT_size: ; is the null selector. dw GDTsize GDT_actualptr: dd GDT dw 0x0000
_SELCod32 equ 8 dw 0FFFFh ; bits 0-15 length dw 00000h ; bits 0-15 base address db 0 ; bits 16-23 base address db 10011010b ; bits P,DPL,DT and type db 11001111b ; bits G,D and bits 16-19 length db 0 ; bits 24-31 base address
_SELDat32 equ 16 ;this is the "plain data selector" dw 0FFFFh ; bits 0-15 length dw 00000h ; bits 0-15 base address db 0 ; bits 16-23 base address db 10010010b ; bits P,DPL,DT and type db 11001111b ; bits G,D and bits 16-19 length db 0 ; bits 24-31 base address GDT_end:
GDTsize equ (GDT_end-GDT)-1 ;END: GDT ;END: GDT ;END: GDT
unused: times resbytes db 0x55
;END: 448 free bytes ;END: 448 free bytes ;END: 448 free bytes
_1F_55AA_signature dw 0xAA55 ;2 (512)
SELNull equ 0 SELCod32 equ 8 SELDat32 equ 16
The next step now is to study the ISO file format/file system, analyze the existing image we produced, reconstruct it manually using pure assembly, and then produce documentation explaining how one can build a proper ISO image manually. Finally, we could use it to load a kernel from the CD/DVD itself, or load a kernel choosing from floppy or FAT hard disks to begin with (and maybe also USB storage).
_________________ Live Development (click image links for full size): PC 1: PC 2:
|