Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
21 KiB

  1. Page ,132
  2. TITLE BOOT SECTOR 1 OF TRACK 0 - BOOT LOADER
  3. ;/*
  4. ; * Microsoft Confidential
  5. ; * Copyright (C) Microsoft Corporation 1991
  6. ; * All Rights Reserved.
  7. ; */
  8. ; Rev 1.0 ChrisP, AaronR and others. 2.0 format boot
  9. ;
  10. ; Rev 3.0 MarkZ PC/AT enhancements
  11. ; 2.50 in label
  12. ; Rev 3.1 MarkZ 3.1 in label due to vagaries of SYSing to IBM drive D's
  13. ; This resulted in the BPB being off by 1. So we now trust
  14. ; 2.0 and 3.1 boot sectors and disbelieve 3.0.
  15. ;
  16. ; Rev 3.2 LeeAc Modify layout of extended BPB for >32M support
  17. ; Move PHYDRV to 3rd byte from end of sector
  18. ; so that it won't have to be moved again
  19. ; FORMAT and SYS count on PHYDRV being in a known location
  20. ;
  21. ; Rev 3.3 D.C.L. Changed Sec 9 EOT field from 15 to 18. May 29, 1986.
  22. ;
  23. ; Rev 3.31 MarkT The COUNT value has a bogus check (JBE????) to determine
  24. ; if we've loaded in all the sectors of IBMBIO. This will
  25. ; cause too big of a load if the sectors per track is high
  26. ; enough, causing either a stack overflow or the boot code
  27. ; to be overwritten.
  28. ;
  29. ; Rev 4.00 J. K. For DOS 4.00 Modified to handle the extended BPB, and
  30. ; 32 bit sector number calculation to enable the primary
  31. ; partition be started beyond 32 MB boundary.
  32. ;
  33. ; Rev 7.0 JeffPar Loads WINBOOT.SYS from anywhere in the root of the boot
  34. ; drive. WINBOOT.SYS must be an EXE with exactly 1 sector
  35. ; of header information, followed by a series of binary
  36. ; images imbedded in the EXE, as follows:
  37. ;
  38. ; 1. WINLOAD module (ORGed at 200h, loaded at 70:200h)
  39. ; 2. IO.SYS module
  40. ; 3. MSDOS.SYS module
  41. ;
  42. ; The WINLOAD module should fit within WINLOAD_SIZE sectors,
  43. ; which includes 1 sector for the EXE header, so the code
  44. ; and data for the WINLOAD bootstrap should fit in 3 sectors.
  45. ;
  46. ; Rev 7.1 ScottQ removed Winboot.sys stuff, add support for extended int 13
  47. ; MSliger bootable partitions implemented by scanning the MBR.
  48. ;
  49. ; Rev 8.0 ScottQ re-implement winboot.sys dual-boot as JO.SYS dual boot
  50. ; MSliger
  51. ;
  52. ; The ROM in the IBM PC starts the boot process by performing a hardware
  53. ; initialization and a verification of all external devices. If all goes
  54. ; well, it will then load from the boot drive the sector from track 0, head 0,
  55. ; sector 1. This sector is placed at physical address 07C00h. The initial
  56. ; registers are presumably set up as follows: CS=DS=ES=SS=0, IP=7C00h, and
  57. ; SP=0400h. But all we rely on is the BIOS being loaded at linear 07C00h.
  58. ;
  59. ; If IO.SYS is not found, an error message is displayed and the user is
  60. ; prompted to insert another disk. If there is a disk error during the
  61. ; process, a message is displayed and things are halted.
  62. ;
  63. ; At the beginning of the boot sector, there is a table which describes the
  64. ; MSDOS structure of the media. This is equivalent to the BPB with some
  65. ; additional information describing the physical layout of the driver (heads,
  66. ; tracks, sectors)
  67. ;
  68. ;==============================================================================
  69. ;REALLY OLD REVISION HISTORY which has no meaning but hey its nostalgic
  70. ;AN000 - New for DOS Version 4.00 - J.K.
  71. ;AC000 - Changed for DOS Version 4.00 - J.K.
  72. ;AN00x - PTM number for DOS Version 4.00 - J.K.
  73. ;==============================================================================
  74. ;AN001; d52 Make the fixed positioned variable "CURHD" to be local. 7/6/87 J.K.
  75. ;AN002; d48 Change head settle at boot time. 7/7/87 J.K.
  76. ;AN003; P1820 New message SKL file 10/20/87 J.K.
  77. ;AN004; D304 New structrue of Boot record for OS2. 11/09/87 J.K.
  78. ;AN005; Changed version to 5.0 03/08/90 E.A.
  79. ;AN006; Changed to remove MSLOAD in first cluster restriction 04/23/90 J.H.
  80. ;==============================================================================
  81. .xlist
  82. include bpb.inc
  83. include bootsec.inc
  84. include dirent.inc
  85. ;include version.inc
  86. .list
  87. ; ==========================================================================
  88. ORIGIN EQU 7C00H ; Origin of bootstrap LOADER
  89. BIO_SEG EQU 2000H ; Destination segment of ntldr
  90. BIO_OFFSET EQU 0 ; Offset of ntldr
  91. SECTOR_SIZE EQU 512 ; Sector size in bytes
  92. DIR_ENTRY_SIZE EQU SIZE DIR_ENTRY ; Size of directory entry in bytes
  93. DSK_PARMS EQU 1EH*4 ; POINTER TO DRIVE PARMS
  94. SEC9 EQU 522h ; ADDRESS OF NEW DRIVE PARM TABLE
  95. ROM_DISKRD EQU 2
  96. ROM_DISKRDX EQU 42h
  97. ; ==========================================================================
  98. ;
  99. ; This little set of directives establishes the address
  100. ; where we'll jump to once the first sector of ntldr has been
  101. ; loaded. The first 3 bytes of that sector are used for non-FAT
  102. ; filesystems, so we must skip over them.
  103. ;
  104. SEGBIOS SEGMENT AT BIO_SEG
  105. ORG 3
  106. NTLOAD LABEL BYTE
  107. SEGBIOS ENDS
  108. ; ==========================================================================
  109. ; Data storage between temp. stack and start of boot sector
  110. pReadClustersO EQU -16
  111. pReadClustersS EQU -14
  112. pReadSectorsO EQU -12
  113. pReadSectorsS EQU -10
  114. SectorBase EQU -8
  115. DataSecL EQU -4 ; absolute sector # of first sector in data area
  116. DataSecH EQU -2
  117. ; ==========================================================================
  118. CODE SEGMENT
  119. ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
  120. ORG ORIGIN
  121. Public $START
  122. $START Label byte
  123. jmp short Main
  124. PartitionType: ;the in-memory copy of this will leave
  125. nop ;the partition type in this NOP's place
  126. ;so IO.SYS can tell if it needs x13
  127. PartitionTypeOffset = (offset PartitionType - offset $START)
  128. ; ==========================================================================
  129. ; Start of BPB area of the boot record
  130. OsName DB "MSWIN"
  131. OsVersion DB "4.1" ; Windows version number
  132. CoreBpb label byte
  133. BytesPerSector DW SECTOR_SIZE ; Size of a physical sector
  134. SecsPerClust DB 8 ; Sectors per allocation unit
  135. ReservedSecs DW 1 ; Number of reserved sectors
  136. NumFats DB 2 ; Number of fats
  137. NumDirEntries DW 512 ; Number of direc entries
  138. TotalSectors DW 4*17*305-1 ; Number of sectors - number of hidden
  139. ; sectors (0 when 32 bit sector number)
  140. MediaByte DB 0F8H ; MediaByte byte
  141. NumFatSecs DW 8 ; Number of fat sectors
  142. SecPerTrack DW 17 ; Sectors per track
  143. NumHeads DW 4 ; Number of drive heads
  144. HiddenSecs DD 1 ; Number of hidden sectors
  145. BigTotalSecs DD 0 ; 32 bit version of number of sectors
  146. .errnz ($-BytesPerSector) NE SIZE BPB
  147. BootDrv DB 80h
  148. CurrentHead DB 0h ; Current Head
  149. ExtBootSig DB 41
  150. SerialNum DD 0
  151. VolumeLabel DB 'NO NAME '
  152. FatId DB 'FAT12 '
  153. .errnz ($-$START) NE SIZE BOOTSEC
  154. ; =========================================================================
  155. ;
  156. ; First thing is to reset the stack to a better and more known
  157. ; place. The ROM may change, but we'd like to get the stack
  158. ; in the correct place.
  159. ;
  160. Main:
  161. xor CX,CX
  162. mov SS,CX ;Work in stack just below this routine
  163. mov SP,ORIGIN+pReadClustersO
  164. ASSUME SS:CODE
  165. mov ds,cx
  166. assume DS:CODE
  167. ;
  168. ; Set up es for sector reads
  169. ;
  170. mov ax,BIO_SEG
  171. mov es,ax
  172. ASSUME ES:NOTHING
  173. cld
  174. mov BP,ORIGIN
  175. ; The system is now prepared for us to begin reading.
  176. ; First we read the master boot record (MBR) if this is a
  177. ; harddisk so we can get our partion type. Types E and C
  178. ; need extended int13 calls.
  179. cmp [BP].bsDriveNumber,cl ;assert cl == 0 from above
  180. jnl failed ;dont check for MBR on floppies!
  181. mov ax,cx ;read sector zero
  182. cwd
  183. call DoReadOne ;note! assume cx==0 for DoReadOne
  184. jc failed
  185. sub bx,(42h - 12 +4) ;bx comes back point it end of sector,
  186. ;we want hidden sector field of first
  187. ;partition entry
  188. .386
  189. mov eax,dword ptr HiddenSecs;get the hidden sectors for us
  190. scan:
  191. cmp eax,dword ptr es:[bx] ;is it the same as this partition entry?
  192. .286
  193. mov dl,byte ptr es:[bx-4] ;put the entry's type in dl
  194. jne short dontgotit ;its our partition, go write it down
  195. or dl,2 ;turn C's into E's
  196. mov [bp+2],dl ;put our partition type at byte 2's nop
  197. dontgotit:
  198. add bl,10h ; 8CA - 8DA - 8EA - 8FA - 90A : carry
  199. jnc short scan ; we scan 4 partition entries
  200. failed:
  201. ;if we failed, leave nop alone; we
  202. ;will use old int13 and so will IO.SYS
  203. xor cx,cx ;later code needs CX to still be zero
  204. .286
  205. ; Next, determine logical sector numbers of the start of the
  206. ; directory and the start of the data area.
  207. ;
  208. DirRead:
  209. mov AL,[BP].bsBPB.BPB_NumberOfFATs ; Determine sector dir starts on (NumFats)
  210. cbw ;
  211. mul [BP].bsBPB.BPB_SectorsPerFAT ; DX:AX (NumFatSecs)
  212. add AX,[BP].bsBPB.BPB_HiddenSectors ; (HiddenSecs)
  213. adc DX,[BP].bsBPB.BPB_HiddenSectorsHigh
  214. add AX,[BP].bsBPB.BPB_ReservedSectors;(ReservedSecs)
  215. adc DX,CX
  216. ;
  217. ; DX:AX = NumFats * NumFatSecs + ReservedSecs + cSecHid
  218. ;
  219. mov SI,[BP].bsBPB.BPB_RootEntries
  220. pusha
  221. mov [BP].DataSecL,AX
  222. mov [BP].DataSecH,DX
  223. mov AX,DIR_ENTRY_SIZE ; bytes per directory entry
  224. mul SI ; convert to bytes in directory (NumDirEntries)
  225. mov BX,[BP].bsBPB.BPB_BytesPerSector; add in sector size
  226. add AX,BX
  227. dec AX ; decrement so that we round up
  228. div BX ; convert to sector number
  229. add [BP].DataSecL,AX ; Start sector # of Data area
  230. adc [BP].DataSecH,CX ;
  231. popa
  232. DirSector:
  233. mov DI,BIO_OFFSET ; address in DI for comparisons
  234. call DoReadOne ;NOTE assumes CX==0!
  235. DiskErrorJump:
  236. jc DiskError ; if errors try to recover
  237. DirEntry:
  238. cmp byte ptr es:[di],ch ; empty directory entry?
  239. je MissingFile ; yes, that's the end
  240. pusha
  241. mov cl,11
  242. mov si,offset BootFilename
  243. repz cmpsb ; see if the same
  244. popa
  245. jz DoLoad ; if so, continue booting
  246. dec SI ; decrement # root entries
  247. jz MissingFile ; hmmm, file doesn't exist in root
  248. next_entry:
  249. add DI,DIR_ENTRY_SIZE
  250. cmp DI,BX ; exhausted this root dir sector yet?
  251. jb DirEntry ; no, check next entry
  252. jmp DirSector ; yes, check next sector
  253. MissingFile:
  254. mov al,byte ptr [MSGOFF_NOSYS] ; point to no system file message
  255. ;
  256. ; There has been some recoverable error. Display a message
  257. ; and wait for a keystroke. Al is the offset - 256 of the
  258. ; message within the boot sector. So we first calculate
  259. ; the real segment-based offset to the message and stick it in si
  260. ; so lodsb will work later.
  261. ;
  262. DisplayError:
  263. .ERRNZ ORIGIN MOD 256
  264. mov ah,(ORIGIN / 256) + 1
  265. mov si,ax
  266. DisplayError1:
  267. lodsb ; get next character
  268. cbw ; make 00->0000, FF->FFFF
  269. inc ax ; end of sub-message? (0xFF?)
  270. jz DisplayWait ; if so, get tail
  271. dec ax ; end of message? (0x00?)
  272. jz WaitForKey ; if so, get key
  273. mov AH,14 ; write character & attribute
  274. mov BX,7 ; attribute (white char on black)
  275. int 10h ; print the character
  276. jmp short DisplayError1
  277. DisplayWait:
  278. mov al,byte ptr [MSGOFF_COMMON]
  279. jmp short DisplayError
  280. DiskError:
  281. mov al,byte ptr [MSGOFF_IOERROR]
  282. jmp short DisplayError
  283. WaitForKey:
  284. ;we know ax is zero, thats how we got here
  285. int 16h ; get character from keyboard
  286. int 19h ; Continue in loop till good disk
  287. ;
  288. ; We now load the first sector of ntldr.
  289. ;
  290. ; All we have to do is multiply the file's starting cluster
  291. ; (whose directory entry is at ES:DI-11) by sectors per cluster and
  292. ; add that to the disk's starting data sector.
  293. ;
  294. ; Note that after we're read the sector into 2000:0 the directory sector
  295. ; will get blown away. So we need to save the starting cluster number.
  296. ;
  297. DoLoad:
  298. mov dx,es:[di].dir_first ; dx = ntldr starting cluster
  299. push dx ; save for later
  300. mov al,1 ; 1 sector
  301. mov bx,BIO_OFFSET ; into 2000:0
  302. call NtldrClusterRead
  303. jc DiskError
  304. ; =========================================================================
  305. ;
  306. ; NTLDR requires the following input conditions:
  307. ;
  308. ; BX == starting cluster number of NTLDR
  309. ; DL == int13 unit number of boot drive
  310. ; DS:SI -> BPB
  311. ; DS:DI -> arg structure
  312. ;
  313. ; =========================================================================
  314. pop bx ; starting cluster number
  315. mov dl,[bp].bsDriveNumber
  316. mov si,OFFSET CoreBpb
  317. mov di,sp ; we should be at TOS now
  318. mov [bp].pReadClustersO,OFFSET NtldrClusterRead
  319. mov [bp].pReadSectorsO,OFFSET NtldrSectorRead
  320. mov cx,ds
  321. mov [bp].pReadClustersS,cx
  322. mov [bp].pReadSectorsS,cx
  323. ;
  324. ; We do something nasty. Ntldr is expecting the readcluster and readsector
  325. ; routines to do a far return. Since ntldr is now the only guy who will be
  326. ; reading via those routines, we patch the return instruction from a near
  327. ; return to a far return.
  328. ;
  329. mov byte ptr DoReadExit,0cbh ; retf
  330. jmp FAR PTR NTLOAD ; Crank up NTLDR
  331. ;
  332. ; Sector read routine required by ntldr
  333. ;
  334. ; Read al sectors into es:bx starting from sector SectorBase
  335. ; (SectorBase is logical sector # from start of partition)
  336. ;
  337. NtldrSectorRead label near
  338. .386
  339. movzx cx,al
  340. mov eax,[bp].SectorBase
  341. add eax,dword ptr [bp].bsBPB.BPB_HiddenSectors
  342. mov edx,eax
  343. shr edx,16
  344. .286
  345. jmp short DoReadMore
  346. ;
  347. ; Cluster read routine required by ntldr
  348. ;
  349. ; Read al sectors into es:bx starting from cluster dx
  350. ;
  351. NtldrClusterRead label near
  352. .386
  353. movzx cx,al ; cx = # of sectors to read
  354. .286
  355. dec dx
  356. dec dx ; adjust for reserved clusters 0 and 1
  357. mov al,[BP].bsBPB.BPB_SectorsPerCluster
  358. xor ah,ah
  359. mul dx ; DX:AX = starting sector number
  360. add ax,[bp].DataSecL ; adjust for FATs, root dir, boot sec.
  361. adc dx,[bp].DataSecH
  362. DoPush:
  363. jmp short DoReadMore
  364. ; =========================================================================
  365. ;
  366. ; Read disk sector(s).
  367. ;
  368. ; Inputs: DX:AX == logical sector #
  369. ; CL == # sectors (CH == 0)
  370. ; ES:BX == transfer address
  371. ;
  372. ; Outputs: DX:AX next logical sector #
  373. ; CX == 0 (assuming no errors)
  374. ; ES:BX -> byte after last byte of read
  375. ; Carry set if error, else clear if success
  376. ;
  377. ; Preserves: BP, SI, DI
  378. ;
  379. ; New Notes: This function will now use extended int 13 if
  380. ; necessary. The next note is correct for standard int 13
  381. ;
  382. ; Notes: Reads sectors one at a time in case they straddle a
  383. ; track boundary. Performs full 32-bit division on the
  384. ; first decomposition (of logical sector into track+sector)
  385. ; but not on the second (of track into cylinder+head),
  386. ; since (A) we don't have room for it, and (B) the results
  387. ; of that division must yield a quotient < 1024 anyway, because
  388. ; the CHS-style INT 13h interface can't deal with cylinders
  389. ; larger than that.
  390. ;
  391. ; =========================================================================
  392. switchx13: ;setup for x13 read, and switch to old if not needed
  393. push dx ;
  394. push ax ; block number
  395. push es
  396. push bx ; transfer address
  397. push 1 ; count of one, because we're looping
  398. push 16 ; packet size
  399. xchg AX,CX ; AX -> CX
  400. mov ax,[bp].bsBPB.BPB_SectorsPerTrack ; get sectors/track
  401. xchg ax,si ; save for divides
  402. xchg AX,DX ; DX -> AX
  403. xor DX,DX ; divide 0:AX
  404. div si ; AX = high word of track
  405. xchg AX,CX ; save AX in CX and restore old AX
  406. div si ; CX:AX = track, DX = sector
  407. inc DX ; sector is 1-based
  408. xchg CX,DX ; CX = sector, DX = high word of track
  409. div [BP].bsBPB.BPB_Heads ; AX = cylinder, DX = head
  410. mov DH,DL ; head # < 255
  411. mov CH,AL ; CH = cyl #
  412. ror AH,2 ; move bits 8,9 of AX to bits 14,15
  413. ; (the rest has to be zero, since
  414. ; cyls cannot be more than 10 bits)
  415. or CL,AH ; CL = sector # plus high bits of cyl #
  416. mov AX,(ROM_DISKRD SHL 8)+1 ; disk read 1 sector
  417. cmp byte ptr [bp].PartitionTypeOffset,0Eh ;set flag to be tested
  418. jne short doio ;use extended calls if E (or C)
  419. mov ah,ROM_DISKRDX ;x13, we're ready to rock
  420. mov si,sp ; DS:SI -> X13 packet
  421. doio:
  422. mov dl,[bp].bsDriveNumber ; DL == drive #
  423. int 13h
  424. popa ; throw away packet on stack (8 words)
  425. popa ; get real registers back
  426. jc DoReadExit ; disk error
  427. inc ax
  428. jnz DoReadNext
  429. inc dx
  430. DoReadNext: ; Adjust buffer address
  431. add BX,[BP].bsBPB.BPB_BytesPerSector
  432. dec cx
  433. jnz DoReadMore
  434. clc
  435. DoReadExit:
  436. ret
  437. DoReadOne:
  438. inc cx ;assumes cx == 0! to set to 1 sector read
  439. DoRead:
  440. mov bx,BIO_OFFSET
  441. DoReadMore:
  442. pusha
  443. .386
  444. db 66h ;push a dword of 0 on the stack
  445. push 0 ;hand coded for 386
  446. .286
  447. jmp switchx13
  448. ;
  449. ; This string can go anywhere. NT and NT Setup are not picky about it.
  450. ;
  451. Public BootFilename
  452. BootFilename db "NTLDR ";
  453. ;
  454. ; Message table.
  455. ;
  456. ; We put English messages here as a placeholder only, so that in case
  457. ; anyone uses bootfat.h without patching new messages in, things will
  458. ; still be correct (in English, but at least functional).
  459. ;
  460. include msgstub.inc
  461. ;
  462. ; Now build a table with the low byte of the offset to each message.
  463. ; Code that patches the boot sector messages updates this table.
  464. ;
  465. .errnz ($-$START) GT (SECTOR_SIZE-5)
  466. ORG ORIGIN + SECTOR_SIZE - 5
  467. MSGOFF_NOSYS:
  468. db OFFSET (MSG_NOSYS - ORIGIN) - 256
  469. MSGOFF_IOERROR:
  470. db OFFSET (MSG_IOERROR - ORIGIN) - 256
  471. MSGOFF_COMMON:
  472. db OFFSET (MSG_COMMON - ORIGIN) - 256
  473. .errnz ($-$START) NE (SECTOR_SIZE-2)
  474. DB 55h,0AAh ; Boot sector signature
  475. CODE ENDS
  476. END