1; Copyright 2011, Jean-Loïc Charroud, jcharroud@free.fr 2; Distributed under the terms of the MIT License or LGPL v3 3; 4; Haiku (C) Copyright Haiku 2011 5; MBR Boot code 6; 7; assemble the Master boot record with: 8; yasm -f bin -O5 -o mbr.bin mbr.S 9; 10; assemble the MBR's code (does not contain the partiton table 11; nor the MAGIC code) with: 12; yasm -f bin -O5 -o mbrcode.bin mbr.S -dMBR_CODE_ONLY=1 13 14 15;%define DEBUG 1 16;%define MBR_CODE_ONLY 1 17 18 19;CONST 20 %assign DISKSIG 440 ; Disk signature offset 21 %assign PT_OFF 0x1be ; Partition table offset 22 %assign MAGIC_OFF 0x1fe ; Magic offset 23 %assign MAGIC 0xaa55 ; Magic bootable signature 24 %assign SECSIZE 0x200 ; Size of a single disk sector 25 %assign FLG_ACTIVE 0x80 ; active partition flag 26 %assign SECTOR_COUNT 0x01 ; Number of record to load from 27 ; the ctive partition 28 29 %assign LOAD 0x7c00 ; Load address 30 %assign EXEC 0x600 ; Execution address 31 %assign HEAP EXEC+SECSIZE ; Execution address 32 33;BIOS calls 34 %assign BIOS_VIDEO_SERVICES 0x10; ah - function 35 %assign BIOS_DISK_SERVICES 0x13 36 %assign BIOS_KEYBOARD_SERVICES 0X16 37 %assign BIOS_BASIC 0X18 38 %assign BIOS_REBOOT 0X19 39 40 ;BIOS calls parameters 41 ; video services 42 %assign WRITE_CHAR 0x0e; al - char 43 ; bh - page? 44 45 ; disk services 46 %assign READ_DISK_SECTORS 0x02; dl - drive 47 ; es:bx - buffer 48 ; dh - head 49 ; ch7:0 - track7:0 50 ; cl7:6 - track9:8 51 ; 5:0 - sector 52 ; al - sector count 53 ; -> al - sectors read 54 %assign READ_DRV_PARAMETERS 0x08; dl - drive 55 ; -> cl - max cylinder 9:8 56 ; - sectors per track 57 ; ch - max cylinder 7:0 58 ; dh - max head 59 ; dl - number of drives (?) 60 %assign CHK_DISK_EXTENTIONS 0x41; bx - 0x55aa 61 ; dl - drive 62 ; -> success: carry clear 63 ; ah - extension version 64 ; bx - 0xaa55 65 ; cx - support bit mask 66 ; 1 - Device Access using the 67 ; packet structure 68 ; 2 - Drive Locking and 69 ; Ejecting 70 ; 4 - Enhanced Disk Drive 71 ; Support (EDD) 72 ; -> error: carry set 73 %assign EXTENDED_READ 0x42; dl - drive 74 ; ds:si - address packet 75 ; -> success: carry clear 76 ; -> error: carry set 77 78 %assign FIXED_DSK_SUPPORT 0x1 ; flag indicating fixed disk 79 ; extension command subset 80 81 ; keyboard services 82 %assign READ_CHAR 0x00; -> al - ASCII char 83 84;MACRO 85 ; nicer way to get the size of a structure 86 %define sizeof(s) s %+ _size 87 88 ; using a structure in a another structure definition 89 %macro nstruc 1-2 1 90 resb sizeof(%1) * %2 91 %endmacro 92 93 ; nicer way to access GlobalVariables 94 %macro declare_var 1 95 %define %1 HEAP + GlobalVariables. %+ %1 96 %endmacro 97 98 %macro puts 1 99 mov si, %1 100 call _puts 101 %endmacro 102 103 %macro error 1 104 mov si, %1 105 jmp _error 106 %endmacro 107 108;TYPEDEFS 109 ; 64 bit value 110 struc quadword 111 .lower resd 1 112 .upper resd 1 113 endstruc 114 115 struc CHS_addr 116 .head resb 1 117 .sector resb 1 118 .cylindre resb 1 119 endstruc 120 121 struc PartitionEntry 122 .status resb 1 123 .CHS_first nstruc CHS_addr 124 .type resb 1 125 .CHS_last nstruc CHS_addr 126 .LBA_start resd 1 127 .LBA_size resd 1 128 endstruc 129 130 ; address packet as required by the EXTENDED_READ BIOS call 131 struc AddressPacket 132 .packet_size resb 1 133 .reserved resb 1 134 .block_count resw 1 135 .buffer resd 1 136 .sector nstruc quadword 137 ;.long_buffer nstruc quadword 138 ; We don't need the 64 bit buffer pointer. The 32 bit .buffer is more 139 ; than sufficient. 140 endstruc 141 142 ; Structure containing the variables that don't need pre-initialization. 143 ; this structure will be allocated onto our "heap". 144 struc GlobalVariables 145 .boot_drive_id resd 1 146 .boot_partition resd 1 147 .address_packet nstruc AddressPacket 148 endstruc 149 150;alias for easy access to our global variables 151 declare_var boot_drive_id 152 declare_var boot_partition 153 declare_var address_packet 154 155;///////////////////////////////////////////////////////////////////////// 156;// A 512 byte MBR boot manager that simply boots the active partition. // 157;///////////////////////////////////////////////////////////////////////// 158; 16 bit code 159SECTION .text 160BITS 16 161ORG EXEC ; MBR is loaded at 0x7c00 but relocated at 0x600 162start: ; we run the LOADed code 163 cli ; disable interrupts 164 cld ; clear direction flag (for string operations) 165 init: 166 xor ax, ax ; Zero 167 mov es, ax ; Set up extra segment 168 mov ds, ax ; Set up data segment 169 mov ss, ax ; Set up stack segment 170 mov sp, LOAD ; Set up stack pointer 171 172 ;init our heap allocated variables with zeros 173 mov di, HEAP ;start adress 174 mov cx, sizeof(GlobalVariables) ;size 175 rep ; while(cx--) 176 stosb ; es[di++]:=al; 177 178 ; Relocate ourself to a lower address so that we are out of the way 179 ; when we load in the bootstrap from the partition to boot. 180 reloc: 181 mov si, LOAD ; Source 182 ; init AddressPacket.buffer now since LOAD is in 'si' 183 mov [address_packet+AddressPacket.buffer],si 184 mov byte[address_packet+AddressPacket.packet_size],sizeof(AddressPacket) 185 mov byte[address_packet+AddressPacket.block_count],SECTOR_COUNT 186 187 mov di, EXEC ; Destination 188 mov ch, 1 ; count cx:=256 (cl cleared by precedent rep call) 189 rep ; while(cx--) 190 movsw ; es[di++]:=ds[si++] 191 ; //di and si are incremented by sizeof(word) 192 jmp word 0x0000:continue; FAR jump to the relocated "continue" (some 193 ; BIOSes initialise CS to 0x07c0 so we must set 194 ; CS correctly) 195 196continue: ; Now we run EXEC_based relocated code 197 sti ; enable interrupts 198 %ifdef DEBUG 199 puts kMsgStart 200 %endif 201 202search_active_partition: 203 mov si,EXEC+PT_OFF ; point to first table entry 204 mov al,04 ; there are 4 table entries 205 .loop: ; SEARCH FOR AN ACTIVE ENTRY 206 cmp byte[si],FLG_ACTIVE ; is this the active entry? 207 je found_active ; yes 208 add si, sizeof(PartitionEntry) ; next PartitionEntry 209 dec al ; decrease remaining entry count 210 jnz .loop ; loop if entry count > 0 211 jmp no_bootable_active_partition; last partition reached 212 213found_active: ; active partition (pointed by si) 214 mov [boot_partition],si ; Save active partition pointer 215 216 .get_read_sector: ; initialise address_packet: 217 mov eax,[si + PartitionEntry.LBA_start] 218 mov [address_packet+AddressPacket.sector],eax 219 220 ; if LBA_adress equals 0 then it's not a valid PBR (it is the MBR) 221 ; this can append when we only have a CHS adress in the partition entry 222 test eax, eax ;if ( LBA_adress == 0 ) 223 jz no_disk_extentions ;then no_disk_extentions() 224 225check_disk_extensions: 226 ; Note: this test may be unnecessary since EXTENDED_READ also 227 ; set the carry flag when extended calls are not supported 228 %ifdef DEBUG 229 puts kMsgCheckEx 230 %endif 231 232 mov ah, CHK_DISK_EXTENTIONS ; set command 233 mov bx, 0x55aa ; set parameter : hton(MAGIC) 234 ; dl has not changed yet, still contains the drive ID 235 int BIOS_DISK_SERVICES ; if( do_command() <> OK ) 236 jc no_disk_extentions ; then use simple read operation 237 ; else use extended read 238disk_extentions: 239 %ifdef DEBUG 240 puts kMsgRead_Ex 241 %endif 242 243 ; load first bloc active partition 244 ; dl has not changed yet, still contains the drive ID 245 mov si, address_packet ; set command parameters 246 mov ah, EXTENDED_READ ; set command 247 .read_PBR: 248 int BIOS_DISK_SERVICES ; if ( do_command() <> OK ) 249 jc no_disk_extentions ; then try CHS_read(); 250 251check_for_bootable_partition: 252 cmp word[LOAD+MAGIC_OFF],MAGIC ; if ( ! volume.isBootable() ) 253 jne no_bootable_active_partition; then error(); 254 255jump_PBR: 256 %ifdef DEBUG 257 puts kMsgBootPBR 258 call _pause 259 %else 260 puts kMsgStart 261 %endif 262 263 ; jump to 0x7c00 with : 264 ; - CS=0 265 ; - DL=drive number 266 ; - DS:SI=pointer to the selected partition table 267 ; entry (required by some PBR) 268 269 ; dl has not changed yet, still contains the drive ID 270 mov si, [boot_partition] 271 jmp LOAD ; jump into partition boot loader 272 273no_disk_extentions: 274 %ifdef DEBUG 275 puts kMsgNoExtentions 276 %endif 277 278 mov si, [boot_partition] ; Restore active partition pointer 279 280 ;load CHS PBR sector info 281 mov dh, [si+1] ; dh 7:0 = head 7:0 (0 - 255) 282 mov cx, [si+2] ; cl 5:0 = sector 7:0 (1 - 63) 283 ; cl 7:6 = cylinder 9:8 (0 - 1023) 284 ; ch 7:0 = cylinder 7:0 285 .check_Sector: 286 mov al, cl 287 and al, 0x3F ; extract sector 288 test al, al; ; if (sector==0) 289 jz no_bootable_active_partition; then error("invalid sector"); 290 cmp al, 0x3F ; if( (Sector == 63) 291 jne .CHS_valid ; 292 .check_Cylinder: 293 mov ax, cx 294 shr ah, 6 295 cmp ax, 0x03FF ; and (Cylinder == 1023) 296 jne .CHS_valid 297 .check_Head: 298 cmp dh, 0xFF ; and ( (Head == 255) 299 jne .CHS_valid 300 cmp dh, 0xFE ; or (Head == 254) ) ) 301 je no_bootable_active_partition; then error("invalid CHS_adress"); 302 303 .CHS_valid: 304 ; dl has not changed yet, still contains the drive ID 305 mov bx, LOAD ; set buffer 306 mov al, SECTOR_COUNT ; set Size 307 mov ah, READ_DISK_SECTORS ; set read command 308 int BIOS_DISK_SERVICES ; if ( do_command() == OK ) 309 jnc check_for_bootable_partition; then resume(normal boot sequence) 310 ; else continue; 311 312no_bootable_active_partition: 313 mov si, kMsgNoBootable 314 ;jmp _error ; _error is the next line ! 315 316_error: 317 ; display a non-empty null-terminated string on the screen, 318 ; wait for a key pressed and go back to the bios 319 ; IN : 320 ; - si = address of string to display 321 ; OUT : 322 ; DIRTY : 323 ; - ax 324 ; - si 325 call _puts 326 call _pause 327 puts kMsgROMBASIC 328 int BIOS_BASIC ; BIOS_BASIC give the control back 329 ; to the BIOS. Doing so, let some 330 ; BIOSes try to boot an alternate 331 ; device (PXE/CD/HDD : see your 332 ; BIOS' boot device order ) 333_pause: 334 ; wait for a key pressed 335 ; IN : 336 ; OUT : 337 ; DIRTY : 338 ; - ax 339 mov ah, READ_CHAR 340 int BIOS_KEYBOARD_SERVICES 341 ret 342 343_puts: 344 ; display a non-empty null-terminated string on the screen 345 ; IN : 346 ; - si = address of string to display 347 ; OUT : 348 ; DIRTY : 349 ; - ax 350 ; - bx 351 ; - si 352 xor bx, bx ; bx:=0 353 .loop: ; do { 354 lodsb ; al=[si++]; 355 mov ah, WRITE_CHAR ; 356 int BIOS_VIDEO_SERVICES ; WRITE_CHAR(al) 357 or al, al ; } while (al<>0); 358 jnz .loop 359 ret 360 361data: 362 kMsgROMBASIC db 'ROM BASIC',0 363 kMsgNoBootable db 'No bootable active volume',13,10,0 364 kMsgStart db 'Loading system',10,13,0 365 366 %ifdef DEBUG 367 kMsgBootPBR db 'JMP PBR',13,10,0 368 kMsgRead_Ex db 'Read_ex block',13,10,0 369 kMsgCheckEx db 'CheckEx',13,10,0 370 kMsgNoExtentions db 'Read block',13,10,0 371 kMsgReloc db 'reloc MBR',13,10,0 372 %endif 373 374; check whether the code is small enough to fit in the boot code area 375end: 376 ;use nasm instead of yasm to check the code size 377 ;%if end - start > DISKSIG 378 ; %error "Code exceeds master boot code area!" 379 ;%endif 380 381%ifdef MBR_CODE_ONLY 382 ;just build the code. 383 ;Do not generate the datas 384%else 385 ;use nasm instead of yasm => use %rep instead of times 386 ;%rep start + DISKSIG - end 387 ; db 0 ;fill the rest of the code area 388 ;%endrep 389 times start + DISKSIG - end db 0 390 kMbrDiskID dd 0 ;Disk signature 391 dw 0 ;reserved 392 PartitionTable times PartitionEntry_size * 4 db 0 393DiskSignature: 394 ;use nasm instead of yasm to check the MAGIC offset 395 ;%if DiskSignature - start <> MAGIC_OFF 396 ; %error "incorrect Disk Signature offset" 397 ;%endif 398 kMbrSignature db 0x55, 0xAA 399%endif 400