Leaked source code of windows server 2003
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.

1406 lines
46 KiB

  1. ;++
  2. ;
  3. ;Copyright (c) 1995 Compaq Computer Corporation
  4. ;
  5. ;Module Name:
  6. ;
  7. ; etfsboot.asm
  8. ;
  9. ;Abstract:
  10. ;
  11. ; The ROM in the IBM PC starts the boot process by performing a hardware
  12. ; initialization and a verification of all external devices. If an El
  13. ; Torito CD-ROM with no-emulation support is detected, it will then load
  14. ; the "image" pointed to in the Boot Catalog. This "image" is placed at
  15. ; the physical address specified in the Boot Catalog (which should be 07C00h).
  16. ;
  17. ; The code in this "image" is responsible for locating NTLDR, loading the
  18. ; first sector of NTLDR into memory at 2000:0000, and branching to it.
  19. ;
  20. ; There are only two errors possible during execution of this code.
  21. ; 1 - NTLDR does not exist
  22. ; 2 - BIOS read error
  23. ;
  24. ; In both cases, a short message is printed, and the user is prompted to
  25. ; reboot the system.
  26. ;
  27. ;
  28. ;Author:
  29. ;
  30. ; Steve Collins (stevec) 25-Oct-1995
  31. ;
  32. ;Environment:
  33. ;
  34. ; Image has been loaded at 7C0:0000 by BIOS. (or 0000:7C00 to support some broken BIOSes)
  35. ; Real mode
  36. ; ISO 9660 El Torito no-emulation CD-ROM Boot support
  37. ; DL = El Torito drive number we booted from
  38. ;
  39. ;Revision History:
  40. ;
  41. ; Calin Negreanu (calinn) 25-May-1998 - added safety check at the beginning of the code
  42. ; - added code for loading and executing BOOTFIX.BIN
  43. ; - modified error path
  44. ;
  45. ; Tom Jolly (tomjolly) 09-Apr-2002 - Added UDF support.
  46. ;
  47. ; Limitations of the UDF support are...
  48. ;
  49. ; - All structures (FSD, root dir etc) must be within
  50. ; the first 128Mb (64k blocks) of the disc. CDIMAGE
  51. ; always places metadata at start, so this is fine.
  52. ; - Only UDF 1.02 currently supported (no XFE)
  53. ; - Minimal checking due to code size requirements. We
  54. ; do checksum the AVDP though which should be good enough
  55. ; to be sure we're looking at a UDF disc.
  56. ; - Assumes single extent files/directories, which should be
  57. ; the case on mastered discs.
  58. ; - Assumes directories will fit in memory (i.e. between LoadSeg
  59. ; and himem).
  60. ; - No embedded files/dirs
  61. ;
  62. ; How it works in outline
  63. ;
  64. ; - Look for AVDP at block 256 only
  65. ; - Checksum it, locate VDS and scan for Partition Descriptor
  66. ; and Logical Volume Descriptor.
  67. ; - Extract Root dir FE location from LVD,
  68. ;
  69. ; (If anything failed before this point, drops back to ISO code,
  70. ; anything after and the boot will fail).
  71. ;
  72. ; - Scan Root for I386, and I386 for target files.
  73. ;--
  74. page ,132
  75. title boot - NTLDR ETFS loader
  76. name etfsboot
  77. EtfsCodeSize EQU 2048
  78. BootSeg segment at 07c0h ;31kb
  79. BootSeg ends
  80. DirSeg segment at 1000h ;64kb
  81. DirSeg ends
  82. LoadSeg segment at 2000h ;128kb
  83. LoadSeg ends
  84. ;
  85. ; Few FS related constants
  86. ;
  87. VrsStartLbn EQU 10h
  88. UdfAVDPLbn EQU 100h
  89. UdfDestagAVDP EQU 0002h
  90. UdfDestagPD EQU 0005h
  91. UdfDestagLVD EQU 0006h
  92. UdfDestagFSD EQU 0100h
  93. UdfDestagFID EQU 0101h
  94. UdfDestagFE EQU 0105h
  95. UdfDestagXFE EQU 010Ah
  96. BootCode segment ;would like to use BootSeg here, but LINK flips its lid
  97. ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
  98. public ETFSBOOT
  99. ETFSBOOT proc far
  100. cli
  101. ;WARNING!!! DO NOT CHANGE THE STACK SETUP. BOOTFIX NEEDS THIS TO BE HERE.
  102. xor ax,ax ; Setup the stack to a known good spot
  103. mov ss,ax ; Stack is set to 0000:7c00, which is just below this code
  104. mov sp,7c00h
  105. sti
  106. mov ax,cs ; Set DS to our code segment (should be 07C0h)
  107. mov ds,ax
  108. assume DS:BootCode
  109. ;
  110. ; Save the Drive Number for later use
  111. ;
  112. push dx
  113. ;
  114. ; Let's do some safety checks here. We are going to check for three things:
  115. ; 1. We are loaded at 07c0:0000 or 0000:7C00
  116. ; 2. Boot Drive Number looks good (80h-FFh)
  117. ; 3. Our code was completely loaded by the BIOS
  118. ;
  119. call NextInstr
  120. NextInstr:
  121. .386
  122. pop si ; Get IP from the stack
  123. sub si,OFFSET NextInstr ; See if we run with ORIGIN 0
  124. jz NormalCase ; Yes
  125. cmp si,7C00h ; See if, at least we run with ORIGIN 7C00H
  126. jne BootErr$wof1 ; If not, try to display some message
  127. mov ax,cs ; If offset is 7C00H, segment should be 0
  128. cmp ax,0000h
  129. jne BootErr$wof2 ; If not, try to display some message
  130. ; We are loaded at 0000:7C00 instead of 07C0:0000. This could mess up
  131. ; some stuff so we are going to fix it.
  132. ; hack to execute JMP 07c0:BootOK
  133. db 0eah
  134. dw OFFSET BootOK
  135. dw BootSeg
  136. NormalCase:
  137. mov MSG_BAD_BIOS_CODE, '3'
  138. mov ax,cs ; See if segment is 07C0H
  139. cmp ax,07c0h
  140. jne BootErr$wnb ; If not, try to display some message
  141. .8086
  142. BootOK:
  143. ;
  144. ; Reset ds in case we needed to change code segment
  145. ;
  146. mov ax,cs
  147. mov ds,ax
  148. ;
  149. ; OK so far. Let's try to see if drive letter looks good (80h-FFh)
  150. ;
  151. mov MSG_BAD_BIOS_CODE, '4'
  152. cmp dl,80h
  153. jb BootErr$wnb
  154. ;
  155. ; OK so far. Let's try to see if all our code was loaded.
  156. ; We look for our signature at the end of the code.
  157. ;
  158. mov MSG_BAD_BIOS_CODE, '5'
  159. mov bx, EtfsCodeSize - 2
  160. mov ax, WORD PTR DS:[bx]
  161. cmp ax, 0AA55h
  162. jne BootErr$wnb
  163. ;
  164. ; Finally, everything looks good.
  165. ;
  166. ;
  167. ; Save the Drive Number for later use - right now drive number is pushed on the stack
  168. ;
  169. pop dx
  170. mov DriveNum,dl
  171. ;
  172. ; Look to see if there is UDF on this disc.
  173. ;
  174. call IsThereUDF
  175. ;
  176. ; Let's try to load and run BOOTFIX.BIN
  177. ;
  178. .386
  179. push OFFSET BOOTFIXNAME
  180. push 11
  181. push LoadSeg
  182. call LoadFile
  183. jc FindSetupLdr
  184. ;
  185. ; We have BOOTFIX.BIN loaded. We call that code to see if we should boot from CD. If we shouldn't
  186. ; we'll not come back here.
  187. ;
  188. .286
  189. pusha
  190. push ds
  191. push es
  192. ;
  193. ; BOOTFIX requires:
  194. ; DL = INT 13 drive number we booted from
  195. ;
  196. mov dl, DriveNum ; DL = CD drive number
  197. ;hack to execute CALL LoadSeg:0000
  198. db 9Ah
  199. dw 0000h
  200. dw LoadSeg
  201. pop es
  202. pop ds
  203. popa
  204. .8086
  205. FindSetupldr:
  206. ;
  207. ; Scan for the presence of SETUPLDR.BIN
  208. ;
  209. .386
  210. push OFFSET LOADERNAME
  211. push 12
  212. push LoadSeg
  213. call LoadFile
  214. jc BootErr$bnf
  215. ;
  216. ; SETUPLDR requires:
  217. ; DL = INT 13 drive number we booted from
  218. ;
  219. mov dl, DriveNum ; DL = CD drive number
  220. xor ax,ax
  221. .386
  222. push LoadSeg
  223. push ax
  224. retf ; "return" to NTLDR (LoadSeg:0000h). Will not come back here.
  225. ETFSBOOT endp
  226. ;
  227. ; BootErr - print error message and hang the system.
  228. ;
  229. BootErr proc
  230. BootErr$wof1: ; we were loaded at a wrong address - Code 1
  231. PUSH SI
  232. MOV BX, SI
  233. ADD BX, OFFSET MSG_BAD_BIOS_CODE
  234. MOV BYTE PTR DS:[BX], '1'
  235. ADD SI, OFFSET MSG_BAD_BIOS
  236. JMP BootErr2
  237. BootErr$wof2: ; we were loaded at a wrong address - Code 2
  238. PUSH SI
  239. MOV BX, SI
  240. ADD BX, OFFSET MSG_BAD_BIOS_CODE
  241. MOV BYTE PTR DS:[BX], '2'
  242. ADD SI, OFFSET MSG_BAD_BIOS
  243. JMP BootErr2
  244. BootErr$wnb: ; some other BIOS problem
  245. PUSH 0
  246. MOV SI, OFFSET MSG_BAD_BIOS
  247. JMP BootErr2
  248. BootErr$bnf: ; NTLDR not found
  249. PUSH 0
  250. MOV SI, OFFSET MSG_NO_NTLDR
  251. JMP BootErr2
  252. BootErr$mof: ; memory overflow
  253. PUSH 0
  254. MOV SI, OFFSET MSG_MEM_OVERFLOW
  255. JMP BootErr2
  256. BootErr2:
  257. CALL BootErrPrint
  258. POP SI
  259. JMP BootFromHD
  260. BootErrPrint:
  261. LODSB ; Get next character
  262. OR AL, AL
  263. JZ BEdone
  264. MOV AH, 14 ; Write teletype
  265. MOV BX, 7 ; Attribute
  266. INT 10H ; Print it
  267. JMP BootErrPrint
  268. BEdone:
  269. RET
  270. ;print: ; print char in AL
  271. ; push bx
  272. ; push es
  273. ; MOV AH, 14 ; Write teletype
  274. ; MOV BX, 7 ; Attribute
  275. ; INT 10H ; Print it
  276. ; pop es
  277. ; pop bx
  278. ; ret
  279. BootErr endp
  280. ;
  281. ; we are trying to boot from HD. We need to move ourself out of
  282. ; this area because we are going to load MBR here
  283. ;
  284. BootFromHD:
  285. ;
  286. ; let's wait here for two seconds, so the user gets a chance to see the message
  287. ;
  288. ;
  289. ; hook INT08
  290. ;
  291. MOV [SI+TicksCount], 24H ; two seconds delay
  292. CLI
  293. PUSH ES
  294. XOR AX, AX
  295. MOV ES, AX
  296. MOV BX, 0020H
  297. MOV AX, ES:[BX]
  298. MOV WORD PTR [SI+OldInt08], AX
  299. MOV AX, ES:[BX+2]
  300. MOV WORD PTR [SI+OldInt08+2], AX
  301. MOV ES:[BX], SI
  302. ADD ES:[BX], OFFSET NewInt08
  303. MOV ES:[BX+2], CS
  304. POP ES
  305. STI
  306. ;
  307. ; now let's actively wait for TicksCount to become zero
  308. ;
  309. Delay:
  310. CMP [SI+TicksCount], 0
  311. JNE Delay
  312. ;
  313. ; unhook INT08
  314. ;
  315. cli
  316. push es
  317. xor ax,ax
  318. mov es,ax
  319. mov bx,08h * 4
  320. mov ax,WORD PTR [SI+OldInt08]
  321. mov es:[bx],ax
  322. mov ax,WORD PTR [SI+OldInt08+2]
  323. mov es:[bx+2],ax
  324. pop es
  325. sti
  326. ;
  327. ; now let's move ourselves away from here because we are going to load MBR here
  328. ;
  329. MoveCode:
  330. push ds
  331. push es
  332. mov ax, LoadSeg
  333. mov es, ax
  334. mov ax, cs
  335. mov ds, ax
  336. ;si is already set
  337. xor di, di
  338. mov cx, EtfsCodeSize
  339. rep movsb
  340. pop es
  341. pop ds
  342. ;hack to execute JMP LoadSeg:AfterMoveLabel
  343. db 0eah
  344. dw OFFSET AfterMoveLabel
  345. dw LoadSeg
  346. AfterMoveLabel:
  347. ;
  348. ; finally load MBR
  349. ;
  350. push es
  351. mov ax, BootSeg
  352. mov es, ax
  353. mov bx, 0000h
  354. mov ax, 0201h ;read function, one sector
  355. mov cx, 0001h
  356. mov dx, 0080h
  357. int 13h
  358. jnc MbrOk
  359. ;
  360. ; there was an error, nothing else to do
  361. ;
  362. jmp $
  363. MbrOk:
  364. pop es
  365. ;
  366. ; now let's return into MBR code
  367. ;
  368. mov dl,80h
  369. ;hack to execute JMP 0000:7C00
  370. db 0eah
  371. dw 7c00h
  372. dw 0000h
  373. ;
  374. ; We rely on the fact that SI is not changed when this INT occurs
  375. ; This is a pretty good assumption since this code is active only
  376. ; within the tight loop near Delay label. The odds are that some
  377. ; other IRQ occures, enables interrupts, changes SI and then INT08
  378. ; occures. This should not happen.
  379. ;
  380. NewInt08:
  381. PUSHF
  382. CLI
  383. CMP CS:[SI+TicksCount], 0
  384. JE Default08
  385. DEC WORD PTR CS:[SI+TicksCount]
  386. Default08:
  387. POPF
  388. PUSH WORD PTR CS:[SI+OldInt08+2]
  389. PUSH WORD PTR CS:[SI+OldInt08]
  390. RETF
  391. include etfsboot.inc ; message text
  392. ;
  393. ; ScanForEntry - Scan for an entry in a directory
  394. ;
  395. ; Entry:
  396. ; ES:0 points to the beginning of the directory to search
  397. ; Directory length in bytes is in ExtentLen1 and Extend_Len_0
  398. ;
  399. ; Exit:
  400. ; CF set on error, clear on success.
  401. ; ES:BX points to record containing entry if match is found
  402. ;
  403. ScanForEntry proc near
  404. mov ScanIncCount, 0
  405. mov cx,ExtentLen0 ; CX = length of root directory in bytes (low word only)
  406. cld ; Work up for string compares
  407. xor bx,bx
  408. xor dx,dx
  409. ScanLoop:
  410. mov si, EntryToFind
  411. mov dl,byte ptr es:[bx] ; directory record length -> DL
  412. cmp dl,0
  413. jz Skip00 ; if the "record length" assume it is "system use" and skip it
  414. mov ax,bx
  415. add ax,021h ; file identifier is at offset 21h in directory record
  416. mov di,ax ; ES:DI now points to file identifier
  417. push cx
  418. xor cx,cx
  419. mov cl,EntryLen ; compare bytes
  420. repe cmpsb
  421. pop cx
  422. jz ScanEnd ; do we have a match?
  423. CheckCountUnderFlow:
  424. ; If CX is about to underflow or be 0 we need to reset CX, ES and BX if ExtentLen1 is non-0
  425. cmp dx,cx
  426. jae ResetCount0
  427. sub cx,dx ; update CX to contain number of bytes left in directory
  428. cmp ScanIncCount, 1
  429. je ScanAdd1ToCount
  430. AdjustScanPtr: ; Adjust ES:BX to point to next record
  431. add dx,bx
  432. mov bx,dx
  433. and bx,0fh
  434. push cx
  435. mov cl,4
  436. shr dx,cl
  437. pop cx
  438. mov ax,es
  439. add ax,dx
  440. mov es,ax
  441. jmp ScanLoop
  442. Skip00:
  443. mov dx,1 ; Skip past this byte
  444. jmp CheckCountUnderFlow
  445. ScanAdd1ToCount:
  446. inc cx
  447. mov ScanIncCount,0
  448. jmp AdjustScanPtr
  449. S0:
  450. mov ScanIncCount,1 ; We'll need to increment Count next time we get a chance
  451. jmp SetNewCount
  452. ResetCount0:
  453. cmp ExtentLen1,0 ; Do we still have at least 64K bytes left to scan?
  454. jne ResetContinue
  455. stc ; We overran the end of the directory - corrupt/invalid directory
  456. ret
  457. ResetContinue:
  458. sub ExtentLen1,1
  459. add bx,dx ; Adjust ES:BX to point to next record - we cross seg boundary here
  460. push bx
  461. push cx
  462. mov cl,4
  463. shr bx,cl
  464. pop cx
  465. mov ax,es
  466. add ax,bx
  467. mov es,ax
  468. pop bx
  469. and bx,0fh
  470. sub dx,cx ; Get overflow amount
  471. je S0 ; If we ended right on the boundary we need to make special adjustments
  472. dec dx
  473. SetNewCount:
  474. mov ax,0ffffh
  475. sub ax,dx ; and subtract it from 10000h
  476. mov cx,ax ; - this is the new count
  477. jmp ScanLoop
  478. ScanEnd:
  479. cmp IsDir,1
  480. je CheckDir
  481. test byte ptr es:[bx][25],2 ; Is this a file?
  482. jnz CheckCountUnderFlow ; No - go to next record
  483. jmp CheckLen
  484. CheckDir:
  485. test byte ptr es:[bx][25],2 ; Is this a directory?
  486. jz CheckCountUnderFlow ; No - go to next record
  487. CheckLen:
  488. mov al,EntryLen
  489. cmp byte ptr es:[bx][32],al ; Is the identifier length correct?
  490. jnz CheckCountUnderFlow ; No - go to next record
  491. clc
  492. ret
  493. ScanForEntry endp
  494. ;
  495. ; ExtRead - Do an INT 13h extended read
  496. ; NOTE: I force the offset of the Transfer buffer address to be 0
  497. ; I force the high 2 words of the Starting absolute block number to be 0
  498. ; - This allows for a max 4 GB medium - a safe assumption for now
  499. ;
  500. ; Entry:
  501. ; Arg1 - word 0 (low word) of Number of 2048-byte blocks to transfer
  502. ; Arg2 - word 1 (high word) of Number of 2048-byte blocks to transfer
  503. ; Arg3 - segment of Transfer buffer address
  504. ; Arg4 - word 0 (low word) of Starting absolute block number
  505. ; Arg5 - word 1 of Starting absolute block number
  506. ;
  507. ; Exit
  508. ; The following are modified:
  509. ; Count0
  510. ; Count1
  511. ; Dest
  512. ; Source0
  513. ; Source1
  514. ; PartialRead
  515. ; NumBlocks
  516. ; Disk Address Packet [DiskAddPack]
  517. ;
  518. ExtRead proc near
  519. push bp ; set up stack frame so we can get args
  520. mov bp,sp
  521. push bx ; Save registers used during this routine
  522. push si
  523. push dx
  524. push ax
  525. mov bx,offset DiskAddPack ; Use BX as base to index into Disk Address Packet
  526. ; Set up constant fields
  527. mov [bx][0],byte ptr 010h ; Offset 0: Packet size = 16 bytes
  528. mov [bx][1],byte ptr 0h ; Offset 1: Reserved (must be 0)
  529. mov [bx][3],byte ptr 0h ; Offset 3: Reserved (must be 0)
  530. mov [bx][4],word ptr 0h ; Offset 4: Offset of Transfer buffer address (force 0)
  531. mov [bx][12],word ptr 0h ; Offset 12: Word 2 of Starting absolute block number (force 0)
  532. mov [bx][14],word ptr 0h ; Offset 14: Word 3 (high word) of Starting absolute block number (force 0)
  533. ;
  534. ; Initialize loop variables
  535. ;
  536. mov ax,[bp][12] ; set COUNT to number of blocks to transfer
  537. mov Count0,ax
  538. mov ax,[bp][10]
  539. mov Count1,ax
  540. mov ax,[bp][8] ; set DEST to destination segment
  541. mov Dest,ax
  542. mov ax,[bp][6] ; set SOURCE to source lbn
  543. mov Source0,ax
  544. mov ax,[bp][4]
  545. mov Source1,ax
  546. ExtReadLoop:
  547. ;
  548. ; First check if COUNT <= 32
  549. ;
  550. cmp Count1,word ptr 0h ; Is upper word 0?
  551. jne SetupPartialRead ; No - we're trying to read at least 64K blocks (128 MB)
  552. cmp Count0,word ptr 20h ; Is lower word greater than 32?
  553. jg SetupPartialRead ; Yes - only read in 32-block increments
  554. mov PartialRead,0 ; Clear flag to indicate we are doing a full read
  555. mov ax,Count0 ; NUMBLOCKS = COUNT
  556. mov NumBlocks,al ; Since Count0 < 32 we're OK just using low byte
  557. jmp DoExtRead ; Do read
  558. SetupPartialRead:
  559. ;
  560. ; Since COUNT > 32,
  561. ; Set flag indicating we are only doing a partial read
  562. ;
  563. mov PartialRead,1
  564. mov NumBlocks,20h ; NUMBYTES = 32
  565. DoExtRead:
  566. ;
  567. ; Perform Extended Read
  568. ;
  569. mov al,NumBlocks ; Offset 2: Number of 2048-byte blocks to transfer
  570. mov [bx][2],al
  571. mov ax,Dest ; Offset 6: Segment of Transfer buffer address
  572. mov [bx][6],ax
  573. mov ax,Source0 ; Offset 8: Word 0 (low word) of Starting absolute block number
  574. mov [bx][8],ax
  575. mov ax,Source1 ; Offset 10: Word 1 of Starting absolute block number
  576. mov [bx][10],ax
  577. mov si,offset DiskAddPack ; Disk Address Packet in DS:SI
  578. mov ah,042h ; Function = Extended Read
  579. mov dl,DriveNum ; CD-ROM drive number
  580. int 13h
  581. ;
  582. ; Determine if we are done reading
  583. ;
  584. cmp PartialRead,1 ; Did we just do a partial read?
  585. jne ExtReadDone ; No - we're done
  586. ReadjustValues:
  587. ;
  588. ; We're not done reading yet, so
  589. ; COUNT = COUNT - 32
  590. ;
  591. sub Count0,020h ; Subtract low-order words
  592. sbb Count1,0h ; Subtract high-order words
  593. ;
  594. ; Just read 32 blocks and have more to read
  595. ; Increment DEST to next 64K segment (this equates to adding 1000h to the segment)
  596. ;
  597. add Dest,1000h
  598. jc BootErr$mof ; Error if we overflowed
  599. ;
  600. ; SOURCE = SOURCE + 32 blocks
  601. ;
  602. add Source0,word ptr 020h ; Add low order words
  603. adc Source1,word ptr 0h ; Add high order words
  604. ; NOTE - I don't account for overflow - probably OK now since we already account for 4 GB medium
  605. ;
  606. ; jump back to top of loop to do another read
  607. ;
  608. jmp ExtReadLoop
  609. ExtReadDone:
  610. pop ax ; Restore registers used during this routine
  611. pop dx
  612. pop si
  613. pop bx
  614. mov sp,bp ; restore BP and SP
  615. pop bp
  616. ret
  617. ExtRead endp
  618. ;
  619. ; ReadExtent - Read in an extent
  620. ;
  621. ; Arg1 - segment to transfer extent to
  622. ;
  623. ; Entry:
  624. ; ExtentLen0 = word 0 (low word) of extent length in bytes
  625. ; ExtentLen1 = word 1 (high word) of extent length in bytes
  626. ; ExtentLoc0 = word 0 (low word) of starting absolute block number of extent
  627. ; ExtentLoc1 = word 1 of starting absolute block number of extent
  628. ;
  629. ; Exit:
  630. ; ExtRead exit mods
  631. ;
  632. ReadExtent proc near
  633. push bp ; set up stack frame so we can get args
  634. mov bp,sp
  635. push cx ; Save registers used during this routine
  636. push bx
  637. push ax
  638. mov cl,11 ; Convert length in bytes to 2048-byte blocks
  639. mov bx,ExtentLen1 ; Directory length = BX:AX
  640. mov ax,ExtentLen0
  641. .386
  642. shrd ax,bx,cl ; Shift AX, filling with BX
  643. .8086
  644. shr bx,cl ; BX:AX = number of blocks (rounded down)
  645. test ExtentLen0,07ffh ; If any of the low-order 11 bits are set we need to round up
  646. jz ReadExtentNoRoundUp
  647. add ax,1 ; We need to round up by incrementing AX, and
  648. adc bx,0 ; adding the carry to BX
  649. ReadExtentNoRoundUp:
  650. push ax ; Word 0 (low word) of Transfer size = AX
  651. push bx ; Word 1 (high word) of Transfer size = BX
  652. .286
  653. push [bp][4] ; Segment used to transfer extent
  654. .8086
  655. push ExtentLoc0 ; Word 0 (low word) of Starting absolute block number
  656. push ExtentLoc1 ; Word 1 of Starting absolute block number
  657. call ExtRead
  658. add sp,10 ; Clean 5 arguments off the stack
  659. pop ax ; Restore registers used during this routine
  660. pop bx
  661. pop cx
  662. mov sp,bp ; restore BP and SP
  663. pop bp
  664. ret
  665. ReadExtent endp
  666. ;
  667. ; GetExtentInfo - Get extent location
  668. ;
  669. ; Entry:
  670. ; ES:BX points to record
  671. ; Exit:
  672. ; Location -> ExtentLoc1 and ExtentLoc0
  673. ; Length -> ExtentLen1 and ExtentLen0
  674. ;
  675. GetExtentInfo proc near
  676. push ax ; Save registers used during this routine
  677. mov ax,es:[bx][2] ; 32-bit LBN of extent
  678. mov ExtentLoc0,ax ; store low word
  679. mov ax,es:[bx][4]
  680. mov ExtentLoc1,ax ; store high word
  681. mov ax,es:[bx][10] ; 32-bit file length in bytes
  682. mov ExtentLen0,ax ; store low word
  683. mov ax,es:[bx][12]
  684. mov ExtentLen1,ax ; store high word
  685. pop ax ; Restore registers used during this routine
  686. ret
  687. GetExtentInfo endp
  688. LoadFileISO proc near
  689. push bp
  690. mov bp, sp
  691. ;
  692. ; First thing, we need to read in the Primary Volume Descriptor so we can locate the root directory
  693. ;
  694. .286
  695. push 01h ; Word 0 (low word) of Transfer size = 1 block (2048 bytes)
  696. push 0h ; Word 1 (high word) of Transfer size = 0
  697. push DirSeg ; Segment of Transfer buffer = DirSeg
  698. push VrsStartLbn ; Word 0 (low word) of Starting absolute block number = 10h
  699. push 0h ; Word 1 of Starting absolute block number = 0
  700. .8086
  701. call ExtRead
  702. add sp,10 ; Clean 5 arguments off the stack
  703. ;
  704. ; Determine the root directory location LBN -> ExtentLoc1:ExtentLoc0
  705. ; determine the root directory data length in bytes -> ExtentLen1:ExtentLen0
  706. ;
  707. mov ax,DirSeg ; ES is set to segment used for storing PVD and directories
  708. mov es,ax
  709. ASSUME ES:DirSeg
  710. mov ax,es:[09eh] ; 32-bit LBN of extent at offset 158 in Primary Volume Descriptor
  711. mov ExtentLoc0,ax ; store low word
  712. mov ax,es:[0a0h]
  713. mov ExtentLoc1,ax ; store high word
  714. mov ax,es:[0a6h] ; 32-bit Root directory data length in bytes at offset 166 in Primary Volume Descriptor
  715. mov ExtentLen0,ax ; store low word
  716. mov ax,es:[0a8h]
  717. mov ExtentLen1,ax ; store high word
  718. ;
  719. ; Now read in the root directory
  720. ;
  721. .286
  722. push DirSeg ; Segment used for transfer = DirSeg
  723. .8086
  724. call ReadExtent
  725. add sp,2 ; Clean 1 argument off the stack
  726. ;
  727. ; Scan for the presence of the I386 directory
  728. ; ES points to directory segment
  729. ;
  730. mov EntryToFind, offset I386DIRNAME
  731. mov EntryLen,4
  732. mov IsDir,1
  733. call ScanForEntry
  734. jc EntryNotFound
  735. ;
  736. ; We found the I386 directory entry, so now get its extent location (offset -31 from filename ID)
  737. ; ES:[BX] still points to the directory record for the I386 directory
  738. ;
  739. call GetExtentInfo
  740. ;
  741. ; Now read in the I386 directory
  742. ;
  743. .286
  744. push DirSeg ; Segment used for transfer = DirSeg
  745. .8086
  746. call ReadExtent
  747. add sp,2 ; Clean 1 argument off the stack
  748. ;
  749. ; Scan for the presence of the file that we need
  750. ; ES points to directory segment
  751. ;
  752. mov ax, DirSeg
  753. mov es, ax
  754. mov ax, [bp][8]
  755. mov EntryToFind, ax
  756. mov al, [bp][6]
  757. mov EntryLen, al
  758. mov IsDir,0
  759. call ScanForEntry
  760. jc EntryNotFound
  761. ;
  762. ; We found the needed file, so now get its extent location (offset -31 from filename ID)
  763. ; ES:[BX] still points to the directory record for that code
  764. ;
  765. call GetExtentInfo
  766. ;
  767. ; Now, go read the file
  768. ;
  769. .286
  770. push [bp][4] ; Segment used for transfer
  771. .8086
  772. call ReadExtent
  773. add sp,2 ; Clean 1 argument off the stack
  774. EntryNotFound:
  775. pop bp
  776. ret
  777. LoadFileISO endp
  778. ;
  779. ; Entry:
  780. ; arg0 - offset to file name to load from i386 directory (bp[8])
  781. ; arg1 - length of name in bytes
  782. ; arg2 - segment to load data into (bp[4])
  783. ;
  784. ; Exit:
  785. ; CARRY SET => not found, CLEAR => found & loaded
  786. ;
  787. LoadFile proc near
  788. cmp MediaIsUdf,1
  789. .386
  790. je LoadFileUDF
  791. jmp LoadFileISO
  792. .286
  793. LoadFile endp
  794. OldInt08 DD ? ; Default Int08 vector
  795. TicksCount dw 24H ; two seconds
  796. DiskAddPack db 16 dup (?) ; Disk Address Packet
  797. PartialRead db 0 ; Boolean indicating whether or not we are doing a partial read
  798. LOADERNAME db "SETUPLDR.BIN"
  799. BOOTFIXNAME db "BOOTFIX.BIN"
  800. I386DIRNAME db "I386"
  801. DriveNum db ? ; Drive number used for INT 13h extended reads
  802. ExtentLoc0 dw ? ; Loader LBN - low word
  803. ExtentLoc1 dw ? ; Loader LBN - high word
  804. ExtentLen0 dw ? ; Loader Length - low word
  805. ExtentLen1 dw ? ; Loader Length - high word
  806. Count0 dw ? ; Read Count - low word
  807. Count1 dw ? ; Read Count - high word
  808. Dest dw ? ; Read Destination segment
  809. Source0 dw ? ; Read Source - word 0 (low word)
  810. Source1 dw ? ; Read Source - word 1
  811. NumBlocks db ? ; Number of blocks to Read
  812. EntryToFind dw ? ; Offset of string trying to match in ScanForEntry
  813. EntryLen db ? ; Length in bytes of entry to match in ScanForEntry
  814. IsDir db ? ; Boolean indicating whether or not entry to match in ScanForEntry is a directory
  815. ScanIncCount db ? ; Boolean indicating if we need to add 1 to Count after adjustment in ScanForEntry
  816. MediaIsUdf db 0 ; Boolean if true => media contains UDF
  817. ;
  818. ; IsThereUDF - look to see if we have UDF here and, if so, locate the
  819. ; root directory for use in file lookups later.
  820. ;
  821. IsThereUDF proc near
  822. push bp
  823. mov bp, sp
  824. mov ax, DirSeg
  825. mov es, ax
  826. ;
  827. ; Look for an AVDP @ block 256 only
  828. ;
  829. .286
  830. push 01h ; Word 0 (low word) of Transfer size
  831. push 0h ; Word 1 (high word) of Transfer size
  832. push DirSeg ; Segment of Transfer buffer = DirSeg
  833. push UdfAVDPLbn ; Word 0 (low word) of Starting absolute block number
  834. push 0h ; Word 1 of Starting absolute block number = 0
  835. .8086
  836. call ExtRead
  837. add sp,10 ; Clean 5 arguments off the stack
  838. cmp word ptr es:[0], UdfDestagAVDP ; No match - bail
  839. .386
  840. jne ExitIsThereUdf
  841. .8086
  842. mov al, es:[0] ; Calculate checksum - bytes 0+1+2
  843. add al, es:[1]
  844. add al, es:[2]
  845. mov bx, 5 ; ...+ bytes [5..15]
  846. AVDPChecksum:
  847. add al, byte ptr es:[bx]
  848. inc bx
  849. cmp bx, 16
  850. jne AVDPCheckSum
  851. cmp es:[4], al ; does it match the checksym in the descriptor?
  852. .386
  853. jne ExitIsThereUdf
  854. .286
  855. ;
  856. ; So the AVDP checked out - pretty good indicator that we have UDF here
  857. ; Now read the VDS extent, and locate the root directory.
  858. ;
  859. .286
  860. mov ax,es:[20] ; 32-bit PSN of extent
  861. mov ExtentLoc0,ax ; store low word
  862. mov ax,es:[22]
  863. mov ExtentLoc1,ax ; store high word
  864. mov ax,es:[16] ; 32-bit extent length (bytes)
  865. mov ExtentLen0,ax ; store low word
  866. xor ax, ax
  867. cmp es:[18], ax ; bail if high word nonzero
  868. jne ExitIsThereUdf
  869. mov ExtentLen1,0 ; store high word (force 0 i.e. <= 64kb length)
  870. push DirSeg ; Segment used for transfer = DirSeg
  871. .8086
  872. call ReadExtent
  873. add sp,2 ; Clean 1 argument off the stack
  874. .286
  875. ;
  876. ; Walk through looking for LVD (to get to FSD) and PD (to find partition start)
  877. ;
  878. mov bx, 0
  879. CheckForLvdPd:
  880. mov ax, es:[bx]
  881. cmp ax, UdfDestagLVD
  882. jne NotLvd
  883. ;
  884. ; Found an LVD - extract the FSD location
  885. ;
  886. mov ax,es:[bx][252] ; 32-bit LBN of extent
  887. mov FSDLbn0,ax ; store low word
  888. mov ax,es:[bx][254]
  889. mov FSDLbn1,ax ; store high word
  890. jmp CheckFoundBoth
  891. NotLvd:
  892. cmp ax, UdfDestagPD
  893. jne NextVdsBlock
  894. ;
  895. ; Found PD - extract partition start
  896. ;
  897. mov ax,es:[bx][188] ; 32-bit LBN of extent
  898. mov PartitionLbn0,ax ; store low word
  899. cmp word ptr es:[bx][190], 0 ; we don't currently support a partition
  900. .386 ; start address > 128mb (64k blocks)
  901. jne BootErr$bnf
  902. .286
  903. ;
  904. ; If we've found both descriptors, then we're done here
  905. ;
  906. CheckFoundBoth:
  907. cmp PartitionLbn0, 0
  908. je NextVdsBlock
  909. cmp FSDLbn0, 0ffffh ; FSD is typically at LBN 0, so we check against
  910. jne FoundBoth ; an impossible LBN which we init the fields with.
  911. cmp FSDLbn1, 0ffffh
  912. jne FoundBoth
  913. ;
  914. ; No, process next block in the VDS
  915. ;
  916. NextVdsBlock:
  917. add bx, 0800h ; next block of VDS
  918. cmp bx, ExtentLen0 ; if there are more blocks, loop.
  919. jne CheckForLvdPd
  920. ExitIsThereUDF:
  921. pop bp
  922. ret
  923. FoundBoth:
  924. ;
  925. ; So we found both LVD and PVD, so now load up the FSD
  926. ;
  927. mov ax, PartitionLbn0 ; convert part rel FSD lbn -> absolute PSN
  928. add ax, FSDLbn0
  929. push 1 ; low word transfer size (blocks)
  930. push 0 ; high...
  931. push DirSeg ; Segment used for transfer = DirSeg
  932. push ax ; low word start PSN
  933. push 0 ; high
  934. .8086
  935. call ExtRead
  936. add sp,10 ; Clean arguments
  937. .286
  938. cmp word ptr es:[0], UdfDestagFSD ; Is the tag right?
  939. jne ExitIsThereUdf ; No, bail out
  940. ;
  941. ; Extract the Lbn of the root directory FE
  942. ;
  943. mov ax,es:[404] ; 32-bit LBN of extent
  944. mov RootFELbn0,ax ; store low word
  945. mov ax,es:[406]
  946. mov RootFELbn1,ax ; store high word
  947. mov MediaIsUdf, 1 ; OK, so we've found the root dir - done!
  948. jmp ExitIsThereUDF
  949. IsThereUDF endp
  950. ;
  951. ; arg 0 = seg to load data to (bp[6])
  952. ; arg 1 = FE lbn lowword (bp[4]) (part rel LBN)
  953. ;
  954. ; Exit - ExtentLocX and ExtentLenX will hold location/len of stream data
  955. ;
  956. LoadStreamFromFE proc near
  957. .8086
  958. push bp
  959. mov bp, sp
  960. mov ax, [bp][4] ; FE LBN (low word)
  961. add ax, PartitionLbn0 ; Add partition base PSN
  962. .286
  963. push 1
  964. push 0
  965. push DirSeg
  966. push ax
  967. push 0
  968. call ExtRead
  969. add sp, 10 ; Clean args
  970. mov ax, DirSeg
  971. mov es, ax
  972. cmp word ptr es:[0], UdfDestagFE ; Is the tag an FE?
  973. .386
  974. jne BootErr$bnf ; No, error.
  975. ;
  976. ; Ensure L_AD == 8 (1 x short AD)
  977. ;
  978. cmp word ptr es:[172], 8
  979. jne BootErr$bnf ; No, error.
  980. cmp word ptr es:[174], 0
  981. jne BootErr$bnf
  982. ;
  983. ; Skip any EAs
  984. ;
  985. cmp word ptr es:[170], 0 ; better not be > 64Kb of EAs!
  986. jne BootErr$bnf
  987. mov bx, es:[168] ; pickup low word of L_EA to adjust AD offset later.
  988. .286
  989. ;
  990. ; Extract the stream start Lbn / Length
  991. ;
  992. mov ax, es:[bx][176] ; low word of length (bytes)
  993. mov ExtentLen0, ax
  994. mov ax, es:[bx][178] ; high word of length (bytes)
  995. mov ExtentLen1, ax
  996. mov ax, es:[bx][182] ; high word of LBN
  997. mov ExtentLoc1, ax
  998. mov ax, es:[bx][180] ; low word of LBN
  999. add ax, PartitionLbn0 ; convert part rel -> absolute block number
  1000. adc ExtentLoc1, 0 ; carry
  1001. mov ExtentLoc0, ax
  1002. push [bp][6] ; target segment
  1003. .8086
  1004. call ReadExtent
  1005. add sp,2 ; Clean 1 argument off the stack
  1006. pop bp
  1007. ret
  1008. LoadStreamFromFE endp
  1009. ;
  1010. ; On Entry:
  1011. ;
  1012. ; arg[0] = dir FE (part. rel) LBN low word (bp[10])
  1013. ; arg[1] = OFFSET name (ASCII)
  1014. ; arg[2] = name len
  1015. ; arg[3] = OFFSET DWORD to place FE LBN (part.rel) (bp[4])
  1016. ;
  1017. ; On Exit:
  1018. ; output Lbn (arg3) = ffffffffh if not found. Otherwise, found.
  1019. ;
  1020. FindFEForName proc near
  1021. .286
  1022. push bp
  1023. mov bp, sp
  1024. push es
  1025. ;
  1026. ; Set output to invalid LBN
  1027. ;
  1028. mov bx, [bp][4]
  1029. mov [bx], 0ffffh
  1030. mov [bx][2], 0ffffh
  1031. ;
  1032. ; Load the directory stream from it's FE. Dir could be large (64 bytes/entry),
  1033. ; so we load it into LoadSeg, which gives us plenty of space (hopefully).
  1034. ;
  1035. push LoadSeg
  1036. push [bp][10] ; Lbn low word
  1037. call LoadStreamFromFE
  1038. add sp, 4
  1039. ;
  1040. ; NOTE now the length of the directory stream is in ExtentLen0 / 1
  1041. ;
  1042. mov ax, LoadSeg
  1043. mov es, ax
  1044. mov bx, 0 ; Offset into dir of current FID
  1045. ;
  1046. ; Scan the directory for a matching FID
  1047. ;
  1048. ; es -> dir seg.
  1049. ; bx -> offset in seg (es)
  1050. ; si = offset in DS of required name
  1051. ;
  1052. ExamineFID:
  1053. cmp word ptr es:[bx], UdfDestagFID ; Look like a FID?
  1054. .386
  1055. jne BootErr$bnf
  1056. .286
  1057. mov CurrentFidOffset, bx ; Store the base offset of this FID.
  1058. mov cl, es:[bx][19] ; Get File identifier length (incl. comp.id)
  1059. mov dx, es:[bx][36] ; Get L_IU
  1060. add bx, dx ; Skip impuse area... BX->identifier start
  1061. add bx, 38 ; sizeof( FID)
  1062. mov di, 0 ; assume ASCII (skip 0 bytes / char)
  1063. cmp cl, 0 ; Check for zero length ID (parent entry case)
  1064. je NoMatch
  1065. mov ch, es:[bx] ; read compression ID (first byte of identifier)
  1066. dec cl ; chop compid off length
  1067. inc bx ; skip compression ID
  1068. cmp ch, 10h ; is this a UTF-16 name?
  1069. jne MatchLength ; no, compare as is, no skip req'd.
  1070. shr cl, 1 ; yes, convert length to characters (/2)
  1071. mov di, 1 ; skip 1 byte per char
  1072. inc bx ; skip high byte of first char (UDF Unicode is big endian)
  1073. MatchLength:
  1074. cmp cl, [bp][6] ; same length as desired match?
  1075. jne NoMatch ; no, don't bother with the name comparison
  1076. mov si, [bp][8] ; Offset to name to match
  1077. MatchNextChar:
  1078. mov al, es:[bx] ; fetch FID char
  1079. cmp al, 061h ; upcase if neccessary
  1080. jb Upcased
  1081. cmp al, 07ah
  1082. ja UpCased
  1083. and al, 0dfh ; clear bit 5
  1084. Upcased:
  1085. cmp al, [si] ; compare against source char
  1086. jne NoMatch
  1087. inc si ; point to next char in source
  1088. inc bx ; point to next char in FID
  1089. add bx, di ; skip a zero byte if unicode
  1090. dec cl ; decrease chars remaining
  1091. jnz MatchNextChar
  1092. ;
  1093. ; We have a match! Extract the location of the FE from the FID
  1094. ;
  1095. mov bx, CurrentFidOffset
  1096. mov di, [bp][4]
  1097. mov ax, es:[bx][24] ; LBN low
  1098. mov [di], ax
  1099. mov ax, es:[bx][26] ; LBN high
  1100. mov [di][2], ax
  1101. jmp ExitFindFEForName
  1102. NoMatch:
  1103. ;
  1104. ; This name doesn't match, so skip to next FID
  1105. ;
  1106. mov ch, 0 ; skip remaining chars. Clear CH. CL is bytes remaining .
  1107. add bx, cx
  1108. cmp di, 0 ; If this was unicode, we need to add char count twice
  1109. jz CheckMoreFids
  1110. add bx, cx
  1111. dec bx ; step back one to compensate for skipping the first byte of first char
  1112. CheckMoreFids:
  1113. ;
  1114. ; So here we are. BX is our offset in the current segment, we need to
  1115. ;
  1116. ; 1. dword align offset (see ECMA 167 4/14.4.9), and decrease bytes remaining.
  1117. ;
  1118. add bx, 3
  1119. and bx, 0fffch
  1120. mov ax, bx ; get size of the FID we just processed
  1121. sub ax, CurrentFidOffset
  1122. sub ExtentLen0, ax ; and chop if off bytes remaining
  1123. jae AlignES
  1124. sub ExtentLen1, 1 ; dec high word
  1125. jb ExitFindFEForName
  1126. ;
  1127. ; 2. compare offset to directory length. For simplicity, to avoid ever straddling
  1128. ; a segment boundry, we'll advance the seg pointer every time we cross a 4kb
  1129. ; aligned address (i.e. 0x1000).
  1130. ;
  1131. AlignES:
  1132. cmp bx, 1000h
  1133. jb CheckEndOfDir
  1134. sub bx, 1000h ; dec offset by 4k
  1135. mov ax, es
  1136. add ax, 100h ; inc segment base reg compensate
  1137. mov es, ax
  1138. CheckEndOfDir:
  1139. .386
  1140. cmp ExtentLen1, 0 ; high word != 0 -> plenty more.
  1141. ja ExamineFID
  1142. cmp ExtentLen0, 42 ; remaining must be >= sizeof( FID) + 4 (min size with name)
  1143. jae ExamineFID ; More...
  1144. .286
  1145. ExitFindFEForName:
  1146. pop es
  1147. pop bp
  1148. ret
  1149. FindFEForName endp
  1150. ;
  1151. ; See LoadFile for params.
  1152. ;
  1153. LoadFileUDF proc near
  1154. push bp
  1155. mov bp, sp
  1156. ;
  1157. ; Find I386 in the root dir, if we haven't already.
  1158. ;
  1159. cmp I386FELbn0, 0
  1160. jne AlreadyLookedupI386
  1161. cmp I386FELbn1, 0
  1162. jne AlreadyLookedupI386
  1163. push RootFELbn0 ; NOTE only takes low word of LBN currently (limit 128Mb)
  1164. push offset I386DIRNAME
  1165. push 4
  1166. push offset I386FELbn0
  1167. call FindFEForName
  1168. add sp, 8
  1169. cmp I386FELbn1, 0ffffh ; Fatal error - didn't find i386
  1170. .386
  1171. je BootErr$bnf
  1172. .286
  1173. AlreadyLookedupI386:
  1174. ;
  1175. ; Now look for the target name in the I386 directory
  1176. ;
  1177. push I386FELbn0 ; NOTE only takes low word of LBN currently (limit 128Mb)
  1178. push [bp][8] ; Offset of name to find
  1179. push [bp][6] ; Length of entry
  1180. push offset CurrentFileFE0 ; Addr to store FE Lbn
  1181. call FindFEForName
  1182. add sp, 8
  1183. cmp CurrentFileFE0, 0ffffh ; Did we find a match?
  1184. jne ExitLFUDFFound
  1185. stc
  1186. jmp ExitLFUDF
  1187. ;
  1188. ; Yes, load the file.
  1189. ;
  1190. ExitLFUDFFound:
  1191. push LoadSeg
  1192. push CurrentFileFE0
  1193. call LoadStreamFromFE
  1194. add sp, 4
  1195. clc
  1196. ExitLFUDF:
  1197. pop bp
  1198. ret
  1199. LoadFileUDF endp
  1200. ; NOTE: Do NOT change the ordering of the next 3 decls.
  1201. UDFPVDSIG db 0 ; Opening 4 bytes of UDF SVD.
  1202. db "NSR"
  1203. UDFSVDSIG1XX db "02" ; Alternate final 2 bytes of UDF SVD,
  1204. UDFSVDSIG20X db "03" ; depends on version of UDF used.
  1205. PartitionLbn0 dw 0
  1206. FSDLbn0 dw 0ffffh
  1207. FSDLbn1 dw 0ffffh
  1208. RootFELbn0 dw 0
  1209. RootFELbn1 dw 0
  1210. RootDirIsXFE db 0
  1211. I386FELbn0 dw 0
  1212. I386FELbn1 dw 0
  1213. CurrentFileFE0 dw 0
  1214. CurrentFileFE1 dw 0
  1215. CurrentFidOffset dw 0 ; Offset within segment of current FID during name scan
  1216. tempmsg dw 0
  1217. .errnz ($-ETFSBOOT) GT (EtfsCodeSize - 2) ; FATAL PROBLEM: boot sector is too large
  1218. org (EtfsCodeSize - 2)
  1219. db 55h,0aah
  1220. BootSectorEnd label dword
  1221. BootCode ends
  1222. END ETFSBOOT