|
| 1 | +; ============================================================================= |
| 2 | +; Pure64 MBR -- a 64-bit OS/software loader written in Assembly for x86-64 systems |
| 3 | +; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT |
| 4 | +; |
| 5 | +; This Master Boot Record will load Pure64 from a pre-defined location on the |
| 6 | +; hard drive without making use of the file system. |
| 7 | +; |
| 8 | +; In this code we are expecting a BMFS-formatted drive. With BMFS the Pure64 |
| 9 | +; binary is required to start at sector 16 (8192 bytes from the start). A small |
| 10 | +; check is made to make sure Pure64 was loaded by comparing a signature. |
| 11 | +; ============================================================================= |
| 12 | + |
| 13 | +; Default location of the second stage boot loader. This loads |
| 14 | +; 32 KiB from sector 16 into memory at 0x8000 |
| 15 | +%define DAP_SECTORS 64 |
| 16 | +%define DAP_STARTSECTOR 16 |
| 17 | +%define DAP_ADDRESS 0x8000 |
| 18 | +%define DAP_SEGMENT 0x0000 |
| 19 | + |
| 20 | +BITS 16 |
| 21 | +org 0x7C00 |
| 22 | + |
| 23 | +entry: |
| 24 | + jmp bootcode ; Jump past the BPB data |
| 25 | + nop |
| 26 | + |
| 27 | +; BPB (BIOS Parameter Block) |
| 28 | +dq 0 ; OEM identifier |
| 29 | +dw 0 ; Bytes per sector |
| 30 | +db 0 ; Sectors per cluster |
| 31 | +dw 0 ; Reserved sectors |
| 32 | +db 0 ; Number of FATs |
| 33 | +dw 0 ; Number of root directory entries |
| 34 | +dw 0 ; The total sectors in the logical volume |
| 35 | +db 0 ; Media descriptor type |
| 36 | +dw 0 ; Number of sectors per FAT |
| 37 | +dw 0 ; Number of sectors per track |
| 38 | +dw 0 ; Number of heads or sides on the storage media |
| 39 | +dd 0 ; Number of hidden sectors |
| 40 | +dd 0 ; Large sector count |
| 41 | + |
| 42 | +; EBPB (Extended Boot Record) |
| 43 | +dd 0 ; Sectors per FAT |
| 44 | +dw 0 ; Flags |
| 45 | +dw 0 ; FAT version number |
| 46 | +dd 0 ; The cluster number of the root directory |
| 47 | +dw 0 ; The sector number of the FSInfo structure |
| 48 | +dw 0 ; The sector number of the backup boot sector |
| 49 | +dq 0 ; Reserved |
| 50 | +dd 0 ; Reserved |
| 51 | +db 0 ; Drive number |
| 52 | +db 0 ; Flags in Windows NT |
| 53 | +db 0 ; Signature |
| 54 | +dd 0 ; Volume ID 'Serial' number |
| 55 | +times 11 db 0 ; Volume label string |
| 56 | +dq 0 ; System identifier string. Always "FAT32 " |
| 57 | + |
| 58 | +bootcode: |
| 59 | + cli ; Disable interrupts |
| 60 | + cld ; Clear direction flag |
| 61 | + xor eax, eax |
| 62 | + mov ss, ax |
| 63 | + mov es, ax |
| 64 | + mov ds, ax |
| 65 | + mov sp, 0x7C00 |
| 66 | + sti ; Enable interrupts |
| 67 | + |
| 68 | + mov [DriveNumber], dl ; BIOS passes drive number in DL |
| 69 | + |
| 70 | + ; Configure first serial port |
| 71 | + mov dx, 0 ; Port |
| 72 | + mov ax, 0x00E3 ; Init port, 9600bps 8N1 |
| 73 | + int 0x14 |
| 74 | + |
| 75 | + ; Output message to serial |
| 76 | + mov si, msg_Load |
| 77 | + call output_serial |
| 78 | + |
| 79 | +; Get the BIOS E820 Memory Map |
| 80 | +; https://wiki.osdev.org/Detecting_Memory_(x86)#BIOS_Function:_INT_0x15,_EAX_=_0xE820 |
| 81 | +; The code below is from https://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map |
| 82 | +; inputs: es:di -> destination buffer for 24 byte entries |
| 83 | +; outputs: bp = entry count, trashes all registers except esi |
| 84 | +; The function below creates a memory map at address 0x6000 and the records are: |
| 85 | +; 64-bit Base |
| 86 | +; 64-bit Length |
| 87 | +; 32-bit Type (1 = normal, 2 reserved, ACPI reclaimable) |
| 88 | +; 32-bit ACPI |
| 89 | +; 64-bit Padding |
| 90 | +do_e820: |
| 91 | + mov edi, 0x00006000 ; location that memory map will be stored to |
| 92 | + xor ebx, ebx ; ebx must be 0 to start |
| 93 | + xor bp, bp ; keep an entry count in bp |
| 94 | + mov edx, 0x0534D4150 ; Place "SMAP" into edx |
| 95 | + mov eax, 0xe820 |
| 96 | + mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry |
| 97 | + mov ecx, 24 ; ask for 24 bytes |
| 98 | + int 0x15 |
| 99 | + jc nomemmap ; carry set on first call means "unsupported function" |
| 100 | + mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? |
| 101 | + cmp eax, edx ; on success, eax must have been reset to "SMAP" |
| 102 | + jne nomemmap |
| 103 | + test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) |
| 104 | + je nomemmap |
| 105 | + jmp jmpin |
| 106 | +e820lp: |
| 107 | + mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call |
| 108 | + mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry |
| 109 | + mov ecx, 24 ; ask for 24 bytes again |
| 110 | + int 0x15 |
| 111 | + jc memmapend ; carry set means "end of list already reached" |
| 112 | + mov edx, 0x0534D4150 ; repair potentially trashed register |
| 113 | +jmpin: |
| 114 | + jcxz skipent ; skip any 0 length entries |
| 115 | + cmp cl, 20 ; got a 24 byte ACPI 3.X response? |
| 116 | + jbe notext |
| 117 | + test byte [es:di + 20], 1 ; if so: is the "ignore this data" bit clear? |
| 118 | + je skipent |
| 119 | +notext: |
| 120 | + mov ecx, [es:di + 8] ; get lower dword of memory region length |
| 121 | + test ecx, ecx ; is the qword == 0? |
| 122 | + jne goodent |
| 123 | + mov ecx, [es:di + 12] ; get upper dword of memory region length |
| 124 | + jecxz skipent ; if length qword is 0, skip entry |
| 125 | +goodent: |
| 126 | + inc bp ; got a good entry: ++count, move to next storage spot |
| 127 | + add di, 32 ; Pad to 32 bytes for each record |
| 128 | +skipent: |
| 129 | + test ebx, ebx ; if ebx resets to 0, list is complete |
| 130 | + jne e820lp |
| 131 | +nomemmap: |
| 132 | +memmapend: |
| 133 | + xor eax, eax ; Create a blank record for termination (32 bytes) |
| 134 | + mov ecx, 8 |
| 135 | + rep stosd |
| 136 | + |
| 137 | +; Enable the A20 gate |
| 138 | +set_A20: |
| 139 | + in al, 0x64 |
| 140 | + test al, 0x02 |
| 141 | + jnz set_A20 |
| 142 | + mov al, 0xD1 |
| 143 | + out 0x64, al |
| 144 | +check_A20: |
| 145 | + in al, 0x64 |
| 146 | + test al, 0x02 |
| 147 | + jnz check_A20 |
| 148 | + mov al, 0xDF |
| 149 | + out 0x60, al |
| 150 | + |
| 151 | + ; Video modes aren't supported in this boot sector |
| 152 | + ; Clear the video detail memory for Pure64 |
| 153 | + mov edi, 0x5F00 |
| 154 | + xor eax, eax |
| 155 | + mov ecx, 64 |
| 156 | + rep stosd |
| 157 | + |
| 158 | + ; Read the 2nd stage boot loader into memory. |
| 159 | + mov ah, 0x42 ; Extended Read |
| 160 | + mov dl, [DriveNumber] ; http://www.ctyme.com/intr/rb-0708.htm |
| 161 | + mov si, DAP |
| 162 | + int 0x13 |
| 163 | + jc halt |
| 164 | + |
| 165 | + ; Check signature |
| 166 | +; mov eax, [0x8000] |
| 167 | +; cmp eax, 0x00017EE9 ; Match against the Pure64 binary |
| 168 | +; jne halt |
| 169 | + |
| 170 | + ; Output message to serial |
| 171 | + mov si, msg_Ok |
| 172 | + call output_serial |
| 173 | + |
| 174 | + mov bl, 'B' ; 'B' as we booted via BIOS |
| 175 | + |
| 176 | + ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode. |
| 177 | + cli ; No more interrupts |
| 178 | + lgdt [cs:GDTR32] ; Load GDT register |
| 179 | + mov eax, cr0 |
| 180 | + or al, 0x01 ; Set protected mode bit |
| 181 | + mov cr0, eax |
| 182 | + jmp 8:0x8000 ; Jump to 32-bit protected mode |
| 183 | + |
| 184 | +;magic_fail: |
| 185 | +; mov si, msg_Error |
| 186 | +; call output_serial |
| 187 | +halt: |
| 188 | + hlt |
| 189 | + jmp halt |
| 190 | +;------------------------------------------------------------------------------ |
| 191 | + |
| 192 | + |
| 193 | +;------------------------------------------------------------------------------ |
| 194 | +; Output a string via serial |
| 195 | +; IN: SI - Address of start of string |
| 196 | +output_serial: ; Output string via serial |
| 197 | + pusha |
| 198 | +output_serial_next_char: |
| 199 | + mov dx, 0 ; First serial port |
| 200 | + mov ah, 0x01 ; SERIAL - WRITE CHARACTER TO PORT |
| 201 | + lodsb ; Get char from string |
| 202 | + cmp al, 0 |
| 203 | + je output_serial_done ; If char is zero, end of string |
| 204 | + int 0x14 ; Otherwise, output it |
| 205 | + jmp short output_serial_next_char |
| 206 | +output_serial_done: |
| 207 | + popa |
| 208 | + ret |
| 209 | +;------------------------------------------------------------------------------ |
| 210 | + |
| 211 | +msg_Load db "MBR ", 0 |
| 212 | +msg_Ok db "OK", 0 |
| 213 | +msg_Error db "Error!", 0 |
| 214 | + |
| 215 | +align 16 |
| 216 | +GDTR32: ; Global Descriptors Table Register |
| 217 | +dw gdt32_end - gdt32 - 1 ; limit of GDT (size minus one) |
| 218 | +dq gdt32 ; linear address of GDT |
| 219 | + |
| 220 | +align 16 |
| 221 | +gdt32: |
| 222 | +dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null descriptor |
| 223 | +dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code descriptor |
| 224 | +dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data descriptor |
| 225 | +gdt32_end: |
| 226 | + |
| 227 | +align 4 |
| 228 | + |
| 229 | +DAP: |
| 230 | +db 0x10 |
| 231 | +db 0x00 |
| 232 | +dw DAP_SECTORS |
| 233 | +dw DAP_ADDRESS |
| 234 | +dw DAP_SEGMENT |
| 235 | +dq DAP_STARTSECTOR |
| 236 | + |
| 237 | +DriveNumber db 0x00 |
| 238 | + |
| 239 | +times 446-$+$$ db 0 |
| 240 | + |
| 241 | +; Partition entries (4x 16-bytes) |
| 242 | + |
| 243 | +times 510-$+$$ db 0 |
| 244 | + |
| 245 | +dw 0xAA55 ; Boot signature |
| 246 | + |
| 247 | + |
| 248 | +; EOF |
0 commit comments