vbr.S 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*++
  2. Copyright (c) 2012 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. vbr.S
  5. Abstract:
  6. This module implements the Volume Boot Record code used to bootstrap the
  7. operating system. This code lives on the first sector of the partition or
  8. disk.
  9. Author:
  10. Evan Green 19-Jun-2012
  11. Environment:
  12. MBR
  13. --*/
  14. ##
  15. ## ---------------------------------------------------------------- Definitions
  16. ##
  17. ##
  18. ## Define the segment where the BIOS loads this MBR code.
  19. ##
  20. .equ BOOT_ADDRESS, 0x7C00
  21. ##
  22. ## Define the boot stack, with some room for locals.
  23. ##
  24. .equ BOOT_STACK, (BOOT_ADDRESS - 0x8)
  25. .equ STACK_SIZE, 0x3000
  26. ##
  27. ## Define the size of a disk sector.
  28. ##
  29. .equ SECTOR_SIZE, 0x200
  30. ##
  31. ## Define the physical address where the second stage loader is loaded.
  32. ##
  33. .equ LOADER_ADDRESS, 0x7E00
  34. ##
  35. ## Define the segment descriptors within the initial GDT.
  36. ##
  37. .equ CS_DESCRIPTOR, 0x08
  38. .equ DS_DESCRIPTOR, 0x10
  39. ##
  40. ## ----------------------------------------------------------------------- Code
  41. ##
  42. ##
  43. ## .text specifies that this code belongs in the executable section. This is
  44. ## the only section in the MBR code, data also lives in the text section.
  45. ##
  46. ## .code16 specifies that this is 16-bit real mode code.
  47. ##
  48. .text
  49. .code16
  50. ##
  51. ## .globl allows this label to be visible to the linker.
  52. ##
  53. .globl _start
  54. _start:
  55. jmp AfterFileSystemParameters # Jump over the FS parameters next.
  56. nop # A free byte. Go nuts!
  57. ##
  58. ## These are the well defined locations where the LBA of this sector lives. If
  59. ## this is a partition, then this contains the partition offset.
  60. ##
  61. .org 0x5C
  62. BootCodeStartSector:
  63. .long 0
  64. BootCodeSectorCount:
  65. .byte 0
  66. AfterFileSystemParameters:
  67. cli # Disable interrupts for stack setup.
  68. xorl %eax, %eax # Zero out AX.
  69. movl %eax, %esp # Zero out ESP.
  70. movw %ax, %ds # Zero out DS.
  71. movw %ax, %es # Zero out ES.
  72. movw %ax, %ss # Zero out SS.
  73. movw $BOOT_STACK, %si # Load SI with the stack pointer.
  74. movw %si, %sp # Set it as the stack.
  75. movw %si, %bp # Also set it as the base.
  76. sti # Re-enable interrupts.
  77. ##
  78. ## Check to see if INT 13 extensions are supported. If CF is cleared, BX
  79. ## changes to 0xAA55, and the lowest bit in CX is set, then they are
  80. ## supported.
  81. ##
  82. movb %dl, (%bp) # Save the drive number.
  83. movb $0x41, %ah # Set AH.
  84. movw $0x55AA, %bx # Set BX to the magic value.
  85. int $0x13 # Call the BIOS.
  86. jb DiskReadError # Fail if CF is set.
  87. cmpw $0xAA55, %bx # See if BX changed to 0xAA55.
  88. jnz DiskReadError # Fail if BX wasn't set to the magic.
  89. test $0x0001, %cx # See if the lowest bit of CX is set.
  90. jz DiskReadError # Fail if lowest bit of CX is not set.
  91. ##
  92. ## Get the boot sector offset and count.
  93. ##
  94. xorw %cx, %cx
  95. movb BootCodeSectorCount, %cl # Get the sector count.
  96. cmpb $0, %cl # Compare to zero.
  97. jz BootCodeFailure # Fail if a count is not set.
  98. movl BootCodeStartSector, %eax # Load the start sector.
  99. incl %eax
  100. ##
  101. ## Perform an INT 13 extended read of the boot sectors. The extended read
  102. ## takes a disk packet that looks like this:
  103. ## Offset 0, size 1: Size of packet (16 or 24 bytes).
  104. ## Offset 1, size 1: Reserved (0).
  105. ## Offset 2, size 2: Number of blocks to transfer.
  106. ## Offset 4, size 4: Transfer buffer.
  107. ## Offset 8, size 8: Absolute starting sector.
  108. ## Offset 0x10, size 8: 64-bit flat address of transfer buffer. Only used
  109. ## if the value at offset 4 is 0xFFFFFFFF (which it is not in this case).
  110. ##
  111. ## Remember that things need to be pushed on in reverse.
  112. ##
  113. pushl $0 # Push starting sector high.
  114. pushl %eax # Push the starting sector low.
  115. pushl $LOADER_ADDRESS # Push the transfer buffer.
  116. pushw %cx # Push the sector count.
  117. pushw $0x0010 # Push reserved and packet size.
  118. movb $0x42, %ah # Function 0x42, extended read.
  119. movb (%bp), %dl # Load the drive number.
  120. movw %sp, %si # SI points to the disk packet address.
  121. int $0x13 # Read the first sector.
  122. jb DiskReadError # Fail if it didn't read.
  123. addw $0x10, %sp # Pop the parameters off.
  124. ##
  125. ## Enable the A20 address line. The 8088 processor in the original PC only
  126. ## had 20 address lines, and when it reached its topmost address at 1MB it
  127. ## would silently wrap around. When the 80286 was released it was designed
  128. ## to be compatible with programs written for the 8088, which had come to
  129. ## rely on this wrapping behavior. The 8042 keyboard controller had an
  130. ## extra pin, so the gate to control the A20 line behavior on newer (286)
  131. ## processors was stored there. The keyboard status register is read from
  132. ## port 0x64. If bit 2 is set (00000010b) the input buffer is full, and the
  133. ## controller is not accepting commands. The CPU can control the keyboard
  134. ## controller by writing to port 0x64. Writing 0xd1 corresponds to "Write
  135. ## Output port". Writing 0xDF to port 0x60, the keyboard command register,
  136. ## tells the controller to enable the A20 line (as it sets bit 2, which
  137. ## controlled that line).
  138. ##
  139. call WaitForKeyboardController # Wait for not busy.
  140. jnz AfterA20Line # Jump out if stuck.
  141. cli # Disable interrupts.
  142. movb $0xD1, %al # Send command 'Write output port'.
  143. outb %al, $0x64 # Write 0xD1 to port 0x64.
  144. call WaitForKeyboardController # Wait for not busy.
  145. movb $0xDF, %al # Set the mask.
  146. outb %al, $0x60 # Write 0xDF to port 0x60.
  147. call WaitForKeyboardController # Wait for not busy again.
  148. movb $0xFF, %al # Clear the command.
  149. outb %al, $0x64 # Write 0xFF to port 0x60.
  150. call WaitForKeyboardController # One last time.
  151. sti # Re-enable interrupts.
  152. AfterA20Line:
  153. ##
  154. ## Jump off to the remainder of the boot sectors.
  155. ##
  156. movb (%bp), %dl # Load the drive number.
  157. jmp $0, $LOADER_ADDRESS # Rock and roll.
  158. ##
  159. ## In error cases down here, print a message and die sadly.
  160. ##
  161. DiskReadError:
  162. movw $ReadFailureMessage, %si
  163. jmp PrintStringAndDie
  164. BootCodeFailure:
  165. movw $BootCodeFailureMessage, %si
  166. jmp PrintStringAndDie
  167. ##
  168. ## Print a null-terminated string, and then end it.
  169. ##
  170. PrintStringAndDie:
  171. cld
  172. lodsb # Load SI into AL.
  173. cmp $0, %al # Is this the null terminator?
  174. jz Die # If so, then go quietly into the night.
  175. movw $0x0007, %bx # Display normal white on black.
  176. movb $0x0E, %ah # Print character function.
  177. int $0x10 # Print the character.
  178. jmp PrintStringAndDie # Loop printing more characters.
  179. ##
  180. ## This is the end of the line.
  181. ##
  182. Die:
  183. hlt # Stop.
  184. jmp Die # Die again forever.
  185. ##
  186. ## This routine waits for the keyboard controller's busy bit to clear.
  187. ##
  188. WaitForKeyboardController:
  189. xorw %cx, %cx # Zero out CX.
  190. WaitForKeyboardControllerLoop:
  191. inb $0x64, %al # Read port 0x64.
  192. jmp WaitForKeyboardControllerDelay # Add in a tiny bit of delay.
  193. WaitForKeyboardControllerDelay:
  194. and $0x2, %al # Isolate the second bit.
  195. loopne WaitForKeyboardControllerLoop # Loop if it's still set.
  196. and $0x2, %al # Set the flags for return.
  197. ret # Return.
  198. ##
  199. ## Define the messages that are printed from the MBR.
  200. ##
  201. ReadFailureMessage:
  202. .asciz "Disk Read Error"
  203. BootCodeFailureMessage:
  204. .asciz "Boot code lost"
  205. ##
  206. ## Define the signature that the BIOS uses to determine this disk is bootable.
  207. ##
  208. .org 0x1FE
  209. .byte 0x55
  210. .byte 0xAA
  211. ##
  212. ## This portion onward is on the second sector. It is loaded by the first
  213. ## sector above, it is the destination of LOADER_ADDRESS.
  214. ##
  215. .org 0x200
  216. ##
  217. ## Clear the screen, since it's easy here.
  218. ##
  219. pushw %dx
  220. movw $0x0003, %ax # BH = Clear Screen Function. BL = Text.
  221. int $0x10 # Clear the screen.
  222. popw %dx
  223. ##
  224. ## Prepare to switch from 16-bit Real Mode to 32-bit Protected Mode.
  225. ## Segment registers turn into descriptors (indices into the Global
  226. ## Descriptor Table) which must be set up prior to switching into protected
  227. ## mode.
  228. ##
  229. cli # Disable interrupts.
  230. lgdt Gdt # Load the GDT.
  231. movl %cr0, %eax # Get the CR0 control register.
  232. orl $0x1, %eax # Set bit 0 to enable Protected Mode.
  233. movl %eax, %cr0 # Set CR0.
  234. movw $DS_DESCRIPTOR, %ax # Load the data descriptor.
  235. movw %ax, %ds # Set the data segment.
  236. movw %ax, %ss # Set the stack segment as well.
  237. movw %ax, %es # Set up ES.
  238. movw %ax, %fs # Set up FS.
  239. movw %ax, %gs # Set up GS.
  240. ##
  241. ## A long jump is required to complete the switch to protected mode. Long jumps
  242. ## specify the segment descriptor and an offset from that segment.
  243. ##
  244. ljmp $CS_DESCRIPTOR, $ProtectedModeCode
  245. ##
  246. ## The .code32 assembler directive lets the compiler know that it should
  247. ## generate 32-bit instructions. Load the segment descriptors, set up the
  248. ## initial stack, and jump to loader code.
  249. ##
  250. .code32
  251. ProtectedModeCode:
  252. andl $0x000000FF, %edx # Clear all but dl.
  253. pushl %edx # Push boot drive number.
  254. movl BootCodeStartSector, %eax # Load the start sector.
  255. pushl $0 # Push the high partition offset.
  256. pushl %eax # Push the low partition offset.
  257. pushl $STACK_SIZE # Push stack size parameter.
  258. pushl $BOOT_STACK # Push stack location.
  259. xorl %ebp, %ebp # Zero out stack base.
  260. ##
  261. ## Jump to the loader image. Control will not return from here.
  262. ##
  263. call BoMain # Control should not return... but
  264. int $0x18 # if it does, "Press a key to reboot".
  265. jmp Die # And loop forever.
  266. ##
  267. ## --------------------------------------------------------- Internal Functions
  268. ##
  269. ##
  270. ## ---------------------------------------------------------------- Definitions
  271. ##
  272. ##
  273. ## The GDT must be loaded from an address aligned on 8 bytes.
  274. ##
  275. .align 8
  276. ##
  277. ## GDT Table. The GDT table sets up the segmentation features of the processor
  278. ## and privilege levels. In this case set up two descriptors, a code segment
  279. ## and a data segment, both of which simply span the entire address space.
  280. ## This way the kernel essentially has full control over the whole address
  281. ## space. A GDT entry is 8 bytes long, and looks as follows:
  282. ## USHORT limit_low; // The lower 16 bits of the limit.
  283. ## USHORT base_low; // The lower 16 bits of the base.
  284. ## UCHAR base_middle; // The next 8 bits of the base.
  285. ## UCHAR access; // Access flags, described below.
  286. ## UCHAR granularity; // Defined below.
  287. ## UCHAR base_high; // The high 8 bits of the base.
  288. ##
  289. ## The granularity byte has the following fields:
  290. ## | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  291. ## | | | | | |
  292. ## | G | D | 0 | A | Segment length 19:16 |
  293. ##
  294. ## G - Granularity. 0 = 1 byte, 1 = 1 KByte.
  295. ##
  296. ## D - Operand Size. 0 = 16 bit, 1 = 32 bit.
  297. ##
  298. ## 0 - Always zero.
  299. ##
  300. ## A - Available for system use (always zero).
  301. ##
  302. ## The access byte has the following fields:
  303. ## | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  304. ## | | | | |
  305. ## | P | DPL | DT | Type |
  306. ##
  307. ## P - Is segment present (1 = Yes)
  308. ##
  309. ## DPL - Descriptor privilege level: Ring 0-3. Zero is the highest
  310. ## privilege, 3 is the lowest (least privileged).
  311. ##
  312. ## Type - Segment type: code segment / data segment.
  313. ##
  314. Gdt:
  315. .word (3 * 8) - 1 # GDT table limit
  316. .long GdtTable # GDT table location
  317. GdtTable:
  318. .long 0x0 # The first GDT entry is called the
  319. .long 0x0 # null descriptor, it is essentially
  320. # unused by the processor.
  321. ##
  322. ## Kernel code segment descriptor
  323. ##
  324. .word 0xFFFF # Limit 15:0
  325. .word 0x0 # Base 15:0
  326. .byte 0x0 # Base 23:16
  327. .byte 0x9A # Access: Present, Ring 0, Code Segment
  328. .byte 0xCF # Granularity: 1Kb, 32-bit operands
  329. .byte 0x00 # Base 31:24
  330. ##
  331. ## Kernel data segment descriptor
  332. ##
  333. .word 0xFFFF # Limit 15:0
  334. .word 0x0 # Base 15:0
  335. .byte 0x0 # Base 23:16
  336. .byte 0x92 # Access: Present, Ring 0, Data Segment
  337. .byte 0xCF # Granularity: 1Kb, 32-bit operands
  338. .byte 0x00 # Base 31:24