/* Released under GPL version 2 ** ** How to read/update MultiBoot MBR: ** 1. Check that first 16 bytes are "xxxMultiBoot 0.4" ** 2. Update fields at 0x13..0x20 as you need. ** 3. offsets[0] (i.e. 2-byte field at 1a) holds a pointer to ** the text label of 1st partition, do not change it, ** but use it's value as a starting address of new labels. ** 4. offsets[1..3] can (should) be modified ** 5. Labels should be written as null-terminated strings ** in the offsets[0]...0x1b5 range. ** 6. NB: you need to adjust offsets[i] values by 0x600 ** (i.e. offsets[i]==0x740 means "offset 0x140 from the start of MBR") ** 7. Do not touch stuff at and after 0x1b6 - it may be used ** Windows/whatever ** ** A few special cases: ** if default_char doesn't match any of chars[], there is no default ** and also there is no automatic boot timeout. ** if char[i] = '\0', the partition is unselectable. ** delay = 0 suppresses any output, and boots default partition ** with no delay; but if default_char doesn't match any of chars[], then ** user will need BLINDLY type the desired partition char ** in order to boot. */ .code16 .text std_boot_addr = 0x7c00 part_tbl = 0x7c00 + 0x200 - 2 - 4*16 .org 0x600 start0: cli # lilo 22.5.8: "NT 4 blows up if this is missing" jmp start1 banner: .ascii "MultiBoot 0.4" # The above is exactly 16 bytes and may be used as a signature # for tools trying to directly manipulate the MBR crlf: .asciz "\r\n" # 10 default_char: .byte '1' # 13 chars: .byte '1','2','3','4' # 14 delay: .word 5*18 # 18 offsets: .word part1_msg # 1a .word part2_msg # 1c .word part3_msg # 1e .word part4_msg # 20 // The disk I/O packet edd_packet: .word 16 # size of packet .word 1 # count of sectors to transfer addr: .word std_boot_addr # address offset to transfer to // space-saving trick, see (*) below // .word 0 # address segment to transfer to //daddr: .long 0 # low order disk address // .long 0 # high order disk address daddr = addr + 4 start1: // Move ourself to 0x600 mov $std_boot_addr,%si xor %ax,%ax mov %ax,%es mov %ax,%ds mov %ax,%ss mov %si,%sp sti cld mov $start0,%di mov $0x200/2,%cx rep movsw jmp $0,$next next: // Zero out the remaining part of disk I/O packet (*) mov $addr+2,%di mov $5,%cl # %ax is still == 0 rep stosw # zero out addr_seg,daddr_lo,daddr_hi // Autoboot or 'blind selection'? mov delay,%cx jcxz use_default // Print banner mov $banner,%si call print_crlf // Print list of partitions xor %bx,%bx next_part: mov chars(%bx),%al test %al,%al jz skip_part mov $delim,%si call putc_and_print add %bx,%bx mov offsets(%bx),%si shr $1,%bx call print_crlf skip_part: inc %bx cmp $4,%bx jb next_part // 'Choose:' mov $choose,%si call print // Calculate timeout deadline mov $0,%ah # get time int $0x1a add delay,%dx # cx:dx += delay adc $0,%cx mov %dx,%bx # si:bx = deadline mov %cx,%si wait_for_interrupt: hlt mov $0,%ah # get time int $0x1a sub %bx,%dx # timed out? (cx:dx - si:bx > 0?) sbb %si,%cx jae use_default # yes, bail out, use default mov $0x1,%ah # is a key pressed? int $0x16 je wait_for_interrupt # no, repeat getchar: mov $0,%ah int $0x16 cmp $0xd,%al # if - take default jne not_default use_default: mov default_char,%al not_default: mov $4,%cx # look up pressed char mov $chars,%di repne scasb jne getchar # not found? - repeat sub $chars+1,%di # di -= (offset chars+1) // The partition has been selected in %di mov $crlf,%si # display the char call putc_and_print shl $4,%di # di *= 16 lea part_tbl(%di),%si # si => part_tbl[i] mov $0x80,%dx cmp %dl,(%si) # is it already marked bootable? je skip_rewrite // Update the MBR (write bootable flag to selected partition) xor %ax,%ax mov %al,part_tbl mov %al,part_tbl+0x10 mov %al,part_tbl+0x20 mov %al,part_tbl+0x30 mov %dl,(%si) mov $1,%cx mov $std_boot_addr,%bx mov $0x301,%ax int $0x13 skip_rewrite: // Ok, read the bootsect and jump to it call disk_read cmpw $0xaa55,std_boot_addr + 0x200 - 2 jne missing_os // By convention, ds:si must point to partition table entry // which was used for booting lea part_tbl_copy(%di),%si mov %dl,(%si) # "bootable" marker jmp *%sp # jmp to 0x7c00 read_error: mov $read_error_msg,%si jmp do_print missing_os: mov $missing_os_msg,%si do_print: call print hang: jmp hang /* Disk read subroutine ** //** %si guaranteed to not be clobbered */ disk_read: mov 8(%si),%eax # copy part_start into disk addr mov %eax,daddr mov $0x80,%dl # hdd0 mov $0x55aa,%bx # magic number mov $0x41,%ah int $0x13 jc disk_read_old # error? cmp $0xaa55,%bx # is magic number changed? jne disk_read_old test $1,%cl # are EDD packet calls supported? jz disk_read_old //push %si mov $edd_packet,%si # ds:si => packet mov $0x42,%ah # edd packet read int $0x13 //pop %si jmp chk_carry disk_read_old: mov (%si),%dx # disk,head mov 2(%si),%cx # cyl,sect mov $0x7c00,%bx # es:bx => buffer mov $0x201,%ax # read, 1 sect int $0x13 chk_carry: jc read_error ret /* Printing subroutines */ putc_and_print: mov $0xe,%ah int $0x10 print: lodsb test %al,%al jnz putc_and_print ret print_crlf: call print mov $crlf,%si jmp print choose: .ascii "\r\nChoose" delim: .asciz ": " read_error_msg: .asciz "Read error" missing_os_msg: .asciz "Missing OS" part1_msg: .asciz "Partition 1" part2_msg: .asciz "Partition 2" part3_msg: .asciz "Partition 3" part4_msg: .asciz "Partition 4" // Pad the .bin to the exact size of MBR code // sector - sig - ptbl - pad - vol - pad .org 0x600 + 0x200 - 2 - 4*16 - 2 - 4 - 2 end_mbr_code: // Volume serial# /* .word ? vol_serial_no: .long ? .word ? */ part_tbl_copy = end_mbr_code + 2 + 4 + 2 // Partition table entry /* .byte ? # +0: 0x80/0x00 - bootable/not bootable .byte ? # +1: head (start) .word ? # +2: cyl+sect (start) .byte ? # +4: type .byte ? # +5: head (end) .word ? # +6: cyl+sec (end) .dword ? # +8: offset in sectors .dword ? # +12: size in sectors # +16: size of entry is 16 bytes */