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: