diff --git a/bin/disk.img b/bin/disk.img new file mode 100644 index 0000000..f3694b6 Binary files /dev/null and b/bin/disk.img differ diff --git a/bin/loader.bin b/bin/loader.bin new file mode 100644 index 0000000..c38ae04 Binary files /dev/null and b/bin/loader.bin differ diff --git a/bin/mbr.bin b/bin/mbr.bin new file mode 100644 index 0000000..8445126 Binary files /dev/null and b/bin/mbr.bin differ diff --git a/obj/boot/loader.bin b/obj/boot/loader.bin new file mode 100644 index 0000000..c38ae04 Binary files /dev/null and b/obj/boot/loader.bin differ diff --git a/obj/boot/mbr.bin b/obj/boot/mbr.bin new file mode 100644 index 0000000..8445126 Binary files /dev/null and b/obj/boot/mbr.bin differ diff --git a/src/ChangeLog b/src/ChangeLog index 0f5b213..e95ff0b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -14,4 +14,6 @@ 2018/12/06 - Actually started project, began MBR, decided directories organization, created this file and others 2018/12/08 - MBR actually supports Long Mode Compatibility Verification - Added A20 line Enabling to MBR +2018/12/21 - Boot is now in two stages. First stage is 512 MBR code that loads second stage loader from FAT16. + That second stage loader enables A20, switches into long mode and write colored text =D diff --git a/src/Makefile b/src/Makefile index 6d51fb0..8caea17 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,7 +29,7 @@ SYSTDIR=kaleid/system all: bootloader -boot.mbr.s: $(BOOTDIR)/mbr.s $(BOOTDIR)/mbr.inc +boot.mbr.s: $(BOOTDIR)/mbr.s $(ASM) $(BOOTFLAGS) $(BOOTDIR)/mbr.s -o $(OBJDIR)/boot/mbr.bin boot.loader.s: $(BOOTDIR)/loader.s diff --git a/src/boot/common.inc b/src/boot/common.inc deleted file mode 100644 index fbd5616..0000000 --- a/src/boot/common.inc +++ /dev/null @@ -1,27 +0,0 @@ -;=----------------------------------------------------------------------------=; -; GNU GPL OS/K ; -; ; -; Authors: spectral` ; -; NeoX ; -; ; -; Desc: Functions for the OS/K loaders ; -; (x86_64 architecture only) ; -;=----------------------------------------------------------------------------=; - -;-------------------------------------------------------------------; -; Prints the (null-terminated) string at address @es:@si ; -;-------------------------------------------------------------------; -PrintB: - pushad ; saving context -.pLoop: - lodsb ; loads the character at @es:@si in @al - mov bh, 0x00 - mov bl, 0x07 - cmp al,0 - je .pEnd ; while @al, i.e. while we're not hitting '\0' - mov ah, 0x0E - int 0x10 ; print character @al in color @bl (BIOS interrupt) - jmp .pLoop -.pEnd: - popad ; restores context - ret diff --git a/src/boot/loader.s b/src/boot/loader.s index 81efc52..c1e7c40 100644 --- a/src/boot/loader.s +++ b/src/boot/loader.s @@ -7,114 +7,119 @@ ; Desc: Kernel (second stage) Loader for OS/K ; ; (x86_64 architecture only) ; ;=----------------------------------------------------------------------------=; +%define TRAM 0x0B8000 ; [T]ext[RAM] +%define VRAM 0x0A0000 ; [V]ideo[RAM] +[BITS 16] [ORG 0x1000] - -pop word [Bootdrv] -jmp main + +mov ax, cs +mov ds, ax +mov es, ax + +mov [Bootdrv], dl +jmp 0x0000:main ;; DATA -%define TRAM 0x0B8000 ; [T]ext[RAM] -%define VRAM 0x0A0000 ; [V]ideo[RAM] -Bootdrv db 0x00 + +Bootdrv db 0 VGA_HEIGHT dq 0 VIDEO_MODE dw 0 ;; GDT WITH DOC -GDT64: - NULL_SELECTOR: ;; null selector within 64 bits - dw GDT_LENGTH ; limit of GDT - dw GDT64 ; linear address of GDT - dd 0x0 - CODE_SELECTOR: ;; 32-bit code selector (ring 0) - dw 0x0FFFF ; Segment Limit 15:00 - db 0x0, 0x0, 0x0 ; Base Address 23:00 - db 10011010b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- 1 when segment used. - ; | | | | | | `------ 1 when writable. - ; | | | | | `------- 1 if "conformant". Don't know what is it... spectral ? xD - ; | | | | `-------- 1 always - ; | | | `--------- 1 for segment descriptor, 0 for system descriptor - ; | | `---------- DPL !!! 0 for ring 0 - ; | `----------- DPL (2/2) - ; `------------ 1 if in physical memory, 0 if page fault - db 11001111b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- Limit 16 - ; | | | | | | `------ Limit 17 - ; | | | | | `------- Limit 18 - ; | | | | `-------- Limit 19 - ; | | | `--------- available for use - ; | | `---------- 0 always - ; | `----------- size of data. 1 for 32bits - ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) - db 0x0 ; Base Address 31:24 - - DATA_SELECTOR: ;; flat data selector (ring 0) - dw 0x0FFFF ; Segment Limit 15:00 - db 0x0, 0x0, 0x0 ; Base Address 23:00 - db 10010010b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- 1 when segment used. - ; | | | | | | `------ 1 when writable. - ; | | | | | `------- expansion direction. 1 for a LIFO - ; | | | | `-------- 1 always - ; | | | `--------- 1 for segment descriptor, 0 for system descriptor - ; | | `---------- DPL !!! 0 for ring 0 - ; | `----------- DPL (2/2) - ; `------------ 1 if in physical memory, 0 if page fault - db 10001111b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- Limit 16 - ; | | | | | | `------ Limit 17 - ; | | | | | `------- Limit 18 - ; | | | | `-------- Limit 19 - ; | | | `--------- available for use - ; | | `---------- 0 always - ; | `----------- size of data. 1 for 32bits - ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) - db 0x0 ; Base Address 31:24 - - LONG_SELECTOR: ;; 64-bit code selector (ring 0) - dw 0x0FFFF ; Segment Limit 15:00 - db 0x0, 0x0, 0x0 ; Base Address 23:00 - db 10011010b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- 1 when segment used. - ; | | | | | | `------ 1 when writable. - ; | | | | | `------- 1 if "conformant". Don't know what is it... spectral ? xD - ; | | | | `-------- 1 always - ; | | | `--------- 1 for segment descriptor, 0 for system descriptor - ; | | `---------- DPL !!! 0 for ring 0 - ; | `----------- DPL (2/2) - ; `------------ 1 if in physical memory, 0 if page fault - db 10101111b ; |7|6|5|4|3|2|1|0| - ; | | | | | | | `----- Limit 16 - ; | | | | | | `------ Limit 17 - ; | | | | | `------- Limit 18 - ; | | | | `-------- Limit 19 - ; | | | `--------- available for use - ; | | `---------- 0 always - ; | `----------- size of data. 1 for 32bits - ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) - db 0x0 ; Base Address 31:24 - GDT_LENGTH: +GDT64: + NULL_SELECTOR: ;; null selector within 64 bits + dw GDT_LENGTH ; limit of GDT + dw GDT64 ; linear address of GDT + dd 0x0 + CODE_SELECTOR: ;; 32-bit code selector (ring 0) + dw 0x0FFFF ; Segment Limit 15:00 + db 0x0, 0x0, 0x0 ; Base Address 23:00 + db 10011010b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- 1 when segment used. + ; | | | | | | `------ 1 when writable. + ; | | | | | `------- 1 if "conformant". Don't know what is it... spectral ? xD + ; | | | | `-------- 1 always + ; | | | `--------- 1 for segment descriptor, 0 for system descriptor + ; | | `---------- DPL !!! 0 for ring 0 + ; | `----------- DPL (2/2) + ; `------------ 1 if in physical memory, 0 if page fault + db 11001111b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- Limit 16 + ; | | | | | | `------ Limit 17 + ; | | | | | `------- Limit 18 + ; | | | | `-------- Limit 19 + ; | | | `--------- available for use + ; | | `---------- 0 always + ; | `----------- size of data. 1 for 32bits + ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) + db 0x0 ; Base Address 31:24 + + DATA_SELECTOR: ;; flat data selector (ring 0) + dw 0x0FFFF ; Segment Limit 15:00 + db 0x0, 0x0, 0x0 ; Base Address 23:00 + db 10010010b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- 1 when segment used. + ; | | | | | | `------ 1 when writable. + ; | | | | | `------- expansion direction. 1 for a LIFO + ; | | | | `-------- 1 always + ; | | | `--------- 1 for segment descriptor, 0 for system descriptor + ; | | `---------- DPL !!! 0 for ring 0 + ; | `----------- DPL (2/2) + ; `------------ 1 if in physical memory, 0 if page fault + db 10001111b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- Limit 16 + ; | | | | | | `------ Limit 17 + ; | | | | | `------- Limit 18 + ; | | | | `-------- Limit 19 + ; | | | `--------- available for use + ; | | `---------- 0 always + ; | `----------- size of data. 1 for 32bits + ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) + db 0x0 ; Base Address 31:24 + + LONG_SELECTOR: ;; 64-bit code selector (ring 0) + dw 0x0FFFF ; Segment Limit 15:00 + db 0x0, 0x0, 0x0 ; Base Address 23:00 + db 10011010b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- 1 when segment used. + ; | | | | | | `------ 1 when writable. + ; | | | | | `------- 1 if "conformant". Don't know what is it... spectral ? xD + ; | | | | `-------- 1 always + ; | | | `--------- 1 for segment descriptor, 0 for system descriptor + ; | | `---------- DPL !!! 0 for ring 0 + ; | `----------- DPL (2/2) + ; `------------ 1 if in physical memory, 0 if page fault + db 10101111b ; |7|6|5|4|3|2|1|0| + ; | | | | | | | `----- Limit 16 + ; | | | | | | `------ Limit 17 + ; | | | | | `------- Limit 18 + ; | | | | `-------- Limit 19 + ; | | | `--------- available for use + ; | | `---------- 0 always + ; | `----------- size of data. 1 for 32bits + ; `------------ 0 if limit is in Bytes, 1 if it's in pages (4ko) + db 0x0 ; Base Address 31:24 + GDT_LENGTH: -[BITS 16] disable_cursor: - pushf - push eax - push edx - - mov dx, 0x3D4 - mov al, 0xA ; low cursor shape register - out dx, al - - inc dx - mov al, 0x20 ; bits 6-7 unused, bit 5 disables the cursor, bits 0-4 control the cursor shape - out dx, al - - pop edx - pop eax - popf - ret - + pushf + push eax + push edx + + mov dx, 0x3D4 + mov al, 0xA ; low cursor shape register + out dx, al + + inc dx + mov al, 0x20 ; bits 6-7 unused, bit 5 disables the cursor, bits 0-4 control the cursor shape + out dx, al + + pop edx + pop eax + popf + ret + get_dimensions: push eax push ebx @@ -125,7 +130,7 @@ get_dimensions: pop ebx pop eax ret - + ;-----------------------------------------------------------------------; ; Checks if the CPU is compatible with 64-bits operating systems ; ; If the 21th bit of the eax register is set, then CPUID is supported ; @@ -160,11 +165,26 @@ Is64bits: stc ret -;; INCLUDES -%include "boot/common.inc" ; for PrintB +PrintB: +;---------------------------------------------------; +; Print out a simple string. ; +; ; +; Expects: DS:SI = String to print ; +; ; +; Returns: None ; +; ; +;---------------------------------------------------; + lodsb ; Load byte from ds:si to al + or al, al ; If al is empty stop looping + jz .done ; Done looping and return + mov ah, 0x0e ; Teletype output + int 0x10 ; Video interupt + jmp PrintB ; Loop untill string is null + .done: + ret -main: - ;; compatibility check +main: + ;; compatibility check mov si, Init call PrintB pop si @@ -174,7 +194,7 @@ main: mov si, Pass call PrintB - ;; enabling A20 + ;; Enabling A20 mov si, EnA20 call PrintB pop si @@ -186,11 +206,11 @@ main: call PrintB ;; DISABLING CURSOR BLINKING AND GETTING INFOS - call get_dimensions - call disable_cursor - - ;;GO GDT64 - cli ; disable interrupts + call get_dimensions + call disable_cursor + + ;;GO GDT64 + cli ; disable interrupts lgdt [GDT64] ;; ACTIVATE PROTECTED MODE mov eax, cr0 @@ -203,7 +223,7 @@ main: ; | ; `------ Paging bit mov cr0, eax - + push dword [VIDEO_MODE] push dword [VGA_HEIGHT] jmp (CODE_SELECTOR-GDT64):main32 @@ -244,11 +264,11 @@ main32: .make_page_entries: stosd add edi, 4 - add eax, 0x1000 + add eax, 0x1000 loop .make_page_entries ;; pointing pml4 mov eax, 0x70000 ; Bass address of PML4 - mov cr3, eax ; load page-map level-4 base + mov cr3, eax ; load page-map level-4 base ;; ACTIVATE LONG MODE mov ecx, 0xC0000080 ; address of MSR @@ -257,7 +277,7 @@ main32: wrmsr ; write MSR ;; ACTIVATE PAGING - mov eax, cr0 + mov eax, cr0 or eax, 0x80000000 ; make bit 31 (PG = Paging) to 1 : ; |1|000000000000000000000000000000 ; | @@ -271,6 +291,23 @@ main32: [BITS 64] ;; FUNCTIONS +clear: +;-----------------------------------------------------------------------; +; x64/LM Clear Text Screen Function ; +;-----------------------------------------------------------------------; + mov qword [NextTRAM], TRAM + mov edi, TRAM + push rsi + push rdi + push rcx + mov ah, 0 + mov al, 0 + mov rcx, 0x4000 ; traditionnal value + rep stosw ; fill screen with al while cx > 0 + pop rcx + pop rsi + pop rdi + ret write: ;-----------------------------------------------------------------------; @@ -297,8 +334,8 @@ write: jmp .pLoop .pEnd: pop rdi - pop rsi - ret + pop rsi + ret .lf: mov rax, [VGA_HEIGHT64] add [NextTRAM], rax ; Cursor moving @@ -318,13 +355,15 @@ write: jmp .pLoop -;; DATA +;; DATA txt db 0x09, " Switching to Long Mode... OK", 0x0A, 0x0D, 0 -Init db 0x09, " Checking CPUID", 0 -EnA20 db 0x09, " Enabling A20 line", 0 -NoLongMode db 0x0A, 0x0D, "ERROR: Your computer is not designed for x64 OS", 0 +Init db "Booting OS/K !", 0x0D, 0x0A, 0x0D, 0x0A, 0x09, " Checking CPUID...",0 +Reinit db "Booting OS/K !", 0x0D, 0x0A, 0x0D, 0x0A, 0 +CPUIDD db 0x09, " Checking CPUID...", 0 +EnA20 db 0x09, " Enabling A20 line...", 0 +NoLongMode db 0x0A, 0x0D, "ERROR : Your computer is not designed for x64 OS", 0 Pass db " OK", 0x0A, 0x0D, 0 -NextTRAM dq 0x0B8AA0 ; Last position of cursor +NextTRAM dq 0x0B8000 ; Last position of cursor VIDEO_MODE64 dq 0 VGA_HEIGHT64 dq 0 VGA_X dq 0x0 @@ -335,30 +374,39 @@ main64: pop qword [VIDEO_MODE64] ;; INITIALIZE STACK mov rsp, 0x9F000 + + call clear - ;; NOTIFYING + ;; Printing + mov bl, 0x0B + mov esi, Reinit + call write + + mov bl, 0x0F + mov esi, CPUIDD + call write + + mov bl, 0x0A + mov esi, Pass + call write + + mov bl, 0x0F + mov esi, EnA20 + call write + + mov bl, 0x0A + mov esi, Pass + call write + mov bl, 0x0A mov esi, txt call write - mov bl, 0x0B - mov esi, msg - call write - - mov bl, 0x0C - mov esi, msg - call write - mov bl, 0x0D mov esi, msg call write - - mov bl, 0x0E - mov esi, msg - call write - - ;call KERNEL - jmp $ + + jmp Die [BITS 16] ErrorNo64: @@ -368,6 +416,4 @@ Die: cli hlt ; die nooooow retf - -times 1024-($-$$) db 144 KERNEL: diff --git a/src/boot/mbr.inc b/src/boot/mbr.inc deleted file mode 100644 index 682cfd7..0000000 --- a/src/boot/mbr.inc +++ /dev/null @@ -1,11 +0,0 @@ -;=----------------------------------------------------------------------------=; -; GNU GPL OS/K ; -; ; -; Authors: spectral` ; -; NeoX ; -; ; -; Desc: Functions for the OS/K mbr ; -; (x86_64 architecture only) ; -;=----------------------------------------------------------------------------=; - - diff --git a/src/boot/mbr.s b/src/boot/mbr.s index c409122..9bbb40f 100644 --- a/src/boot/mbr.s +++ b/src/boot/mbr.s @@ -4,185 +4,338 @@ ; Authors: spectral` ; ; NeoX ; ; ; -; Desc: Master Boot Record for OS/K ; +; Desc: Bootsector for OS/K ; ; (x86_64 architecture only) ; ;=----------------------------------------------------------------------------=; -%define SECOND_STAGE 0x100 ;about to change +;; BOOT "SEGMENT" +%define BOOT_SEG 0x07c0 ; (BOOT_SEG << 4) + BOOT_OFF = 0x007c00 +%define BOOT_OFF 0x0000 +;; STACK "SEGMENT" +%define STACK_SEG 0x0600 ; (STACK_SEG << 4) + STACK_OFF = 0x007000 +%define STACK_OFF 0x1000 -[BITS 16] ; real mode -[ORG 0x7C00] ; address where the BIOS/UEFI CSM is loading us +;; DISK BUFFER "SEGMENT" +%define BUFFER_SEG 0x2000 ; (BUFFER_SEG << 4) + BUFFER_OFF = 0x020000 +%define BUFFER_OFF 0x0000 -jmp short Start +;; SECOND STAGE LOADER "SEGMENT" +%define LOAD_SEG 0x0000 ; (LOAD_SEG << 4) + LOAD_OFF = 0x030000 +%define LOAD_OFF 0x1000 -;; File Allocation Table Disk Identifiers -nop -OEM_ID db "GPL OS/K" -BytesPerSector dw 0x0200 -SectorsPerCluster db 0x08 -ReservedSectors dw 0x0020 -TotalFATs db 0x02 -MaxRootEntries dw 0x0000 -NumberOfSectors dw 0x0000 -MediaDescriptor db 0xF8 -SectorsPerFAT dw 0x0000 -SectorsPerTrack dw 0x003D -SectorsPerHead dw 0x0002 -HiddenSectors dd 0x00000000 -TotalSectors dd 0x00FE3B1F -BigSectorsPerFAT dd 0x00000778 -Flags dw 0x0000 -FSVersion dw 0x0000 -RootDirectoryStart dd 0x00000002 -FSInfoSector dw 0x0001 -BackupBootSector dw 0x0006 +[BITS 16] ; Ensure 16-bit code (because fuck UEFI) -TIMES 12 DB 0 ; going to the next offset +;--------------------------------------------------- +; Disk description table +;--------------------------------------------------- +Intro: -DriveNumber db 0x00 -ReservedByte db 0x00 -Signature db 0x29 -VolumeID dd 0xFFFFFFFF -VolumeLabel db "OS/K BDISK " -SystemID db "FAT32 " + jmp short _start ; Jump over the BIOS PARAMETER BLOCK + nop ; Required by BIOS to recognize the Disk +BPB: + %define OEMName bp+0x03 ; Disk label + %define bytesPerSector bp+0x0b ; Bytes per sector + %define sectorsPerCluster bp+0x0d ; Sectors per cluster + %define reservedSectors bp+0x0e ; Reserved sectors + %define fats bp+0x10 ; Number of fats + %define rootDirEntries bp+0x11 ; Number of entries in root dir + %define sectors bp+0x13 ; Logical sectors + %define mediaType bp+0x15 ; Media descriptor byte + %define fatSectors bp+0x16 ; Sectors per FAT + %define sectorsPerTrack bp+0x18 ; Sectors per track + %define heads bp+0x1a ; Number of sides/heads + %define hiddenSectors bp+0x1c ; Hidden sectors + %define hugeSectors bp+0x20 ; LBA sectors + %define biosBootdrvNum bp+0x24 ; Bootdrv number + %define reserved bp+0x25 ; This is not used + %define bootSignature bp+0x26 ; Bootdrv signature + %define volumeId bp+0x27 ; Volume ID + %define volumeLabel bp+0x2b ; Volume Label + %define fatTypeLabel bp+0x36 ; File system type + times 0x3b db 0x00 -Start: - ;; saving registers for future - pushad - pushfd - pushf +;; ENTRY POINT +_start: + jmp BOOT_SEG:$+5 ; Fix the cs:ip registers with a vaudou magical trip - ;; dl contains the boot drive. Saving it. - mov [Bootdrv], dl - - ;; hello world - push si - mov si, Starting - call PrintB +bootstrap: + mov ax, BOOT_SEG ; Set segments to the location of the bootloader + mov ds, ax + mov es, ax + + ;; INIT STACK + cli + mov ax, STACK_SEG ; Init the staaaaaack + mov ss, ax ; Continue init the staaaaaaaack + mov sp, STACK_OFF ; Ok man, the stack is in 4K :O + sti + mov bp, (0x7c0-STACK_SEG) << 4 ; Correct bp for the disk description table ! - ;; SWITCHING TO LOADER - mov si, Switch - call PrintB - pop si - mov cx, word [SectorsPerCluster] ; size of a cluster in sectors is stored in cx - - ; computing location of the begining of the Data area and store in ax - mov al, byte [TotalFATs] ; Total number of FATs - mul word [BigSectorsPerFAT] ; Number of sectors for a FAT - add ax, word [ReservedSectors] ; Find the start of the Data area - mov word [Datasector], ax ; Store the begining of the Data area + ;; INITIALIZE BOOT DISK + or dl, dl ; Verifying dl points actually to the boot drive + jz loadRoot + mov byte [Bootdrv], dl ; Another soul (the disk) saved ! + mov ah, 0x08 + int 0x13 ; int 0x13 : read drive parameters/geom + jc loadRoot + and cx, 0x003f ; Maximum sector number is the high bits 6-7 of cl + mov word [sectorsPerTrack], cx ; And whose low 8 bits are in ch + mov dl, dh ; Convert the maximum head number to a word with another vaudou magical trip + xor dh, dh + inc dx ; because head numbers start at zero + mov word [heads], dx ; Another soul (the heads number) saved ! + + + ;; LOAD THE ROOT DIRECTORY FROM DISK +loadRoot: + xor cx, cx + mov ax, 32 ; Size of root dir = (rootDirEntries * 32) / bytesPerSector + mul word [rootDirEntries] ; multiply by the total size of the root directory + div word [bytesPerSector] ; divide by the number of bytes used per sector + xchg cx, ax + + mov al, byte [fats] ; Location of root dir = (fats * fatSectors) + reservedSectors + mul word [fatSectors] ; multiply by the sectors used + add ax, word [reservedSectors] ; increase ax by the reserved sectors + mov word [userData], ax ; Start of user data = startOfRoot + numberOfRoot + add word [userData], cx ; Add the size and location of the root directory - ; reading 1st data cluster into memory (7C00:0200) - mov ax, word [RootDirectoryStart] - call ClusterLBA - mov bx, 0x0200 ; copy 1st data cluter above bootcode - call ReadSectors + mov di, BUFFER_SEG ; Set the extra segment to the disk buffer + mov es, di + mov di, BUFFER_OFF ; Set es:di and load the root directory into the disk buffer + call readSectors ; Read the sectoooooooors ! - ; Point Index register to 1st File Entry - mov di, 0x0200 + 0x20 - ;Point to the offset where the file location information contains - mov dx, word [di + 0x001A] - mov word [Cluster], dx - ;Set up the segments where the loader needs to be loaded ;) - mov ax, 0100h ; set ES:BX = 0100:0000 - mov es, ax - xor bx, bx - - ;Read the cluster which contains the loader - mov cx, 0x0008 - mov ax, word [Cluster] - call ClusterLBA - call ReadSectors - - ;Jump to the location where the loader was loded - popf - popfd - popad - push word [Bootdrv] - call dword SECOND_STAGE:0000 + ;; FIND THE SECOND STAGE LOADER + mov di, BUFFER_OFF ; Set es:di to the disk buffer + mov cx, word [rootDirEntries] ; Search through all of the root dir entries + xor ax, ax ; Clear ax for the file entry offset +searchRoot: + xchg cx, dx ; Save cx because it's a loop counter + mov si, filename ; Load the filename + mov cx, 11 ; Compare first 11 bytes + rep cmpsb ; Compare si and di cx times + je loadFat ; We found the LOADEEEEEEEER!!! + add ax, 32 ; File entry offset + mov di, BUFFER_OFF ; Point back to the start of the entry + add di, ax ; Add the offset to point to the next entry + xchg dx, cx + loop searchRoot ; Continue to search for the file - ; Exiting - mov ah, 0x00 - int 0x19 ; warm boot computer + ;; ERROR... + mov si, fileNotFound ; Could not find the file + call print + + ;; REBOOT +reboot: + xor ax, ax + int 0x16 ; Get a single keypress + mov ah, 0x0e ; Teletype output + mov al, 0x0d ; Carriage return + int 0x10 ; Video interupt + mov al, 0x0a ; Line feed + int 0x10 ; Video interupt + mov al, 0x0a ; Line feed + int 0x10 ; Video interupt + xor ax, ax + int 0x19 ; Reboot the system - ;;END OF MBR + ;; LOAD THE FAT FROM THE FILE +loadFat: + mov ax, word [es:di + 15] ; Get the file cluster at offset 26 + push ax ; Store the FAT cluster + xor ax, ax ; Size of fat = (fats * fatSectors) + mov al, byte [fats] ; Move number of fats into al + mul word [fatSectors] ; Move fat sectors into bx + mov cx, ax ; Store in cx + mov ax, word [reservedSectors] ; Convert the first fat on the disk + mov di, BUFFER_OFF ; Set es:di and load the fat sectors into the disk buffer + call readSectors ; Read the sectooooooooooors !!! -;;----------------------------------------------------------------------------;; -;; DATA -Starting db 0x0A, 0x0D, 0x07, "GNU GPL OS/K", 0x0A, 0x0D, 0x0A, 0x0D, 0 -Switch db 0x09, " Loading Second Stage", 0 -AbsoluteSector db 0x00 -AbsoluteHead db 0x00 -AbsoluteTrack db 0x00 -Cluster dw 0x0000 -Datasector dw 0x0000 + ;; LOAD THE CLUSTERS OF THE LOADER AND JUMP +loadFile: + mov di, LOAD_SEG + mov es, di ; Set es:bx to where the file will load + mov di, LOAD_OFF + pop ax ; File cluster restored + call readClusters ; Read clusters from the file + mov dl, byte [Bootdrv] ; Pass the boot Bootdrv into dl + jmp LOAD_SEG:LOAD_OFF ; Jump to the file loaded! -Bootdrv db 0x00 + hlt ; This should never be hit... + ; ... + ; ... + ; I hope.... + ; ... -;; INCLUDES -%include "boot/common.inc" ; for PrintB -;; INTERNAL FUNCTIONS -;-------------------------------------------------------------------; -; loading second stage loader (loader.s) ; -;-------------------------------------------------------------------; -ReadSectors: -.main: - mov di, 0x0005 ; five retries for error -.sectorloop: + +;---------------------------------------------------; +; FUNCTIONS ; +;---------------------------------------------------; + +readClusters: +;---------------------------------------------------; +; Read file clusters, starting at the given cluster,; +; expects FAT to be loaded into the disk buffer. ; +; Please note that this may allocate up to 128KB ; +; of ram. ; +; ; +; Expects: AX = Starting cluster ; +; ES:DI = Location to load clusters ; +; ; +; Returns: None ; +; ; +;---------------------------------------------------; push ax push bx push cx - call LbaToChs - 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 - 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 + push dx + push di + push es + .clusterLoop: + xor bh, bh + xor dx, dx + push ax ; Get the cluster start = (cluster - 2) * sectorsPerCluster + userData + sub ax, 2 ; Subtract 2 + mov bl, byte [sectorsPerCluster] ; Sectors per cluster is a byte value + mul bx ; multiply (cluster - 2) * sectorsPerCluster + add ax, word [userData] ; add the userData + xor ch, ch + mov cl, byte [sectorsPerCluster] ; Sectors to read + call readSectors ; Read the sectors + pop ax ; Current cluster number + xor dx, dx + .calculateNextSector16: ; Get the next sector for FAT16 (cluster * 2) + mov bx, 2 ; Multiply the cluster by two (cluster is in ax) + mul bx + .loadNextSector: + push ds + push si + mov si, BUFFER_SEG + mov ds, si ; Temporarly set ds:si to the FAT buffer + mov si, BUFFER_OFF + add si, ax ; Point to the next cluster in the FAT entry + mov ax, word [ds:si] ; Load ax to the next cluster in FAT + pop si + pop ds + .nextSectorCalculated: + cmp ax, 0xfff8 ; Check if we are at the end of the file? + jae .done + add di, 512 ; Add to the pointer offset + jnc .clusterLoop + .fixBuffer: ; An error will occur if the buffer in memory + mov dx, es ; overlaps a 64k page boundry, when di overflows + add dh, 0x10 ; it will trigger the carry flag, so correct + mov es, dx ; extra segment by 0x1000 + jmp .clusterLoop ; Load the next file cluster + .done: + pop es + pop di + pop dx pop cx pop bx pop ax - jnz .sectorloop ; attempt to read again - int 0x18 -.success: - pop cx - pop bx - pop ax - add bx, WORD [BytesPerSector] ; queue next buffer - inc ax ; queue next sector - loop .main ; read next sector ret -;-------------------------------------------------------------------; -; Converts LBA Adresses to CHS ; -;-------------------------------------------------------------------; -LbaToChs: - xor dx, dx ; prepare dx:ax for operation - div WORD [SectorsPerTrack] ; calculate - inc dl ; adjust for sector 0 - mov BYTE [AbsoluteSector], dl - xor dx, dx ; prepare dx:ax for operation - div WORD [SectorsPerHead] ; calculate - mov BYTE [AbsoluteHead], dl - mov BYTE [AbsoluteTrack], al + +readSectors: +;---------------------------------------------------; +; Read sectors starting at a given sector by ; +; the given times and load into a buffer. Please ; +; note that this may allocate up to 128KB of ram. ; +; ; +; Expects: AX = Starting sector ; +; CX = Number of sectors to read ; +; ES:DI = Location to load sectors ; +; ; +; Returns: None ; +; ; +;---------------------------------------------------; + push ax + push bx + push cx + push dx + push di + push es + mov bx, di ; Convert es:di to es:bx for int 13h + .sectorLoop: + push ax + push cx + xor dx, dx + div word [sectorsPerTrack] ; Divide the lba (value in ax) by sectorsPerTrack + mov cx, dx ; Save the absolute sector value + inc cx + xor dx, dx ; Divide by the number of heads + div word [heads] ; to get absolute head and track values + mov dh, dl ; Move the absolute head into dh + mov ch, al ; Low 8 bits of absolute track + shl ah, 1 ; High 2 bits of absolute track + shl ah, 1 + shl ah, 1 + shl ah, 1 + shl ah, 1 + shl ah, 1 + or cl, ah ; Now cx is set with respective track and sector numbers + mov dl, byte [Bootdrv] ; Set correct Bootdrv for int 13h + mov di, 5 ; Try five times to read the sector because i love 5 + .attemptRead: + mov ax, 0x0201 ; Read Sectors func of int 13h, read one sector + int 0x13 ; Call int 13h (BIOS disk I/O) + jnc .readOk ; If no carry set, the sector has been read + xor ah, ah ; Reset Bootdrv func of int 13h + int 0x13 ; Call int 13h (BIOS disk I/O) + dec di ; Decrease read attempt counter + jnz .attemptRead ; Try to read the sector again + mov si, diskError ; Error reading the disk :/ + call print + jmp reboot + .readOk: + pop cx + pop ax + inc ax ; Increase the next sector to read + add bx, word [bytesPerSector] ; Add to the buffer address for the next sector + jnc .nextSector + .fixBuffer: ; An error will occur if the buffer in memory + mov dx, es ; overlaps a 64k page boundry, when bx overflows + add dh, 0x10 ; it will trigger the carry flag, so correct + mov es, dx ; es segment by 0x1000 + .nextSector: + loop .sectorLoop + pop es + pop di + pop dx + pop cx + pop bx + pop ax ret -;-------------------------------------------------------------------; -; Converts FAT Adresses to LBA ; -;-------------------------------------------------------------------; - ClusterLBA: - sub ax, 0x0002 ; zero base cluster number - xor cx, cx - mov cl, BYTE [SectorsPerCluster] ; convert byte to word - mul cx - add ax, WORD [Datasector] ; base data sector + +print: +;---------------------------------------------------; +; Print out a simple string. ; +; ; +; Expects: DS:SI = String to print ; +; ; +; Returns: None ; +; ; +;---------------------------------------------------; + lodsb ; Load byte from ds:si to al + or al, al ; If al is empty stop looping + jz .done ; Done looping and return + mov ah, 0x0e ; Teletype output + int 0x10 ; Video interupt + jmp print ; Loop untill string is null + .done: ret - -End: - times 510-($-$$) db 144 ; NOP until 510 - dw 0xAA55 ; magic word + +;; LOVELY DATA + fileNotFound db "FStage ERR : NO LOADER", 0 ; File was not found + diskError db "FStage ERR : DISK", 0 ; Error while reading from the disk + userData dw 0 ; Start of the data sectors + Bootdrv db 0 ; Boot Bootdrv number + filename db "LOADER BIN" ; Filename + +;; END + times 510 - ($ - $$) db 0 ; Pad remainder of boot sector with zeros + dw 0xaa55 ; Boot signature +