Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Nasm MBR linux bootloader

By: a guest on Jun 8th, 2010  |  syntax: None  |  size: 8.19 KB  |  views: 80  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. eval :; if [ $# -gt 1 ]; then xxd -p -r | dd conv=notrunc bs=1 seek=0$2 of=$1; fi; exit
  2. ; asmsyntax=nasm for vim
  3.  
  4. ; bl.asm
  5. ;
  6. ; This is a bootloader that fits into the MBR. It iterates through every
  7. ; partition until it finds one that is marked bootable. If the partition's
  8. ; type is 0x8a (from AiR-Boot) it loads a Linux bzImage. Otherwise it
  9. ; chainloads the first sector.
  10. ;
  11. ; The bootloader supports partitions starting at above or being larger than
  12. ; 2 TiB using an extension to the partition table where if bit 6 (0x40) of
  13. ; the 'status' field is set, the fields 'chs' and 'chs_end' become the higher
  14. ; bytes of the lba start address and size, respectively. I know of no
  15. ; partitioning tool which supports this, however.
  16. ;
  17. ; The code being limited to 446 bytes, no configuration is supported. Since
  18. ; you can't pass any parameters to the kernel, you have to configure it by
  19. ; manually editing the fields of its header in the bzImage file. The short
  20. ; shell script line above helps with this.
  21. ;
  22. ; $ echo aa bb | sh bl.asm <file> <offset>
  23. ;
  24. ; This will write the two bytes 0xaa 0xbb to the file <file> at offset
  25. ; <offset> (in decimal). Trying to boot without setting a root device will
  26. ; give you some lines like:
  27. ;
  28. ; 0300           36065 hda driver: ide-gd
  29. ;   0301           16033 hda1
  30. ;
  31. ; To boot from device 0301, do:
  32. ;
  33. ; $ echo 01 03 | sh bl.asm bzImage 508 # little-endian
  34. ;
  35. ; That should be all you need, but all of the header fields are available in
  36. ; Documentation/x86/boot.txt of the kernel sources.
  37. ;
  38. ; To install:
  39. ; # nasm bl.asm
  40. ; # dd conv=notrunc if=bl of=/dev/drive
  41. ;
  42. ; We also have to enable the boot signature as bl.asm doesn't contain it or it
  43. ; would overwrite the partition table.
  44. ; # echo 55aa | sh bl.asm /dev/drive 510
  45. ;
  46. ; Installing the kernel:
  47. ; # fdisk /dev/device # Mark the partition bootable and set its type to 0x8a.
  48. ; # dd conv=notrunc if=bzImage of=/dev/device_partition
  49. ;
  50. ; You also have to make sure that the kernel partition has the bootable flag on
  51. ; and has partition type 0x8a. The warning about an ancient bootloader is
  52. ; normal. Don't use this on kernels older than 2.6.14.
  53. ;
  54. ; An 'e' is printed if your BIOS lacks LBA support or there is no bootable
  55. ; partition. A hang can mean that your computer lacks fast A20 or that the
  56. ; kernel overwrote reserved memory. Try making your kernel smaller. I get
  57. ; roughly 7 MiB on my hardware but unlimited in QEMU (a recent bzImage can be
  58. ; anything between 2 and 30 MiB.
  59.  
  60. [BITS 16]
  61. [ORG 0x7e00]
  62. PE equ 1
  63. BOOTABLE equ 0x80
  64. BIG equ 0x40
  65.  
  66.  ; Describes a disc address packet for int 13h extended read sectors from drive.
  67. struc dap
  68. .size resb 1
  69. .res0 resb 1
  70. .len resb 1
  71. .res1 resb 1
  72. .offset resw 1
  73. .segment resw 1
  74. .lba resq 1
  75. endstruc
  76.  
  77.  ; Describes a record in the MBR partition table
  78. struc prec
  79. .status resb 1
  80. .chs resb 3
  81. .type resb 1
  82. .chs_end resb 3
  83. .lba resd 1
  84. .lba_end resd 1
  85. endstruc
  86.  
  87.  ; The first part of the bzImage header. See Linux Documentation/x86/boot.txt.
  88. struc bzImage
  89. .code resb 0x1f1
  90. .setup_sects resb 1
  91. .root_flags resw 1
  92. .syssize resd 1
  93. .ram_size resw 1
  94. .vid_mode resw 1
  95. .root_dev resw 1
  96. .boot_flag resw 1
  97. .jump resb 2
  98. .header resd 1
  99. .version resw 1
  100. endstruc
  101.  
  102.  ; dl remains the boot device (usually 0x80) throughout the code.
  103. intro:
  104.  ; Set up segments and stack.
  105.  xor ax, ax
  106.  mov ds, ax
  107.  mov es, ax
  108.  mov ss, ax
  109.  mov sp, 0x9c00
  110.  
  111.  ; Move code out of the way for chainloading (to just after this, 0x7e00)
  112.  mov cx, 0x100
  113.  lea si, [ds:0x7c00]
  114.  lea di, [es:0x7e00]
  115.  rep movsw
  116.  
  117.  ; Move to proper code
  118.  jmp 0x0:start
  119. start:
  120.  
  121.  ; Check for BIOS LBA support
  122.  mov ah, 0x41
  123.  mov bx, 0x55aa
  124.  int 0x13
  125.  jc no_pc
  126.  
  127.  ; Fast A20 enable.
  128.  in al, 0x92
  129.  or al, 0x02
  130.  out 0x92, al
  131.  
  132.  ; Enable unreal mode, allowing us to access all 4 GiB of memory.
  133.  cli
  134.  push ds
  135.  push es
  136.  lgdt [gdt]
  137.  
  138.  ; Enable protected mode
  139.  mov eax, cr0
  140.  or al, PE
  141.  mov cr0, eax
  142.  ; Load ds segment register
  143.  mov bx, 0x08
  144.  mov ds, bx
  145.  mov es, bx
  146.  ; Leave protected mode
  147.  and al, ~PE
  148.  mov cr0, eax
  149.  
  150.  pop es
  151.  pop ds
  152.  sti
  153.  
  154. load_mbr:
  155.  ; Try to boot one of the four primary partitions.
  156.  xor ecx, ecx
  157.  xor edi, edi
  158.  
  159.  mov cx, 4
  160.  mov bx, partitions
  161. .try:
  162.  mov al, [bx+prec.type]
  163.  test al, al
  164.  jz .next
  165.  cmp al, 0x05
  166.  je .extended
  167.  ; Primary partition. Try loading.
  168.  call load_partition
  169.  jmp .next
  170.  
  171. .ext_err:
  172.  call pop_lba
  173.  jmp .ext_out
  174.  
  175. .extended:
  176.  ; This is an extended partition. Iterate through all logical partitions.
  177.  pusha
  178.  call push_lba
  179.  call add_lba
  180.  
  181.  ; LBA is the first EBR at this point.
  182.  call push_lba
  183.  jmp .ext_first
  184.  
  185. .ext_next:
  186.  ; LBA is the first EBR and the bx record is the link partition entry.
  187.  call push_lba
  188.  call add_lba
  189. .ext_first:
  190.  ; Load EBR.
  191.  mov cl, 1
  192.  mov di, 0x1200
  193.  call push_lba
  194.  call read_sects
  195.  jc .ext_err
  196.  call pop_lba
  197.  mov bx, 0x1200+0x1be
  198.  call load_partition
  199.  
  200.  call pop_lba
  201.  add bx, prec_size
  202.  cmp byte[bx+prec.type], 0
  203.  jne .ext_next
  204.  
  205. .ext_out:
  206.  call pop_lba
  207.  popa
  208.  
  209. .next:
  210.  add bx, prec_size
  211.  loop .try
  212.  
  213.  ; There is no bootable OS (no partition marked bootable).
  214.  
  215.  ; The computer hardware is unsupported (BIOS A20 or LBA missing).
  216. no_pc:
  217.  
  218.  ; Print an 'e'.
  219.  mov ax, 0x0e65
  220.  int 0x10
  221.  ; Wait for a key press.
  222.  xor ax, ax
  223.  int 0x16
  224.  ; Reboot.
  225.  int 0x19
  226.  jmp $
  227.  
  228.  ; Destroys bp
  229. push_lba:
  230.  pop bp
  231.  push dword[LBA]
  232.  push dword[LBA+4]
  233.  push bp
  234.  ret
  235.  
  236.  ; Destroys bp
  237. pop_lba:
  238.  pop bp
  239.  pop dword[LBA+4]
  240.  pop dword[LBA]
  241.  push bp
  242.  ret
  243.  
  244.  ; We have, in bx, a primary or logical partition record.
  245. load_partition:
  246.  ; Add the start sector from the partition record to [LBA]. We add instead of
  247.  ; just replace because logical partitions have an offset relative to the
  248.  ; start of the extended partition.
  249.  call add_lba
  250.  
  251.  xor ecx, ecx
  252.  mov cl, 1
  253.  xor edi, edi
  254.  
  255.  ; Check bootable flag
  256.  test byte[bx+prec.status], BOOTABLE
  257.  jz .out
  258.  
  259.  ; Check for linux bzImage partition (type 0x8a, first defined by AiR-Boot).
  260.  cmp byte[bx+prec.type], 0x8a
  261.  jne .chainload
  262.  
  263.  ; Load kernel boot sector and bzImage header.
  264.  mov edi, 0x90000
  265.  call read_sects
  266.  jc .out
  267.  
  268.  ; Load rest of kernel real-mode setup code to 0x90200.
  269.  a32 mov cl, [0x90000+bzImage.setup_sects]
  270.  call read_sects
  271.  jc .out
  272.  
  273.  ; Load protected-mode kernel to 0x100000.
  274.  a32 mov ecx, [0x90000+bzImage.syssize]
  275.  shr ecx, 5
  276.  inc ecx
  277.  mov edi, 0x100000
  278.  call read_sects
  279.  jc .out
  280.  
  281.  ; Invoke kernel.
  282.  mov ax, 0x9000
  283.  mov ds, ax
  284.  mov es, ax
  285.  mov ss, ax
  286.  jmp 0x9020:0x00
  287.  
  288. .chainload:
  289.  ; Generic partition; chainload.
  290.  mov di, 0x7c00
  291.  call read_sects
  292.  jc .out
  293.  ; We could check for 55aa here but we don't care.
  294.  jmp 0x7c0:0x00
  295.  
  296. .out:
  297.  ret
  298.  
  299. .ebr_adr db 0x1200/0x200
  300.  
  301.  ; Add partition (at bx) start sector to [LBA]. Destroys eax.
  302. add_lba:
  303.  mov eax, [bx+prec.lba]
  304.  add [LBA], eax
  305.  ; Save carry for adc below.
  306.  lahf
  307.  xor eax, eax
  308.  test byte[bx+prec.status], BIG
  309.  jz .simple
  310.  ; Extended partition offset. Use the three bytes traditionally reserved for
  311.  ; CHS as the upper bytes of the LBA if bit 6 (0x40) in prec.status is set.
  312.  mov eax, [bx+prec.chs]
  313.  and eax, 0x00ffffff
  314. .simple:
  315.  sahf
  316.  adc [LBA+4], eax
  317.  ret
  318.  
  319. read_sects:
  320.  ; Read ecx sectors of drive dl starting at [LBA] to the buffer at edi. edi
  321.  ; and [LBA] are updated. ecx and esi are destroyed.
  322. .sector:
  323.  call read_sector
  324.  jc .out
  325.  push ecx
  326.  
  327.  ; Move sector data from low memory (0x1000) to edi.
  328.  mov esi, 0x1000
  329.  mov ecx, 0x200/4
  330.  ; edi is also updated by this.
  331.  a32 rep movsd
  332.  
  333.  ; Increment qword[LBA], one word at a time.
  334.  mov si, LBA-2
  335. .next:
  336.  add si, 2
  337.  mov ax, [si]
  338.  inc ax
  339.  mov [si], ax
  340.  jz .next
  341.  
  342.  pop ecx
  343.  a32 loop .sector
  344.  
  345. .out:
  346.  ret
  347.  
  348. read_sector:
  349.  ; Read a sector from hard drive dl into 0x1000. Specify sector by
  350.  ; directly changing [LBA].
  351.  pusha
  352.  
  353.  mov cx, 5
  354. .retry:
  355.  mov ah, 0x42
  356.  mov si, read_dap
  357.  int 0x13
  358.  jnc .out
  359.  loop .retry
  360. .out:
  361.  popa
  362.  ret
  363.  
  364. read_dap istruc dap
  365. at dap.size, db 0x10
  366. at dap.len, db 1
  367. at dap.offset, dw 0x1000
  368. at dap.segment, dw 0x0
  369. iend
  370. LBA equ read_dap+dap.lba
  371.  
  372. gdt:
  373.  ; This is the unused null descriptor. It doubles as the lgdt argument.
  374.  dw .end - gdt - 1
  375.  dd gdt
  376.  dw 0
  377. .flat:
  378.  ; Flat descriptor
  379.  dw 0x07ff, 0x0000, 0x9200, 0x00c0
  380. .end:
  381.  
  382.  ; Make sure the boot code won't be too long.
  383.  times 446-($-$$) db 0
  384.  
  385. partitions: