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.

3427 lines
97 KiB

  1. NEC_98=1;
  2. page ,132
  3. title ntfsboot - NTFS boot loader
  4. name ntfsboot
  5. ; The ROM in the IBM PC starts the boot process by performing a hardware
  6. ; initialization and a verification of all external devices. If all goes
  7. ; well, it will then load from the boot drive the sector from track 0, head 0,
  8. ; sector 1. This sector is placed at physical address 07C00h.
  9. ;
  10. ; The boot code's sole resposiblity is to find NTLDR, load it at
  11. ; address 2000:0000, and then jump to it.
  12. ;
  13. ; The boot code understands the structure of the NTFS root directory,
  14. ; and is capable of reading files. There is no contiguity restriction.
  15. ;
  16. ; DEBUG EQU 1
  17. MASM equ 1
  18. .xlist
  19. .286
  20. A_DEFINED EQU 1
  21. include ntfs.inc
  22. DoubleWord struc
  23. lsw dw ?
  24. msw dw ?
  25. DoubleWord ends
  26. IFDEF NEC_98
  27. PartitionDescriptor struc
  28. BootableFlag db ?
  29. PartitionType db ?
  30. Reserved dw ?
  31. IPLSector db ?
  32. IPLTrack db ?
  33. IPLCylinder dw ?
  34. StartingSector db ?
  35. StartingTrack db ?
  36. StartingCylinder dw ?
  37. EndingSector db ?
  38. EndingTrack db ?
  39. EndingCylinder dw ?
  40. PartitionName db 16 dup(?) ; ASCII strings
  41. PartitionDescriptor ends
  42. DAUA equ 584h
  43. endif
  44. ;
  45. ; The following are various segments used by the boot loader. The first
  46. ; two are the segments where the boot sector is initially loaded and where
  47. ; the boot sector is relocated to. The third is the static location
  48. ; where the NTLDR is loaded.
  49. ;
  50. IFDEF NEC_98
  51. ; We must not use BootSeg.
  52. ; Because, we don't know address that this module is loaded.
  53. ; The address transform under the influence of Sector Length and Machine Mode.
  54. else
  55. BootSeg segment at 07c0h ; this is where the MBR loads us initially.
  56. BootSeg ends
  57. endif
  58. NewSeg segment at 0d00h ; this is where we'll relocate to.
  59. NewSeg ends ; enough for 16 boot sectors +
  60. ; 4-sector scratch
  61. ; below where we'll load NTLDR.
  62. LdrSeg segment at 2000h ; we want to load the loader at 2000:0000
  63. LdrSeg ends
  64. ;/********************** START OF SPECIFICATIONS ************************/
  65. ;/* */
  66. ;/* SUBROUTINE NAME: ntfsboot */
  67. ;/* */
  68. ;/* DESCRIPTIVE NAME: Bootstrap loader */
  69. ;/* */
  70. ;/* FUNCTION: To load NTLDR into memory. */
  71. ;/* */
  72. ;/* NOTES: ntfsboot is loaded by the ROM BIOS (Int 19H) at */
  73. ;/* physical memory location 0000:7C00H. */
  74. ;/* ntfsboot runs in real mode. */
  75. ;/* This boot record is for NTFS volumes only. */
  76. ;/* */
  77. ;/* ENTRY POINT: ntfsboot */
  78. ;/* LINKAGE: Jump (far) from Int 19H */
  79. ;/* */
  80. ;/* INPUT: CS:IP = 0000:7C00H */
  81. ;/* SS:SP = 0030:00FAH (CBIOS dependent) */
  82. ;/* */
  83. ;/* EXIT-NORMAL: DL = INT 13 drive number we booted from */
  84. ;/* Jmp to main in NTLDR */
  85. ;/* */
  86. ;/* EXIT-ERROR: None */
  87. ;/* */
  88. ;/* EFFECTS: NTLDR is loaded into the physical memory */
  89. ;/* location 00020000H */
  90. ;/* */
  91. ;/*********************** END OF SPECIFICATIONS *************************/
  92. BootCode segment ;would like to use BootSeg here, but LINK flips its lid
  93. assume cs:BootCode,ds:nothing,es:nothing,ss:nothing
  94. org 0 ; start at beginning of segment, not 0100h.
  95. public _ntfsboot
  96. _ntfsboot label near
  97. IFDEF NEC_98
  98. jmp short start
  99. else
  100. jmp start
  101. endif
  102. .errnz ($-_ntfsboot) GT (3) ;<FATAL PROBLEM: JMP is more than three bytes>
  103. org 3
  104. Version db "NTFS " ; Signature, must be "NTFS "
  105. BPB label byte
  106. BytesPerSector dw 512 ; Size of a physical sector
  107. SectorsPerCluster db 1 ; Sectors per allocation unit
  108. ;
  109. ; Traditionally the next 7 bytes were the reserved sector count, fat count,
  110. ; root dir entry count, and the small volume sector count. However all of
  111. ; these fields must be 0 on NTFS volumes.
  112. ;
  113. ; We use this space to store some temporary variables used by the boot code,
  114. ; which avoids the need for separate space in sector 0 to store them.
  115. ; We also take advantage of the free 0-initialization to save some space
  116. ; by avoiding the code to initialize them.
  117. ;
  118. ; Note that ideally we'd want to use an unused field for the SectorCount
  119. ; and initialize it to 16. This would let us save a few bytes by avoiding
  120. ; code to explicitly initialize this value before we read the 16 boot sectors.
  121. ; However setup and other code tends to preserve the entire bpb area when
  122. ; it updates boot code, so we avoid a dependency here and initialize
  123. ; the value explicitly to 16 in the first part of the boot code.
  124. ;
  125. SectorCount dw 0 ; number of sectors to read
  126. SectorBase dd 0 ; start sector for read request
  127. HaveXInt13 db 0 ; extended int13 available flag
  128. Media db 0f8h ; Media byte
  129. FatSectors dw 0 ; (always 0 on NTFS)
  130. SectorsPerTrack dw 0 ; Sectors per track
  131. Heads dw 0 ; Number of surfaces
  132. HiddenSectors dd 0 ; partition start LBA
  133. ;
  134. ; The field below is traditionally the large sector count and is
  135. ; always 0 on NTFS. We use it here for a value the boot code calculates,
  136. ; namely the number of sectors visible on the drive via conventional int13.
  137. ;
  138. Int13Sectors dd 0
  139. DriveNumber db 80h ; int13 unit number
  140. db 3 dup (?) ; alignment filler
  141. ;
  142. ; The following is the rest of the NTFS Sector Zero information.
  143. ; The offsets of most of these fields cannot be changed without changing
  144. ; all code that validates, formats, recognizes, etc, NTFS volumes.
  145. ; In other words, don't change it.
  146. ;
  147. SectorsOnVolume db (size LARGE_INTEGER) dup (?)
  148. MftStartLcn db (size LARGE_INTEGER) dup (?)
  149. Mft2StartLcn db (size LARGE_INTEGER) dup (?)
  150. ClustersPerFrs dd ?
  151. DefClustersPerBuf dd ?
  152. SerialNumber db (size LARGE_INTEGER) dup (?)
  153. CheckSum dd ?
  154. ;
  155. ; Make sure size of fields matches what fs_rec.sys thinks is should be
  156. ;
  157. .errnz ($-_ntfsboot) NE (54h)
  158. ;****************************************************************************
  159. start:
  160. ;
  161. ; First of all, set up the segments we need (stack and data).
  162. ;
  163. cli
  164. xor ax,ax ; Set up the stack to just before
  165. mov ss,ax ; this code. It'll be moved after
  166. mov sp,7c00h ; we relocate.
  167. sti
  168. IFDEF NEC_98
  169. push si ; for after use
  170. mov ax, cs ; Address BPB with DS.
  171. else
  172. mov ax,Bootseg ; Address BPB with DS.
  173. endif
  174. mov ds,ax
  175. assume ds:BootCode
  176. IFDEF NEC_98
  177. ; Boot is from hard disk.
  178. ; Our BPB tells us BytesPerSector, but it is a logical value
  179. ; that may differ from the physical value.
  180. ; We need SectorShiftFactor that is used to convert logical sector address
  181. ; to phisical sector address.
  182. .386
  183. xor eax, eax ;
  184. .286
  185. mov es, ax ;
  186. mov al, es:[DAUA] ;
  187. mov DriveNumber, al ; Save DriveNumber
  188. IFDEF NEC_98
  189. ;
  190. ; we assume physical sector length is 512.
  191. ; so SectorShiftFactor is 1
  192. ;
  193. else
  194. mov ah, 84h ; ROM BIOS sense command
  195. xor bx, bx ; initialize bx to 0
  196. int 1Bh ;
  197. jc Flg01
  198. test bx, bx ; physical sector length returned ?
  199. jnz Flg02 ; if not,
  200. Flg01:
  201. mov bx, 512 ; we assume 512 bytes per sector
  202. Flg02:
  203. mov ax, BytesPerSector ; BytesPerSector
  204. xor dx, dx
  205. div bx
  206. mov SectorShiftFactor,ax
  207. endif
  208. ;
  209. ; We need to calculate length of hidden sector.
  210. ;
  211. mov HiddenSectors.lsw, 0
  212. mov HiddenSectors.msw, 0
  213. mov SectorBase.lsw, 0 ; read Partition control area
  214. mov SectorBase.msw, 0
  215. mov word ptr [SectorCount], 2 ; Maximum pattern
  216. mov ax, NewSeg ; read it at NewSeg.
  217. mov es, ax ; >> take a room
  218. .386
  219. xor ebx, ebx ; at NewSeg:0000.
  220. .286
  221. call DoReadLL ; Call low-level DoRead routine
  222. .386
  223. mov ax, Heads ;
  224. mov bx, es:[si].StartingCylinder ;
  225. mul ebx ; cylinder * head = total_track
  226. mov bx, SectorsPerTrack ;
  227. mul ebx ; (edx:eax = total_track) * (ebx=sec/trk)
  228. mov HiddenSectors, eax ; = total_sec
  229. .286
  230. else
  231. call Int13SecCnt ; determine range of regular int13
  232. endif
  233. ;
  234. ; Read bootcode and jump to code in sector 1.
  235. ; Assumes HaveXInt13 and SectorBase are initialized to 0,
  236. ; which they are since they are stored in areas of the BPB
  237. ; that must be 0 on NTFS volumes.
  238. ;
  239. IFDEF NEC_98
  240. mov SectorBase.lsw, 0 ; read sector zero.
  241. mov SectorBase.msw, 0
  242. ; xor dx, dx
  243. ; mov ax, 2000h
  244. ; div BytesPerSector
  245. ; mov byte ptr SectorCount,ax ; word field is 0 on-disk so byte init is OK
  246. mov byte ptr SectorCount,10h ; word field is 0 on-disk so byte init is OK
  247. mov ax,NewSeg
  248. mov es,ax
  249. xor bx,bx ; es:bx = transfer address
  250. call DoReadLL ; Call low-level DoRead routine
  251. else
  252. mov ax,NewSeg
  253. mov es,ax
  254. xor bx,bx ; es:bx = transfer address
  255. mov byte ptr SectorCount,16 ; word field is 0 on-disk so byte init is OK
  256. call ReadSectors
  257. endif
  258. IFDEF NEC_98
  259. mov cl, DriveNumber ; Copy DriveNumber
  260. mov bx, HiddenSectors.lsw ;
  261. mov dx, HiddenSectors.msw ; bx:ax = HiddenSectors
  262. pop si ; Partition information
  263. endif
  264. push NewSeg
  265. push offset mainboot
  266. retf
  267. IFDEF NEC_98
  268. else
  269. ;
  270. ; Determine the number of sectors addressable via
  271. ; conventional int13. If we can't get drive params for some reason
  272. ; then something is very wrong -- we'll try to force the caller
  273. ; to use conventional int13 by maxing out the sector count.
  274. ;
  275. ; Input: ds addresses start of first sector
  276. ;
  277. ; Output: in eax; also stored in Int13Sectors variable.
  278. ;
  279. ; Preserves: assume none.
  280. ;
  281. Int13SecCnt proc near
  282. mov dl,DriveNumber ; int13 unit number
  283. mov ah,8 ; get drive params
  284. int 13h ; call BIOS
  285. jnc @f ; no error, procede
  286. mov cx,-1 ; strange case, fake registers to force
  287. mov dh,cl ; use of standard int13 (set all vals to max)
  288. @@:
  289. .386
  290. movzx eax,dh ; eax = max head # (0-255)
  291. inc ax ; eax = heads (1-256)
  292. movzx edx,cl ; edx = sectors per track + cyl bits
  293. and dl,3fh ; edx = sectors per track (1-63)
  294. mul dx ; eax = sectors per cylinder, edx = 0
  295. xchg cl,ch
  296. shr ch,6 ; cx = max cylinder # (0-1023)
  297. inc cx ; cx = cylinders (1-1024)
  298. movzx ecx,cx ; ecx = cylinders (1-1024)
  299. mul ecx ; eax = sectors visible via int13, edx = 0
  300. mov Int13Sectors,eax ; save # sectors addressable via int13
  301. .286
  302. ret
  303. Int13SecCnt endp
  304. ;
  305. ; Determine whether extended int13 services are available on the boot drive.
  306. ;
  307. ; Stores result (boolean) in HaveXInt13 variable.
  308. ;
  309. ; Preserves: assume none.
  310. ;
  311. HaveInt13Ext proc near
  312. mov ah,41h
  313. mov bx,055aah
  314. mov dl,DriveNumber
  315. int 13h
  316. jc @f ; error from int13 means no xint13
  317. cmp bx,0aa55h ; absence of sig means no xint13
  318. jne @f
  319. test cl,1 ; bit 0 off means no xint13
  320. jz @f
  321. inc byte ptr HaveXInt13 ; xint13 is available
  322. @@: ret
  323. HaveInt13Ext endp
  324. endif
  325. IFDEF NEC_98
  326. ;*******************************************************************************
  327. ;
  328. ; Low-level read routine that doesn't work across a 64k addr boundary.
  329. ;
  330. ; Read SectorCount sectors (starting at SectorBase) to es:bx.
  331. ;
  332. ; As a side effect, SectorBase is updated (but es:bx are not)
  333. ; and SectorCount is reduced to zero.
  334. ;
  335. DoReadLL proc
  336. pusha
  337. push es
  338. mov si, SectorCount
  339. mov bp, bx
  340. mov cx, SectorBase.lsw ; Starting sector
  341. mov dx, SectorBase.msw ; Starting sector
  342. ; (dx:cx) = logical sector # relative to partition
  343. ; (si) = # of sectors to read
  344. ; (es:bp) = read buffer
  345. Rpt03:
  346. push dx
  347. push cx
  348. ; [sp] = logical sector number LSW
  349. ; [sp+2] = logical sector number MSW
  350. ; We are reading hard disk.
  351. ; We don't care about DMA boundary
  352. ; as length we read is 64KB max.
  353. ;
  354. ; push dx
  355. ; xor dx, dx
  356. ; mov ax, 32768 ; sectors we can read (32KB)
  357. ; div BytesPerSector ; this is not optimum on performance
  358. ; pop dx
  359. mov ax, 64 ; ax = 32768(32KB)/512(BytePerSector)
  360. ;
  361. ; (ax) = # of sectors we can read with 1 time ROM call
  362. ; (si) = # of sectors we need to read
  363. ;
  364. cmp si, ax
  365. jnb @F
  366. mov ax, si
  367. @@:
  368. push ax ; # of sectors we are reading this call
  369. push dx
  370. mul BytesPerSector ; SECSIZE
  371. pop dx
  372. push ax ; # of bytes.
  373. ; [sp] = # of bytes to read
  374. ; [sp+2] = # of sectors
  375. ; [sp+4] = logical sector number LSW
  376. ; [sp+6] = logical sector number MSW
  377. mov bx, ax ; # of bytes we read
  378. ; mov ax, SectorShiftFactor
  379. ;
  380. ;Rpt04: ; logical sector address to
  381. ; shr ax, 1
  382. ; jc @F
  383. ;
  384. ; shl cx, 1 ; physical sector address
  385. ; rcl dx, 1
  386. ; jmp short Rpt04
  387. ;@@:
  388. Flg00:
  389. add cx, HiddenSectors.lsw
  390. adc dx, HiddenSectors.msw ; (dx:cx) -> relative to drive
  391. mov al, DriveNumber
  392. and al, 7Fh ; drive #
  393. ; We have prepared following ROM BIOS parameters
  394. ; (al) = drive
  395. ; (dx:cx) = relative sector number
  396. ; (es:bp) -> buffer
  397. ;
  398. mov ah, 01010110b ; read
  399. int 1Bh
  400. pop di ; # of bytes
  401. pop bx ; # of sectors
  402. pop cx ; lsn (LSW)
  403. pop dx ; lsn (MSW)
  404. jc @F ; return Carry
  405. ; prepare for next call
  406. add cx, bx ; advance lsn
  407. adc dx, 0 ;
  408. add bp, di ; advance offset
  409. sub si, bx ; decrement # of sectors
  410. jnz Rpt03 ; CarryFlag is Clear
  411. @@:
  412. mov SectorCount, si
  413. mov SectorBase.lsw, cx ; Next Starting sector
  414. mov SectorBase.msw, dx ; Next Starting sector
  415. pop es
  416. popa
  417. ret
  418. DoReadLL endp
  419. endif
  420. IFDEF NEC_98
  421. ;SectorShiftFactor dw ?
  422. endif
  423. ;
  424. ; Read SectorCount sectors starting at logical sector SectorBase,
  425. ; into es:bx, using extended int13 if necessary.
  426. ;
  427. ; Preserves all
  428. ;
  429. ReadSectors proc near
  430. IFDEF NEC_98
  431. ;****************************************************************************
  432. ;
  433. ; ReadSectors - read SectorCount sectors into ES:BX starting from sector
  434. ; SectorBase.
  435. ;
  436. ; NOTE: This code WILL NOT WORK if ES:BX does not point to an address whose
  437. ; physical address (ES * 16 + BX) MOD 512 != 0.
  438. ;
  439. ; ReadSectors adds to ES rather than BX in the main loop so that runs longer than
  440. ; 64K can be read with a single call to ReadSectors.
  441. ;
  442. ; Note that ReadSectors (unlike DoReadLL) saves and restores SectorCount
  443. ; and SectorBase
  444. ;
  445. .286
  446. push ax ; save important registers
  447. push bx
  448. push cx
  449. push dx
  450. push es
  451. push SectorCount ; save state variables too
  452. push SectorBase.lsw
  453. push SectorBase.msw
  454. ;
  455. ; Calculate how much we can read into what's left of the current 64k
  456. ; physical address block, and read it.
  457. ;
  458. ;
  459. mov ax,bx
  460. shr ax,4
  461. mov cx,es
  462. add ax,cx ; ax = paragraph addr
  463. ;
  464. ; Now calc maximum number of paragraphs that we can read safely:
  465. ; 4k - ( ax mod 4k )
  466. ;
  467. and ax,0fffh
  468. sub ax,1000h
  469. neg ax
  470. ;
  471. ; Calc CX = number of paragraphs to be read
  472. ;
  473. push ax
  474. mov ax,SectorCount ; convert SectorCount to paragraph cnt
  475. mov cx,BytesPerSector
  476. shr cx,4
  477. mul cx
  478. mov cx,ax
  479. pop ax
  480. ReadSectors$Loop64:
  481. push cx ; save cpRead
  482. cmp ax,cx ; ax = min(cpReadSafely, cpRead)
  483. jbe @F
  484. mov ax,cx
  485. @@:
  486. push ax
  487. ;
  488. ; Calculate new SectorCount from amount we can read
  489. ;
  490. mov cx,BytesPerSector
  491. shr cx,4
  492. xor dx,dx
  493. div cx
  494. mov SectorCount,ax
  495. call DoReadLL
  496. pop ax ; ax = cpActuallyRead
  497. pop cx ; cx = cpRead
  498. sub cx,ax ; Any more to read?
  499. jbe ReadSectors$Exit64 ; Nope.
  500. ;
  501. ; Adjust ES:BX by amount read
  502. ;
  503. mov dx,es
  504. add dx,ax
  505. mov es,dx
  506. ;
  507. ; Since we're now reading on a 64k byte boundary, cpReadSafely == 4k.
  508. ;
  509. mov ax,01000h ; 16k paragraphs per 64k segment
  510. jmp short ReadSectors$Loop64 ; and go read some more.
  511. ReadSectors$Exit64:
  512. pop SectorBase.msw ; restore all this crap
  513. pop SectorBase.lsw
  514. pop SectorCount
  515. pop es
  516. pop dx
  517. pop cx
  518. pop bx
  519. pop ax
  520. ret
  521. else
  522. .386
  523. pushad ; save registers
  524. push ds
  525. push es
  526. read_loop:
  527. mov eax,SectorBase ; logical starting sector
  528. add eax,HiddenSectors ; eax = physical starting sector
  529. cmp eax,Int13Sectors ; determine if standard int13 is ok
  530. jb stdint13
  531. push ds ; preserve ds
  532. db 66h ; hand-coded 32-bit push of 8-bit immediate
  533. push 0 ; high 32 bits of sector #
  534. push eax ; low 32 bits of sector #
  535. push es
  536. push bx ; transfer address
  537. push dword ptr 10010h ; transfer 1 sector, packet size = 16
  538. cmp byte ptr HaveXInt13,0
  539. jne @f ; already know we have xint13 available
  540. call HaveInt13Ext ; see if we have it
  541. cmp byte ptr HaveXInt13,0
  542. je BootErr$he ; need it but don't have it
  543. @@: mov ah,42h ; extended read
  544. mov dl,DriveNumber ; dl = int13 unit #
  545. push ss
  546. pop ds
  547. mov si,sp ; ds:si -> param packet
  548. int 13h
  549. pop eax ; throw away first 4 bytes of param packet
  550. pop bx ; restore es:bx from param packet
  551. pop es
  552. pop eax ; throw away last 8 bytes of param packet
  553. pop eax ; without clobbering carry flag
  554. pop ds ; restore ds
  555. jmp short did_read
  556. stdint13:
  557. xor edx,edx ; edx:eax = absolute sector number
  558. movzx ecx,SectorsPerTrack ; ecx = sectors per track
  559. div ecx ; eax = track, edx = sector within track (0-62)
  560. inc dl ; dl = sector within track (1-63)
  561. mov cl,dl ; cl = sector within track
  562. mov edx,eax
  563. shr edx,16 ; dx:ax = track
  564. div Heads ; ax = cylinder (0-1023), dx = head (0-255)
  565. xchg dl,dh ; dh = head
  566. mov dl,DriveNumber ; dl = int13 unit #
  567. mov ch,al ; ch = bits 0-7 of cylinder
  568. shl ah,6
  569. or cl,ah ; bits 6-7 of cl = bits 8-9 of cylinder
  570. mov ax,201h ; read 1 sector
  571. int 13h
  572. did_read:
  573. jc BootErr$he
  574. read_next:
  575. mov ax,es ; advance transfer address
  576. add ax,20h ; by moving segment register along
  577. mov es,ax ; thus no 64K limit on transfer length
  578. inc SectorBase ; advance sector number
  579. dec SectorCount ; see if done
  580. jnz read_loop ; not done
  581. pop es
  582. pop ds
  583. popad ; restore registers
  584. .286
  585. ret
  586. endif
  587. ReadSectors endp
  588. BootErr$he:
  589. mov al,byte ptr TXT_MSG_SYSINIT_BOOT_ERROR
  590. IFDEF NEC_98
  591. BootErr2:
  592. mov ah, 1 ; offset = offseet + 256
  593. add ax, 2
  594. mov si, ax ; set string data
  595. ; mov ax, 0a00h ; initialize crt mode 80*25
  596. ; int 18h ;
  597. ; mov ah, 0ch ; start display
  598. ; int 18h ;
  599. call BootErr$print
  600. sti
  601. jmp $ ; Wait forever
  602. BootErr$print:
  603. mov ax, 0a000h ; set V-RAM
  604. mov es, ax
  605. xor ah, ah
  606. lodsb ; Get next character
  607. cmp al, 0 ; Is text end ?
  608. je BootErr$Done ;
  609. stosw ; move to vram
  610. jmp BootErr$print ;
  611. BootErr$Done:
  612. else
  613. BootErr2:
  614. call BootErr$print
  615. mov al,byte ptr TXT_MSG_SYSINIT_REBOOT
  616. call BootErr$print
  617. sti
  618. jmp $ ; Wait forever
  619. BootErr$print:
  620. ;
  621. ; al is offset - 256 of message. Adjust to form real offset
  622. ; and stick in si so lodsb below will work.
  623. ;
  624. mov ah,1
  625. mov si,ax
  626. BootErr$print1:
  627. lodsb ; Get next character
  628. cmp al,0
  629. je BootErr$Done
  630. mov ah,14 ; Write teletype
  631. mov bx,7 ; Attribute
  632. int 10h ; Print it
  633. jmp BootErr$print1
  634. BootErr$Done:
  635. endif
  636. ret
  637. ;****************************************************************************
  638. ;
  639. ; Message table.
  640. ;
  641. ; We put English messages here as a placeholder only, so that in case
  642. ; anyone uses bootntfs.h without patching new messages in, things will
  643. ; still be correct (in English, but at least functional).
  644. ;
  645. MSG_SYSINIT_BOOT_ERROR:
  646. DB 13,10,'A disk read error occurred',0
  647. MSG_SYSINIT_FILE_NOT_FD:
  648. DB 13,10,'NTLDR is missing',0
  649. ifdef NEC_98
  650. else
  651. MSG_SYSINIT_NTLDR_CMPRS:
  652. DB 13,10,'NTLDR is compressed',0
  653. MSG_SYSINIT_REBOOT:
  654. DB 13,10,'Press Ctrl+Alt+Del to restart',13,10,0
  655. endif
  656. ;
  657. ; Now build a table with the low byte of the offset to each message.
  658. ; Code that patches the boot sector messages updates this table.
  659. ;
  660. .errnz ($-_ntfsboot) GT (512-8)
  661. ORG 512 - 8
  662. TXT_MSG_SYSINIT_BOOT_ERROR:
  663. db OFFSET (MSG_SYSINIT_BOOT_ERROR - _ntfsboot) - 256
  664. TXT_MSG_SYSINIT_FILE_NOT_FD:
  665. db OFFSET (MSG_SYSINIT_FILE_NOT_FD - _ntfsboot) - 256
  666. ifdef NEC_98
  667. else
  668. TXT_MSG_SYSINIT_NTLDR_CMPRS:
  669. db OFFSET (MSG_SYSINIT_NTLDR_CMPRS - _ntfsboot) - 256
  670. TXT_MSG_SYSINIT_REBOOT:
  671. db OFFSET (MSG_SYSINIT_REBOOT - _ntfsboot) - 256
  672. endif
  673. ifdef NEC_98
  674. .errnz ($-_ntfsboot) GT (512-4)
  675. ORG 512 - 4
  676. endif
  677. .errnz ($-_ntfsboot) NE (512-4)
  678. db 0,0,55h,0aah
  679. ; Name we look for. ntldr_length is the number of characters,
  680. ; ntldr_name is the name itself. Note that it is not NULL
  681. ; terminated, and doesn't need to be.
  682. ;
  683. ntldr_name_length dw 5
  684. ntldr_name dw 'N', 'T', 'L', 'D', 'R'
  685. ; Predefined name for index-related attributes associated with an
  686. ; index over $FILE_NAME
  687. ;
  688. index_name_length dw 4
  689. index_name dw '$', 'I', '3', '0'
  690. ; Global variables. These offsets are all relative to NewSeg.
  691. ;
  692. AttrList dd 0e000h ; Offset of buffer to hold attribute list
  693. MftFrs dd 3000h ; Offset of general scratch buffer for FRS
  694. SegmentsInMft dd ? ; number of FRS's with MFT Data attribute records
  695. RootIndexFrs dd ? ; Offset of Root Index FRS
  696. AllocationIndexFrs dd ? ; Offset of Allocation Index FRS ; KPeery
  697. BitmapIndexFrs dd ? ; Offset of Bitmap Index FRS ; KPeery
  698. IndexRoot dd ? ; Offset of Root Index $INDEX_ROOT attribute
  699. IndexAllocation dd ? ; Offset of Root Index $INDEX_ALLOCATION attribute
  700. IndexBitmap dd ? ; Offset of Root Index $BITMAP attribute
  701. NtldrFrs dd ? ; Offset of NTLDR FRS
  702. NtldrData dd ? ; Offset of NTLDR $DATA attribute
  703. IndexBlockBuffer dd ? ; Offset of current index buffer
  704. IndexBitmapBuffer dd ? ; Offset of index bitmap buffer
  705. NextBuffer dd ? ; Offset of next free byte in buffer space
  706. BytesPerCluster dd ? ; Bytes per cluster
  707. BytesPerFrs dd ? ; Bytes per File Record Segment
  708. ;
  709. ; For floppyless booting, winnt32.exe creates c:\$win_nt$.~bt\bootsec.dat and
  710. ; places an entry in boot.ini for it (the boot selection says something
  711. ; like "Windows NT Setup or Upgrade"). When that is selected, the boot loader
  712. ; loads 16 sectors worth of data from bootsect.dat into d000 (which is where
  713. ; the first sector of this code would have loaded it) and jumps into it at
  714. ; a known location of 256h. That was correct in earlier versions of NT
  715. ; but is not correct now because the 4 fields below were added to this sector.
  716. ;
  717. ; Note that 0000 is "add [bx+si],al" which because of the way the boot loader
  718. ; is written happens to be a benign add of 0 to something in segment 7c0,
  719. ; which doesn't seem to hose anything but is still somewhat random.
  720. ;
  721. ; We code in a jump here so as this new code proliferates we get this
  722. ; cleaned up.
  723. ;
  724. .errnz $-_ntfsboot ne 256h
  725. SectorsPerFrs label dword ; Sectors per File Record Segment
  726. jmp short mainboot
  727. nop
  728. nop
  729. .errnz $-_ntfsboot ne 25ah
  730. MftLcnFrs dd ? ; Offset scratch FRS buffer for LookupMftLcn
  731. BytesPerIndexBlock dd ? ; Bytes per index alloc block in root index
  732. ClustersPerIndexBlock dd ? ; Clusters per index alloc block in root index
  733. SectorsPerIndexBlock dd ? ; Sectors per index block in root index
  734. .386
  735. SAVE_ALL macro
  736. push es
  737. push ds
  738. pushad
  739. endm
  740. RESTORE_ALL macro
  741. popad
  742. nop
  743. pop ds
  744. pop es
  745. endm
  746. ;****************************************************************************
  747. ;
  748. ; mainboot - entry point after 16 boot sectors have been read in
  749. ;
  750. ;
  751. mainboot proc far
  752. ; Get the new ds and the new stack. Note that ss is zero.
  753. ;
  754. mov ax, cs ; Set DS to CS
  755. mov ds, ax
  756. shl ax, 4 ; convert to an offset.
  757. cli
  758. mov sp, ax ; load new stack, just before boot code.
  759. sti
  760. IFDEF NEC_98
  761. ;
  762. ; cl = DA/UA Booted from
  763. ; dx:bx = HiddenSectors
  764. ; si = Partition information
  765. ;
  766. mov al, cl
  767. mov DriveNumber, al ; Save DriveNumber
  768. mov HiddenSectors.lsw, bx ;
  769. mov HiddenSectors.msw, dx ;
  770. push si ;
  771. ; Boot is from hard disk.
  772. ; Our BPB tells us BytesPerSector, but it is a logical value
  773. ; that may differ from the physical value.
  774. ; We need SectorShiftFactor that is used to convert logical sector address
  775. ; to phisical sector address.
  776. ifdef NEC_98
  777. ; mov SectorShiftFactor, 1
  778. else
  779. mov ah, 84h ; ROM BIOS sense command
  780. xor bx, bx ; initialize bx to 0
  781. int 1Bh ;
  782. jc Flg21
  783. test bx, bx ; physical sector length returned ?
  784. jnz Flg22 ; if not,
  785. Flg21:
  786. mov bx, 256 ; we assume 256 bytes per sector
  787. Flg22:
  788. mov ax, BytesPerSector ; BytesPerSector
  789. xor dx, dx
  790. div bx
  791. mov SectorShiftFactor,ax
  792. endif
  793. else
  794. ;
  795. ; Reinitialize xint13-related variables
  796. ;
  797. call Int13SecCnt ; determine range of regular int13
  798. endif
  799. ; Set up the FRS buffers. The MFT buffer is in a fixed
  800. ; location, and the other three come right after it. The
  801. ; buffer for index allocation blocks comes after that.
  802. ;
  803. ; Compute the useful constants associated with the volume
  804. ;
  805. movzx eax, BytesPerSector ; eax = Bytes per Sector
  806. movzx ebx, SectorsPerCluster ; ebx = Sectors Per Cluster
  807. mul ebx ; eax = Bytes per Cluster
  808. mov BytesPerCluster, eax
  809. mov ecx, ClustersPerFrs ; ecx = clusters per frs
  810. cmp cl, 0 ; is ClustersPerFrs less than zero?
  811. jg mainboot$1
  812. ; If the ClustersPerFrs field is negative, we calculate the number
  813. ; of bytes per FRS by negating the value and using that as a shif count.
  814. ;
  815. neg cl
  816. mov eax, 1
  817. shl eax, cl ; eax = bytes per frs
  818. jmp mainboot$2
  819. mainboot$1:
  820. ; Otherwise if ClustersPerFrs was positive, we multiply by bytes
  821. ; per cluster.
  822. mov eax, BytesPerCluster
  823. mul ecx ; eax = bytes per frs
  824. mainboot$2:
  825. mov BytesPerFrs, eax
  826. movzx ebx, BytesPerSector
  827. xor edx, edx ; zero high part of dividend
  828. div ebx ; eax = sectors per frs
  829. mov SectorsPerFrs, eax
  830. ; Set up the MFT FRS's---this will read all the $DATA attribute
  831. ; records for the MFT.
  832. ;
  833. call SetupMft
  834. ; Set up the remaining FRS buffers. The RootIndex FRS comes
  835. ; directly after the last MFT FRS, followed by the NTLdr FRS
  836. ; and the Index Block buffer.
  837. ;
  838. mov ecx, NextBuffer
  839. mov RootIndexFrs, ecx
  840. add ecx, BytesPerFrs ; AllocationFrs may be different
  841. mov AllocationIndexFrs, ecx ; from RootIndexFrs - KPeery
  842. add ecx, BytesPerFrs ; BitmapFrs may be different
  843. mov BitmapIndexFrs, ecx ; from RootIndexFrs - KPeery
  844. add ecx, BytesPerFrs
  845. mov NtldrFrs, ecx
  846. add ecx, BytesPerFrs
  847. mov IndexBlockBuffer, ecx
  848. ;
  849. ; Read the root index, allocation index and bitmap FRS's and locate
  850. ; the interesting attributes.
  851. ;
  852. mov eax, $INDEX_ROOT
  853. mov ecx, RootIndexFrs
  854. call LoadIndexFrs
  855. or eax, eax
  856. jz BootErr$he
  857. mov IndexRoot, eax ; offset in Frs buffer
  858. mov eax, $INDEX_ALLOCATION ; Attribute type code
  859. mov ecx, AllocationIndexFrs ; FRS to search
  860. call LoadIndexFrs
  861. mov IndexAllocation, eax
  862. mov eax, $BITMAP ; Attribute type code
  863. mov ecx, BitmapIndexFrs ; FRS to search
  864. call LoadIndexFrs
  865. mov IndexBitmap, eax
  866. ; Consistency check: the index root must exist, and it
  867. ; must be resident.
  868. ;
  869. mov eax, IndexRoot
  870. or eax, eax
  871. jz BootErr$he
  872. cmp [eax].ATTR_FormCode, RESIDENT_FORM
  873. jne BootErr$he
  874. ; Determine the size of the index allocation buffer based
  875. ; on information in the $INDEX_ROOT attribute. The index
  876. ; bitmap buffer comes immediately after the index block buffer.
  877. ;
  878. ; eax -> $INDEX_ROOT attribute record
  879. ;
  880. lea edx, [eax].ATTR_FormUnion ; edx -> resident info
  881. add ax, [edx].RES_ValueOffset ; eax -> value of $INDEX_ROOT
  882. movzx ecx, [eax].IR_ClustersPerBuffer
  883. mov ClustersPerIndexBlock, ecx
  884. mov ecx, [eax].IR_BytesPerBuffer
  885. mov BytesPerIndexBlock, ecx
  886. mov eax, BytesPerIndexBlock
  887. movzx ecx, BytesPerSector
  888. xor edx, edx
  889. div ecx ; eax = sectors per index block
  890. mov SectorsPerIndexBlock, eax
  891. mov eax, IndexBlockBuffer
  892. add eax, BytesPerIndexBlock
  893. mov IndexBitmapBuffer, eax
  894. ; Next consistency check: if the $INDEX_ALLOCATION attribute
  895. ; exists, the $INDEX_BITMAP attribute must also exist.
  896. ;
  897. cmp IndexAllocation, 0
  898. je mainboot30
  899. cmp IndexBitmap, 0 ; since IndexAllocation exists, the
  900. je BootErr$he ; bitmap must exist, too.
  901. ; Since the bitmap exists, we need to read it into the bitmap
  902. ; buffer. If it's resident, we can just copy the data.
  903. ;
  904. mov ebx, IndexBitmap ; ebx -> index bitmap attribute
  905. push ds
  906. pop es
  907. mov edi, IndexBitmapBuffer ; es:edi -> index bitmap buffer
  908. call ReadWholeAttribute
  909. mainboot30:
  910. ;
  911. ; OK, we've got the index-related attributes.
  912. ;
  913. movzx ecx, ntldr_name_length ; ecx = name length in characters
  914. mov eax, offset ntldr_name ; eax -> name
  915. call FindFile
  916. or eax, eax
  917. jz BootErr$fnf
  918. ; Read the FRS for NTLDR and find its data attribute.
  919. ;
  920. ; eax -> Index Entry for NTLDR.
  921. ;
  922. mov eax, [eax].IE_FileReference.REF_LowPart
  923. push ds
  924. pop es ; es:edi = target buffer
  925. mov edi, NtldrFrs
  926. call ReadFrs
  927. mov eax, NtldrFrs ; pointer to FRS
  928. mov ebx, $DATA ; requested attribute type
  929. mov ecx, 0 ; attribute name length in characters
  930. mov edx, 0 ; attribute name (NULL if none)
  931. call LocateAttributeRecord
  932. ; eax -> $DATA attribute for NTLDR
  933. ;
  934. or eax, eax ; if eax is zero, attribute not found.
  935. jnz mainboot$FoundData
  936. ;
  937. ; The ntldr $DATA segment is fragmented. Search the attribute list
  938. ; for the $DATA member. And load it from there.
  939. ;
  940. mov ecx, $DATA ; Attribute type code
  941. mov eax, NtldrFrs ; FRS to search
  942. call SearchAttrList ; search attribute list for FRN
  943. ; of specified ($DATA)
  944. or eax, eax ; if eax is zero, attribute not found.
  945. jz BootErr$fnf
  946. ;
  947. ; We found the FRN of the $DATA attribute; load that into memory.
  948. ;
  949. push ds
  950. pop es ; es:edi = target buffer
  951. mov edi, NtldrFrs
  952. call ReadFrs
  953. ;
  954. ; Determine the beginning offset of the $DATA in the FRS
  955. ;
  956. mov eax, NtldrFrs ; pointer to FRS
  957. mov ebx, $DATA ; requested attribute type
  958. mov ecx, 0 ; attribute name length in characters
  959. mov edx, 0 ; attribute name (NULL if none)
  960. call LocateAttributeRecord
  961. ; eax -> $DATA attribute for NTLDR
  962. ;
  963. or eax, eax ; if eax is zero, attribute not found.
  964. jz BootErr$fnf
  965. mainboot$FoundData:
  966. ; Get the attribute record header flags, and make sure none of the
  967. ; `compressed' bits are set
  968. movzx ebx, [eax].ATTR_Flags
  969. and ebx, ATTRIBUTE_FLAG_COMPRESSION_MASK
  970. jnz BootErr$ntc
  971. mov ebx, eax ; ebx -> $DATA attribute for NTLDR
  972. push LdrSeg
  973. pop es ; es = segment addres to read into
  974. sub edi, edi ; es:edi = buffer address
  975. call ReadWholeAttribute
  976. ;
  977. ; We've loaded NTLDR--jump to it.
  978. ;
  979. ; Before we go to NTLDR, set up the registers the way it wants them:
  980. ; DL = INT 13 drive number we booted from
  981. ;
  982. mov dl, DriveNumber
  983. mov ax,1000
  984. mov es, ax ; we don't really need this
  985. lea si, BPB
  986. sub ax,ax
  987. IFDEF NEC_98
  988. pop bp
  989. endif
  990. push LdrSeg
  991. push ax
  992. retf ; "return" to NTLDR.
  993. mainboot endp
  994. .386
  995. ;****************************************************************************
  996. ;
  997. ; ReadClusters - Reads a run of clusters from the disk.
  998. ;
  999. ; ENTRY: eax == LCN to read
  1000. ; edx == clusters to read
  1001. ; es:edi -> Target buffer
  1002. ;
  1003. ; USES: none (preserves all registers)
  1004. ;
  1005. ReadClusters proc near
  1006. SAVE_ALL
  1007. mov ebx, edx ; ebx = clusters to read.
  1008. movzx ecx, SectorsPerCluster ; ecx = cluster factor
  1009. mul ecx ; Convert LCN to sectors (wipes out edx!)
  1010. mov SectorBase, eax ; Store starting sector in SectorBase
  1011. mov eax, ebx ; eax = number of clusters
  1012. mul ecx ; Convert EAX to sectors (wipes out edx!)
  1013. mov SectorCount, ax ; Store number of sectors in SectorCount
  1014. ;
  1015. ; Note that ReadClusters gets its target buffer in es:edi but calls
  1016. ; the ReadSectors worker function that takes a target in es:bx--we need
  1017. ; to normalize es:edi so that we don't overflow bx.
  1018. ;
  1019. mov bx, di
  1020. and bx, 0Fh
  1021. mov ax, es
  1022. shr edi, 4
  1023. add ax, di ; ax:bx -> target buffer
  1024. push ax
  1025. pop es ; es:bx -> target buffer
  1026. call ReadSectors
  1027. RESTORE_ALL
  1028. ret
  1029. ReadClusters endp
  1030. ;
  1031. ;****************************************************************************
  1032. ;
  1033. ; LocateAttributeRecord -- Find an attribute record in an FRS.
  1034. ;
  1035. ; ENTRY: EAX -- pointer to FRS
  1036. ; EBX -- desired attribute type code
  1037. ; ECX -- length of attribute name in characters
  1038. ; EDX -- pointer to attribute name
  1039. ;
  1040. ; EXIT: EAX points at attribute record (0 indicates not found)
  1041. ;
  1042. ; USES: All
  1043. ;
  1044. LocateAttributeRecord proc near
  1045. ; get the first attribute record.
  1046. ;
  1047. add ax, word ptr[eax].FRS_FirstAttribute
  1048. ; eax -> next attribute record to investigate.
  1049. ; ebx == desired type
  1050. ; ecx == name length
  1051. ; edx -> pointer to name
  1052. ;
  1053. lar10:
  1054. cmp [eax].ATTR_TypeCode, 0ffffffffh
  1055. je lar99
  1056. cmp dword ptr[eax].ATTR_TypeCode, ebx
  1057. jne lar80
  1058. ; this record is a potential match. Compare the names:
  1059. ;
  1060. ; eax -> candidate record
  1061. ; ebx == desired type
  1062. ; ecx == name length
  1063. ; edx -> pointer to name
  1064. ;
  1065. or ecx, ecx ; Did the caller pass in a name length?
  1066. jnz lar20
  1067. ; We want an attribute with no name--the current record is
  1068. ; a match if and only if it has no name.
  1069. ;
  1070. cmp [eax].ATTR_NameLength, 0
  1071. jne lar80 ; Not a match.
  1072. ; It's a match, and eax is set up correctly, so return.
  1073. ;
  1074. ret
  1075. ; We want a named attribute.
  1076. ;
  1077. ; eax -> candidate record
  1078. ; ebx == desired type
  1079. ; ecx == name length
  1080. ; edx -> pointer to name
  1081. ;
  1082. lar20:
  1083. cmp cl, [eax].ATTR_NameLength
  1084. jne lar80 ; Not a match.
  1085. ; Convert name in current record to uppercase.
  1086. ;
  1087. mov esi, eax
  1088. add si, word ptr[eax].ATTR_NameOffset
  1089. call UpcaseName
  1090. ; eax -> candidate record
  1091. ; ebx == desired type
  1092. ; ecx == name length
  1093. ; edx -> pointer to name
  1094. ; esi -> Name in current record (upcased)
  1095. ;
  1096. push ecx ; save cx
  1097. push ds ; Copy data segment into es
  1098. pop es
  1099. mov edi, edx ; note that esi is already set up.
  1100. repe cmpsw ; zero flag is set if equal
  1101. pop ecx ; restore cx
  1102. jnz lar80 ; not a match
  1103. ; eax points at a matching record.
  1104. ;
  1105. ret
  1106. ;
  1107. ; This record doesn't match; go on to the next.
  1108. ;
  1109. ; eax -> rejected candidate attribute record
  1110. ; ebx == desired type
  1111. ; ecx == Name length
  1112. ; edx -> desired name
  1113. ;
  1114. lar80: cmp [eax].ATTR_RecordLength, 0 ; if the record length is zero
  1115. je lar99 ; the FRS is corrupt.
  1116. add eax, [eax].ATTR_RecordLength; Go to next record
  1117. jmp lar10 ; and try again
  1118. ; Didn't find it.
  1119. ;
  1120. lar99: sub eax, eax
  1121. ret
  1122. LocateAttributeRecord endp
  1123. ;****************************************************************************
  1124. ;
  1125. ; LocateIndexEntry -- Find an index entry in a file name index
  1126. ;
  1127. ; ENTRY: EAX -> pointer to index header
  1128. ; EBX -> file name to find
  1129. ; ECX == length of file name in characters
  1130. ;
  1131. ; EXIT: EAX points at index entry. NULL to indicate failure.
  1132. ;
  1133. ; USES: All
  1134. ;
  1135. LocateIndexEntry proc near
  1136. ; Convert the input name to upper-case
  1137. ;
  1138. mov esi, ebx
  1139. call UpcaseName
  1140. ifdef DEBUG
  1141. call PrintName
  1142. call Debug2
  1143. endif ; DEBUG
  1144. add eax, [eax].IH_FirstIndexEntry
  1145. ; EAX -> current entry
  1146. ; EBX -> file name to find
  1147. ; ECX == length of file name in characters
  1148. ;
  1149. lie10: test [eax].IE_Flags, INDEX_ENTRY_END ; Is it the end entry?
  1150. jnz lie99
  1151. lea edx, [eax].IE_Value ; edx -> FILE_NAME attribute value
  1152. ifdef DEBUG
  1153. ; DEBUG CODE -- list file names as they are examined
  1154. SAVE_ALL
  1155. call Debug3
  1156. movzx ecx, [edx].FN_FileNameLength ; ecx = chars in name
  1157. lea esi, [edx].FN_FileName ; esi -> name
  1158. call PrintName
  1159. RESTORE_ALL
  1160. endif ; DEBUG
  1161. ; EAX -> current entry
  1162. ; EBX -> file name to find
  1163. ; ECX == length of file name in characters
  1164. ; EDX -> FILE_NAME attribute
  1165. cmp cl, [edx].FN_FileNameLength ; Is name the right length?
  1166. jne lie80
  1167. lea esi, [edx].FN_FileName ; Get name from FILE_NAME structure
  1168. call UpcaseName
  1169. push ecx ; save ecx
  1170. push ds
  1171. pop es ; copy data segment into es for cmpsw
  1172. mov edi, ebx ; edi->search name (esi already set up)
  1173. repe cmpsw ; zero flag is set if they're equal
  1174. pop ecx ; restore ecx
  1175. jnz lie80
  1176. ; the current entry matches the search name, and eax points at it.
  1177. ;
  1178. ret
  1179. ; The current entry is not a match--get the next one.
  1180. ; EAX -> current entry
  1181. ; EBX -> file name to find
  1182. ; ECX == length of file name in characters
  1183. ;
  1184. lie80: cmp [eax].IE_Length, 0 ; If the entry length is zero
  1185. je lie99 ; then the index block is corrupt.
  1186. add ax, [eax].IE_Length ; Get the next entry.
  1187. jmp lie10
  1188. ; Name not found in this block. Set eax to zero and return
  1189. ;
  1190. lie99: xor eax, eax
  1191. ret
  1192. LocateIndexEntry endp
  1193. ;****************************************************************************
  1194. ;
  1195. ; ReadWholeAttribute - Read an entire attribute value
  1196. ;
  1197. ; ENTRY: ebx -> attribute
  1198. ; es:edi -> target buffer
  1199. ;
  1200. ; USES: ALL
  1201. ;
  1202. ReadWholeAttribute proc near
  1203. cmp [ebx].ATTR_FormCode, RESIDENT_FORM
  1204. jne rwa10
  1205. ; The attribute is resident.
  1206. ; ebx -> attribute
  1207. ; es:edi -> target buffer
  1208. ;
  1209. SAVE_ALL
  1210. lea edx, [ebx].ATTR_FormUnion ; edx -> resident form info
  1211. mov ecx, [edx].RES_ValueLength ; ecx = bytes in value
  1212. mov esi, ebx ; esi -> attribute
  1213. add si, [edx].RES_ValueOffset ; esi -> attribute value
  1214. rep movsb ; copy bytes from value to buffer
  1215. RESTORE_ALL
  1216. ret ; That's all!
  1217. rwa10:
  1218. ;
  1219. ; The attribute type is non-resident. Just call
  1220. ; ReadNonresidentAttribute starting at VCN 0 and
  1221. ; asking for the whole thing.
  1222. ;
  1223. ; ebx -> attribute
  1224. ; es:edi -> target buffer
  1225. ;
  1226. lea edx, [ebx].ATTR_FormUnion ; edx -> nonresident form info
  1227. mov ecx, [edx].NONRES_HighestVcn.LowPart; ecx = HighestVcn
  1228. inc ecx ; ecx = clusters in attribute
  1229. sub eax, eax ; eax = 0 (first VCN to read)
  1230. call ReadNonresidentAttribute
  1231. ret
  1232. ReadWholeAttribute endp
  1233. ;****************************************************************************
  1234. ;
  1235. ; ReadNonresidentAttribute - Read clusters from a nonresident attribute
  1236. ;
  1237. ; ENTRY: EAX == First VCN to read
  1238. ; EBX -> Attribute
  1239. ; ECX == Number of clusters to read
  1240. ; ES:EDI == Target of read
  1241. ;
  1242. ; EXIT: None.
  1243. ;
  1244. ; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1245. ;
  1246. ReadNonresidentAttribute proc near
  1247. SAVE_ALL
  1248. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  1249. je ReadNR10
  1250. ; This attribute is not resident--the disk is corrupt.
  1251. jmp BootErr$he
  1252. ReadNR10:
  1253. ; eax == Next VCN to read
  1254. ; ebx -> Attribute
  1255. ; ecx -> Remaining clusters to read
  1256. ; es:edi -> Target of read
  1257. ;
  1258. cmp ecx, 0
  1259. jne ReadNR20
  1260. ; Nothing left to read--return success.
  1261. ;
  1262. RESTORE_ALL
  1263. ret
  1264. ReadNR20:
  1265. push ebx ; pointer to attribute
  1266. push eax ; Current VCN
  1267. push ecx
  1268. push edi
  1269. push es
  1270. call ComputeLcn ; eax = LCN to read, ecx = run length
  1271. mov edx, ecx ; edx = remaining run length
  1272. pop es
  1273. pop edi
  1274. pop ecx
  1275. ; eax == LCN to read
  1276. ; ecx == remaining clusters to read
  1277. ; edx == remaining clusters in current run
  1278. ; es:edi == Target of read
  1279. ; TOS == Current VCN
  1280. ; TOS + 4 == pointer to attribute
  1281. ;
  1282. cmp ecx, edx
  1283. jge ReadNR30
  1284. ; Run length is greater than remaining request; only read
  1285. ; remaining request.
  1286. ;
  1287. mov edx, ecx ; edx = Remaining request
  1288. ReadNR30:
  1289. ; eax == LCN to read
  1290. ; ecx == remaining clusters to read
  1291. ; edx == clusters to read in current run
  1292. ; es:edi == Target of read
  1293. ; TOS == Current VCN
  1294. ; TOS + == pointer to attribute
  1295. ;
  1296. call ReadClusters
  1297. sub ecx, edx ; Decrement clusters remaining in request
  1298. mov ebx, edx ; ebx = clusters read
  1299. mov eax, edx ; eax = clusters read
  1300. movzx edx, SectorsPerCluster
  1301. mul edx ; eax = sectors read (wipes out edx!)
  1302. movzx edx, BytesPerSector
  1303. mul edx ; eax = bytes read (wipes out edx!)
  1304. add edi, eax ; Update target of read
  1305. pop eax ; eax = previous VCN
  1306. add eax, ebx ; update VCN to read
  1307. pop ebx ; ebx -> attribute
  1308. jmp ReadNR10
  1309. ReadNonresidentAttribute endp
  1310. ;****************************************************************************
  1311. ;
  1312. ; ReadIndexBlockSectors - Read sectors from an index allocation attribute
  1313. ;
  1314. ; ENTRY: EAX == First VBN to read
  1315. ; EBX -> Attribute
  1316. ; ECX == Number of sectors to read
  1317. ; ES:EDI == Target of read
  1318. ;
  1319. ; EXIT: None.
  1320. ;
  1321. ; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1322. ;
  1323. ReadIndexBlockSectors proc near
  1324. SAVE_ALL
  1325. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  1326. je ReadIBS_10
  1327. ; This attribute is resident--the disk is corrupt.
  1328. jmp BootErr$he
  1329. ReadIBS_10:
  1330. ; eax == Next VBN to read
  1331. ; ebx -> Attribute
  1332. ; ecx -> Remaining sectors to read
  1333. ; es:edi -> Target of read
  1334. ;
  1335. cmp ecx, 0
  1336. jne ReadIBS_20
  1337. ; Nothing left to read--return success.
  1338. ;
  1339. RESTORE_ALL
  1340. ret
  1341. ReadIBS_20:
  1342. push ebx ; pointer to attribute
  1343. push eax ; Current VBN
  1344. push ecx
  1345. push edi
  1346. push es
  1347. ; Convert eax from a VBN back to a VCN by dividing by SectorsPerCluster.
  1348. ; The remainder of this division is the sector offset in the cluster we
  1349. ; want. Then use the mapping information to get the LCN for this VCN,
  1350. ; then multiply to get back to LBN.
  1351. ;
  1352. push ecx ; save remaining sectors in request
  1353. xor edx, edx ; zero high part of dividend
  1354. movzx ecx, SectorsPerCluster
  1355. div ecx ; edx = remainder
  1356. push edx ; save remainder
  1357. call ComputeLcn ; eax = LCN to read, ecx = remaining run length
  1358. movzx ebx, SectorsPerCluster
  1359. mul ebx ; eax = LBN of cluster, edx = 0
  1360. pop edx ; edx = remainder
  1361. add eax, edx ; eax = LBN we want
  1362. push eax ; save LBN
  1363. movzx eax, SectorsPerCluster
  1364. mul ecx ; eax = remaining run length in sectors, edx = 0
  1365. mov edx, eax ; edx = remaining run length
  1366. pop eax ; eax = LBN
  1367. pop ecx ; ecx = remaining sectors in request
  1368. pop es
  1369. pop edi
  1370. pop ecx
  1371. ; eax == LBN to read
  1372. ; ecx == remaining sectors to read
  1373. ; edx == remaining sectors in current run
  1374. ; es:edi == Target of read
  1375. ; TOS == Current VCN
  1376. ; TOS + 4 == pointer to attribute
  1377. ;
  1378. cmp ecx, edx
  1379. jge ReadIBS_30
  1380. ; Run length is greater than remaining request; only read
  1381. ; remaining request.
  1382. ;
  1383. mov edx, ecx ; edx = Remaining request
  1384. ReadIBS_30:
  1385. ; eax == LBN to read
  1386. ; ecx == remaining sectors to read
  1387. ; edx == sectors to read in current run
  1388. ; es:edi == Target of read
  1389. ; TOS == Current VCN
  1390. ; TOS + == pointer to attribute
  1391. ;
  1392. mov SectorBase, eax
  1393. mov SectorCount, dx
  1394. ; We have a pointer to the target buffer in es:edi, but we want that
  1395. ; in es:bx for ReadSectors.
  1396. ;
  1397. SAVE_ALL
  1398. mov bx, di
  1399. and bx, 0Fh
  1400. mov ax, es
  1401. shr edi, 4
  1402. add ax, di ; ax:bx -> target buffer
  1403. push ax
  1404. pop es ; es:bx -> target buffer
  1405. call ReadSectors
  1406. RESTORE_ALL
  1407. sub ecx, edx ; Decrement sectors remaining in request
  1408. mov ebx, edx ; ebx = sectors read
  1409. mov eax, edx ; eax = sectors read
  1410. movzx edx, BytesPerSector
  1411. mul edx ; eax = bytes read (wipes out edx!)
  1412. add edi, eax ; Update target of read
  1413. pop eax ; eax = previous VBN
  1414. add eax, ebx ; update VBN to read
  1415. pop ebx ; ebx -> attribute
  1416. jmp ReadIBS_10
  1417. ReadIndexBlockSectors endp
  1418. ;****************************************************************************
  1419. ;
  1420. ; MultiSectorFixup - fixup a structure read off the disk
  1421. ; to reflect Update Sequence Array.
  1422. ;
  1423. ; ENTRY: ES:EDI = Target buffer
  1424. ;
  1425. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1426. ;
  1427. ; Note: ES:EDI must point at a structure which is protected
  1428. ; by an update sequence array, and which begins with
  1429. ; a multi-sector-header structure.
  1430. ;
  1431. MultiSectorFixup proc near
  1432. SAVE_ALL
  1433. movzx ebx, es:[edi].MSH_UpdateArrayOfs ; ebx = update array offset
  1434. movzx ecx, es:[edi].MSH_UpdateArraySize ; ecx = update array size
  1435. or ecx, ecx ; if the size of the update sequence array
  1436. jz BootErr$he ; is zero, this structure is corrupt.
  1437. add ebx, edi ; es:ebx -> update sequence array count word
  1438. add ebx, 2 ; es:ebx -> 1st entry of update array
  1439. add edi, SEQUENCE_NUMBER_STRIDE - 2 ; es:edi->last word of first chunk
  1440. dec ecx ; decrement to reflect count word
  1441. MSF10:
  1442. ; ecx = number of entries remaining in update sequence array
  1443. ; es:ebx -> next entry in update sequence array
  1444. ; es:edi -> next target word for update sequence array
  1445. or ecx, ecx
  1446. jz MSF30
  1447. mov ax, word ptr es:[ebx] ; copy next update sequence array entry
  1448. mov word ptr es:[edi], ax ; to next target word
  1449. add ebx, 2 ; go on to next entry
  1450. add edi, SEQUENCE_NUMBER_STRIDE ; go on to next target
  1451. dec ecx
  1452. jmp MSF10
  1453. MSF30:
  1454. RESTORE_ALL
  1455. ret
  1456. MultiSectorFixup endp
  1457. ;****************************************************************************
  1458. ;
  1459. ; SetupMft - Reads MFT File Record Segments into the LBN array
  1460. ;
  1461. ; ENTRY: none.
  1462. ;
  1463. ; EXIT: NextBuffer is set to the free byte after the last MFT FRS
  1464. ; SegmentsInMft is initialized
  1465. ;
  1466. ;
  1467. SetupMft proc near
  1468. SAVE_ALL
  1469. ; Initialize SegmentsInMft and NextBuffer as if the MFT
  1470. ; had only one FRS.
  1471. ;
  1472. mov eax, 1
  1473. mov SegmentsInMft, eax
  1474. mov eax, MftFrs ; this is the scratch mft buffer
  1475. add eax, BytesPerFrs
  1476. mov MftLcnFrs,eax ; this is the scratch mft buffer for lookup
  1477. add eax, BytesPerFrs
  1478. mov NextBuffer, eax
  1479. ; Read FRS 0 into the first MFT FRS buffer, being sure
  1480. ; to resolve the Update Sequence Array. Remember the physical
  1481. ; location in the Lbn array.
  1482. ;
  1483. mov eax, MftStartLcn.LowPart
  1484. movzx ebx, SectorsPerCluster
  1485. mul ebx ; eax = mft starting sector
  1486. mov ebx, NextBuffer ; Store this location in the Lbn array
  1487. mov [bx], eax
  1488. mov SectorBase, eax ; SectorBase = mft starting sector for read
  1489. add bx, 4
  1490. mov eax, SectorsPerFrs
  1491. mov [bx], eax ; Store the sector count in the Lcn array
  1492. mov SectorCount, ax ; SectorCount = SectorsPerFrs
  1493. add bx, 4
  1494. mov NextBuffer, ebx ; Remember the next Lbn array location
  1495. mov ebx, MftFrs ; Read the sectors into the MftFrs scratch buffer
  1496. push ds
  1497. pop es
  1498. call ReadSectors
  1499. mov edi, ebx ; es:edi = buffer
  1500. call MultiSectorFixup
  1501. ; Determine whether the MFT has an Attribute List attribute
  1502. mov eax, MftFrs
  1503. mov ebx, $ATTRIBUTE_LIST
  1504. mov ecx, 0
  1505. mov edx, 0
  1506. call LocateAttributeRecord
  1507. or eax, eax ; If there's no Attribute list,
  1508. jz SetupMft99 ; we're done!
  1509. ; Read the attribute list.
  1510. ; eax -> attribute list attribute
  1511. ;
  1512. mov ebx, eax ; ebx -> attribute list attribute
  1513. push ds
  1514. pop es ; copy ds into es
  1515. mov edi, AttrList ; ds:edi->attribute list buffer
  1516. call ReadWholeAttribute
  1517. mov ebx, AttrList ; ebx -> first attribute list entry
  1518. ; Now, traverse the attribute list looking for the first
  1519. ; entry for the $DATA type. We know it must have at least
  1520. ; one.
  1521. ;
  1522. ; ebx -> first attribute list entry
  1523. ;
  1524. SetupMft10:
  1525. cmp [bx].ATTRLIST_TypeCode, $DATA
  1526. je SetupMft30
  1527. add bx,[bx].ATTRLIST_Length
  1528. jmp SetupMft10
  1529. SetupMft20:
  1530. ; Scan forward through the attribute list entries for the
  1531. ; $DATA attribute, reading each referenced FRS. Note that
  1532. ; there will be at least one non-$DATA entry after the entries
  1533. ; for the $DATA attribute, since there's a $BITMAP.
  1534. ;
  1535. ; ebx -> Next attribute list entry
  1536. ; NextBuffer -> Target for next mapping information
  1537. ; MftFrs -> Target of next read
  1538. ; SegmentsInMft == number of MFT segments read so far
  1539. ;
  1540. ; Find the physical sector and sector count for the runs for this
  1541. ; file record (max 2 runs). The mapping for this must already
  1542. ; be in a file record already visited. Find the Vcn and cluster
  1543. ; offset for this FRS. Use LookupMftLcn to find the Lcn.
  1544. push ebx ; Save the current position in the attribute list
  1545. ; Convert from Frs to sectors, then to Vcn
  1546. mov eax, [bx].ATTRLIST_SegmentReference.REF_LowPart
  1547. mul SectorsPerFrs
  1548. push eax ; Remember the VBN
  1549. xor edx, edx
  1550. movzx ebx, SectorsPerCluster
  1551. div ebx ; eax = VCN
  1552. push edx ; save remainder, this is cluster offset
  1553. call ComputeMftLcn ; eax = LCN
  1554. or eax, eax ; LCN equal to zero?
  1555. jz BootErr$he ; zero is not a possible LCN
  1556. mov ecx, SectorsPerFrs ; ecx = Number of sectors remaining for this file record
  1557. ; Change the LCN back into an LBN and add the remainder back in to get
  1558. ; the sector we want to read.
  1559. movzx ebx, SectorsPerCluster
  1560. mul ebx ; eax = cluster first LBN
  1561. pop edx ; edx = sector remainder
  1562. add eax, edx ; eax = desired LBN
  1563. ; Store this in the current Lcn array slot
  1564. mov ebx, NextBuffer
  1565. mov [bx], eax ; Store the starting sector
  1566. add bx, 4
  1567. movzx eax, SectorsPerCluster
  1568. sub eax, edx
  1569. cmp eax, ecx ; Check if we have too many sectors
  1570. jbe SetupMft60
  1571. mov eax, ecx ; Limit ourselves to the sectors remaining
  1572. SetupMft60:
  1573. mov [bx], eax ; Store the sector count
  1574. ; If we have a complete file record skip to process the attribute entry
  1575. SetupMft70:
  1576. sub ecx, eax ; Subtract these sectors from remaining sectors
  1577. pop edx ; Get the previous starting VBN (restores stack also)
  1578. jz SetupMft50
  1579. ; This may be a split file record. Go ahead and get the next piece.
  1580. add eax, edx ; Add the sector count for the last run to the start Vbn for the run
  1581. ; This is the next Vbn to read
  1582. push eax ; Save the Vbn
  1583. xor edx, edx ; Convert to Vcn, there should be no remainder this time
  1584. movzx ebx, SectorsPerCluster
  1585. div ebx ; eax = VCN
  1586. push ecx ; Save the remaining sectors
  1587. call ComputeMftLcn ; eax = LCN
  1588. pop ecx ; Restore the remaining sectors
  1589. or eax, eax ; LCN equal to zero?
  1590. jz BootErr$he ; zero is not a possible LCN
  1591. ; Change the LCN back into a LBN to get the starting sector we want to read.
  1592. movzx ebx, SectorsPerCluster
  1593. mul ebx ; eax = cluster first LBN
  1594. ; If this sector is the contiguous with the other half of the run
  1595. ; make it appear to be single longer run.
  1596. mov ebx, NextBuffer ; Recover the last run
  1597. mov edx, [bx]
  1598. add bx, 4
  1599. add edx, [bx] ; This is the next potential LBN
  1600. cmp edx, eax ; Check if we are at the contiguous LBN
  1601. jne SetupMft80
  1602. ; Append this to the previous run.
  1603. movzx eax, SectorsPerCluster
  1604. cmp eax, ecx ; Check if have more sectors than we need
  1605. jbe SetupMft90
  1606. mov eax, ecx
  1607. SetupMft90:
  1608. add [bx], eax
  1609. jmp SetupMft70 ; Loop to see if there more work to do
  1610. ; This is multiple runs. Update the next entry.
  1611. SetupMft80:
  1612. add bx, 4
  1613. mov NextBuffer, ebx ; advance our NextBuffer pointer
  1614. mov [bx], eax ; fill in the next run start sector
  1615. add bx, 4
  1616. movzx eax, SectorsPerCluster
  1617. cmp eax, ecx ; Check if have more sectors than we need
  1618. jbe SetupMft100
  1619. mov eax, ecx
  1620. SetupMft100:
  1621. mov [bx], eax ; and count
  1622. jmp SetupMft70 ; Loop to see if there is more work to do
  1623. SetupMft50:
  1624. ; Advance the count of Frs segments and the NextBuffer pointer
  1625. add bx, 4
  1626. inc SegmentsInMft
  1627. mov NextBuffer, ebx
  1628. pop ebx
  1629. ; Go on to the next attribute list entry
  1630. SetupMft30:
  1631. add bx,[bx].ATTRLIST_Length
  1632. cmp [bx].ATTRLIST_TypeCode, $DATA
  1633. je SetupMft20
  1634. SetupMft99:
  1635. RESTORE_ALL
  1636. ret
  1637. SetupMft endp
  1638. ;****************************************************************************
  1639. ;
  1640. ; ComputeMftLcn -- Computes the LCN for a cluster of the MFT
  1641. ;
  1642. ;
  1643. ; ENTRY: EAX == VCN
  1644. ;
  1645. ; EXIT: EAX == LCN
  1646. ;
  1647. ; USES: ALL
  1648. ;
  1649. ComputeMftLcn proc near
  1650. mov edx, eax ; edx = VCN
  1651. mov ecx, SegmentsInMft ; ecx = # of FRS's to search
  1652. mov esi,MftLcnFrs
  1653. add esi,BytesPerFrs ; si -> FRS LBN list
  1654. MftLcn10:
  1655. ; ECX == number of remaining FRS's to search
  1656. ; EDX == VCN
  1657. ; EBX == Buffer to read into
  1658. ; ESI == LBN array
  1659. ; EDI == Number of sectors to read
  1660. ;
  1661. push edx ; save VCN
  1662. push ecx ; save MFT segment count
  1663. push edx ; save VCN again
  1664. ; Read the sectors for the given FRS
  1665. mov ebx,MftLcnFrs
  1666. mov edi,SectorsPerFrs
  1667. ; Read these sectors
  1668. MftLcn40:
  1669. mov eax,[si] ; Get the start sector and sector count
  1670. mov SectorBase,eax
  1671. add si,4
  1672. mov eax,[si]
  1673. mov SectorCount,ax
  1674. add si,4
  1675. push ds
  1676. pop es
  1677. call ReadSectors
  1678. ; Check if we have more data to read
  1679. sub edi, eax
  1680. je MftLcn30
  1681. ; Read the next run
  1682. mul BytesPerSector ; move forward in the buffer, results in ax:dx
  1683. add bx,ax
  1684. jmp MftLcn40
  1685. MftLcn30:
  1686. ; Do the multi sector fixup
  1687. mov edi,MftLcnFrs
  1688. push ds
  1689. pop es
  1690. call MultiSectorFixup
  1691. mov eax, MftLcnFrs
  1692. mov ebx, $DATA
  1693. mov ecx, 0
  1694. mov edx, ecx
  1695. call LocateAttributeRecord
  1696. ; EAX -> $DATA attribute
  1697. ; TOS == VCN
  1698. ; TOS + 4 == number of remaining FRS's to search
  1699. ; TOS + 8 -> FRS being searched
  1700. ; TOS +12 == VCN
  1701. or eax, eax
  1702. jz BootErr$he ; No $DATA attribute in this FRS!
  1703. mov ebx, eax ; ebx -> attribute
  1704. pop eax ; eax = VCN
  1705. ; EAX == VCN
  1706. ; EBX -> $DATA attribute
  1707. ; TOS number of remaining FRS's to search
  1708. ; TOS + 4 == FRS being searched
  1709. ; TOS + 8 == VCN
  1710. push esi
  1711. call ComputeLcn
  1712. pop esi
  1713. or eax, eax
  1714. jz MftLcn20
  1715. ; Found our LCN. Clean up the stack and return.
  1716. ;
  1717. ; EAX == LCN
  1718. ; TOS number of remaining FRS's to search
  1719. ; TOS + 4 == FRS being searched
  1720. ; TOS + 8 == VCN
  1721. ;
  1722. pop ebx
  1723. pop ebx ; clean up the stack
  1724. ret
  1725. MftLcn20:
  1726. ;
  1727. ; Didn't find the VCN in this FRS; try the next one.
  1728. ;
  1729. ; TOS number of remaining FRS's to search
  1730. ; TOS + 4 -> FRS being searched
  1731. ; TOS + 8 == VCN
  1732. ;
  1733. pop ecx ; ecx = number of FRS's remaining, including current
  1734. pop edx ; edx = VCN
  1735. loop MftLcn10 ; decrement cx and try next FRS
  1736. ; This VCN was not found.
  1737. ;
  1738. xor eax, eax
  1739. ret
  1740. ComputeMftLcn endp
  1741. ;****************************************************************************
  1742. ;
  1743. ; ReadMftSectors - Read sectors from the MFT
  1744. ;
  1745. ; ENTRY: EAX == starting VBN
  1746. ; ECX == number of sectors to read
  1747. ; ES:EDI == Target buffer
  1748. ;
  1749. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1750. ;
  1751. ReadMftSectors proc near
  1752. SAVE_ALL
  1753. RMS$Again:
  1754. push eax ; save starting VBN
  1755. push ecx ; save sector count
  1756. ; Divide the VBN by SectorsPerCluster to get the VCN
  1757. xor edx, edx ; zero high part of dividend
  1758. movzx ebx, SectorsPerCluster
  1759. div ebx ; eax = VCN
  1760. push edx ; save remainder
  1761. push edi ; save the target buffer
  1762. call ComputeMftLcn ; eax = LCN
  1763. pop edi ; recover the buffer
  1764. or eax, eax ; LCN equal to zero?
  1765. jz BootErr$he ; zero is not a possible LCN
  1766. ; Change the LCN back into a LBN and add the remainder back in to get
  1767. ; the sector we want to read, which goes into SectorBase.
  1768. ;
  1769. movzx ebx, SectorsPerCluster
  1770. mul ebx ; eax = cluster first LBN
  1771. pop edx ; edx = sector remainder
  1772. add eax, edx ; eax = desired LBN
  1773. mov SectorBase, eax
  1774. ;
  1775. ; Figure out how many sectors to read this time; we never attempt
  1776. ; to read more than one cluster at a time.
  1777. ;
  1778. pop ecx ; ecx = sectors to read
  1779. movzx ebx, SectorsPerCluster
  1780. cmp ecx,ebx
  1781. jle RMS10
  1782. ;
  1783. ; Read only a single cluster at a time, to avoid problems with fragmented
  1784. ; runs in the mft.
  1785. ;
  1786. mov SectorCount, bx ; this time read 1 cluster
  1787. sub ecx, ebx ; ecx = sectors remaining to read
  1788. pop eax ; eax = VBN
  1789. add eax, ebx ; VBN += sectors this read
  1790. push eax ; save next VBN
  1791. push ecx ; save remaining sector count
  1792. jmp RMS20
  1793. RMS10:
  1794. pop eax ; eax = VBN
  1795. add eax, ecx ; VBN += sectors this read
  1796. push eax ; save next VBN
  1797. mov SectorCount, cx
  1798. mov ecx, 0
  1799. push ecx ; save remaining sector count (0)
  1800. RMS20:
  1801. ; The target buffer was passed in es:edi, but we want it in es:bx.
  1802. ; Do the conversion.
  1803. ;
  1804. push es ; save buffer pointer
  1805. push edi
  1806. mov bx, di
  1807. and bx, 0Fh
  1808. mov ax, es
  1809. shr edi, 4
  1810. add ax, di ; ax:bx -> target buffer
  1811. push ax
  1812. pop es ; es:bx -> target buffer
  1813. call ReadSectors
  1814. pop edi ; restore buffer pointer
  1815. pop es
  1816. add edi, BytesPerCluster ; increment buf ptr by one cluster
  1817. pop ecx ; restore remaining sector count
  1818. pop eax ; restore starting VBN
  1819. cmp ecx, 0 ; are we done?
  1820. jg RMS$Again ; repeat until desired == 0
  1821. RESTORE_ALL
  1822. ret
  1823. ReadMftSectors endp
  1824. ;****************************************************************************
  1825. ;
  1826. ; ReadFrs - Read an FRS
  1827. ;
  1828. ; ENTRY: EAX == FRS number
  1829. ; ES:EDI == Target buffer
  1830. ;
  1831. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1832. ;
  1833. ReadFrs proc near
  1834. SAVE_ALL
  1835. mul SectorsPerFrs ; eax = sector number in MFT DATA attribute
  1836. ; (note that mul wipes out edx!)
  1837. mov ecx, SectorsPerFrs ; number of sectors to read
  1838. call ReadMftSectors
  1839. call MultiSectorFixup
  1840. RESTORE_ALL
  1841. ret
  1842. ReadFrs endp
  1843. ;****************************************************************************
  1844. ;
  1845. ; ReadIndexBlock - read an index block from the root index.
  1846. ;
  1847. ; ENTRY: EAX == Block number
  1848. ;
  1849. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1850. ;
  1851. ReadIndexBlock proc near
  1852. SAVE_ALL
  1853. mul SectorsPerIndexBlock ; eax = first VBN to read
  1854. ; (note that mul wipes out edx!)
  1855. mov ebx, IndexAllocation ; ebx -> $INDEX_ALLOCATION attribute
  1856. mov ecx, SectorsPerIndexBlock ; ecx == Sectors to read
  1857. push ds
  1858. pop es
  1859. mov edi, IndexBlockBuffer ; es:edi -> index block buffer
  1860. call ReadIndexBlockSectors
  1861. call MultiSectorFixup
  1862. RESTORE_ALL
  1863. ret
  1864. ReadIndexBlock endp
  1865. ;****************************************************************************
  1866. ;
  1867. ; IsBlockInUse - Checks the index bitmap to see if an index
  1868. ; allocation block is in use.
  1869. ;
  1870. ; ENTRY: EAX == block number
  1871. ;
  1872. ; EXIT: Carry flag clear if block is in use
  1873. ; Carry flag set if block is not in use.
  1874. ;
  1875. IsBlockInUse proc near
  1876. push eax
  1877. push ebx
  1878. push ecx
  1879. mov ebx, IndexBitmapBuffer
  1880. mov ecx, eax ; ecx = block number
  1881. shr eax, 3 ; eax = byte number
  1882. and ecx, 7 ; ecx = bit number in byte
  1883. add ebx, eax ; ebx -> byte to test
  1884. mov eax, 1
  1885. shl eax, cl ; eax = mask
  1886. test byte ptr[ebx], al
  1887. jz IBU10
  1888. clc ; Block is not in use.
  1889. jmp IBU20
  1890. IBU10: stc ; Block is in use.
  1891. IBU20:
  1892. pop ecx
  1893. pop ebx
  1894. pop eax ; restore registers
  1895. ret
  1896. IsBlockInUse endp
  1897. ;****************************************************************************
  1898. ;
  1899. ; ComputeLcn - Converts a VCN into an LCN
  1900. ;
  1901. ; ENTRY: EAX -> VCN
  1902. ; EBX -> Attribute
  1903. ;
  1904. ; EXIT: EAX -> LCN (zero indicates not found)
  1905. ; ECX -> Remaining run length
  1906. ;
  1907. ; USES: ALL.
  1908. ;
  1909. ComputeLcn proc near
  1910. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  1911. je clcn10
  1912. sub eax, eax ; This is a resident attribute.
  1913. ret
  1914. clcn10: lea esi, [ebx].ATTR_FormUnion ; esi -> nonresident info of attrib
  1915. ; eax -> VCN
  1916. ; ebx -> Attribute
  1917. ; esi -> Nonresident information of attribute record
  1918. ;
  1919. ; See if the desired VCN is in range.
  1920. mov edx, [esi].NONRES_HighestVcn.LowPart ; edx = HighestVcn
  1921. cmp eax, edx
  1922. ja clcn15 ; VCN is greater than HighestVcn
  1923. mov edx, [esi].NONRES_LowestVcn.LowPart ; edx = LowestVcn
  1924. cmp eax, edx
  1925. jae clcn20
  1926. clcn15:
  1927. sub eax, eax ; VCN is not in range
  1928. ret
  1929. clcn20:
  1930. ; eax -> VCN
  1931. ; ebx -> Attribute
  1932. ; esi -> Nonresident information of attribute record
  1933. ; edx -> LowestVcn
  1934. ;
  1935. add bx, [esi].NONRES_MappingPairOffset ; ebx -> mapping pairs
  1936. sub esi, esi ; esi = 0
  1937. clcn30:
  1938. ; eax == VCN to find
  1939. ; ebx -> Current mapping pair count byte
  1940. ; edx == Current VCN
  1941. ; esi == Current LCN
  1942. ;
  1943. cmp byte ptr[ebx], 0 ; if count byte is zero...
  1944. je clcn99 ; ... we're done (and didn't find it)
  1945. ; Update CurrentLcn
  1946. ;
  1947. call LcnFromMappingPair
  1948. add esi, ecx ; esi = current lcn for this mapping pair
  1949. call VcnFromMappingPair
  1950. ; eax == VCN to find
  1951. ; ebx -> Current mapping pair count byte
  1952. ; ecx == DeltaVcn for current mapping pair
  1953. ; edx == Current VCN
  1954. ; esi == Current LCN
  1955. ;
  1956. add ecx, edx ; ecx = NextVcn
  1957. cmp eax, ecx ; If target < NextVcn ...
  1958. jl clcn80 ; ... we found the right mapping pair.
  1959. ; Go on to next mapping pair.
  1960. ;
  1961. mov edx, ecx ; CurrentVcn = NextVcn
  1962. push eax
  1963. movzx ecx, byte ptr[ebx] ; ecx = count byte
  1964. mov eax, ecx ; eax = count byte
  1965. and eax, 0fh ; eax = number of vcn bytes
  1966. shr ecx, 4 ; ecx = number of lcn bytes
  1967. add ebx, ecx
  1968. add ebx, eax
  1969. inc ebx ; ebx -> next count byte
  1970. pop eax
  1971. jmp clcn30
  1972. clcn80:
  1973. ; We found the mapping pair we want.
  1974. ;
  1975. ; eax == target VCN
  1976. ; ebx -> mapping pair count byte
  1977. ; edx == Starting VCN of run
  1978. ; ecx == Next VCN (ie. start of next run)
  1979. ; esi == starting LCN of run
  1980. ;
  1981. sub ecx, eax ; ecx = remaining run length
  1982. sub eax, edx ; eax = offset into run
  1983. add eax, esi ; eax = LCN to return
  1984. ret
  1985. ; The target VCN is not in this attribute.
  1986. clcn99: sub eax, eax ; Not found.
  1987. ret
  1988. ComputeLcn endp
  1989. ;****************************************************************************
  1990. ;
  1991. ; VcnFromMappingPair
  1992. ;
  1993. ; ENTRY: EBX -> Mapping Pair count byte
  1994. ;
  1995. ; EXIT: ECX == DeltaVcn from mapping pair
  1996. ;
  1997. ; USES: ECX
  1998. ;
  1999. VcnFromMappingPair proc near
  2000. sub ecx, ecx ; ecx = 0
  2001. mov cl, byte ptr[ebx] ; ecx = count byte
  2002. and cl, 0fh ; ecx = v
  2003. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  2004. jne VFMP5
  2005. sub ecx, ecx
  2006. ret
  2007. VFMP5:
  2008. push ebx
  2009. push edx
  2010. add ebx, ecx ; ebx -> last byte of compressed vcn
  2011. movsx edx, byte ptr[ebx]
  2012. dec ecx
  2013. dec ebx
  2014. ; ebx -> Next byte to add in
  2015. ; ecx == Number of bytes remaining
  2016. ; edx == Accumulated value
  2017. ;
  2018. VFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  2019. je VFMP20
  2020. shl edx, 8
  2021. mov dl, byte ptr[ebx]
  2022. dec ebx ; Back up through bytes to process.
  2023. dec ecx ; One less byte to process.
  2024. jmp VFMP10
  2025. VFMP20:
  2026. ; edx == Accumulated value to return
  2027. mov ecx, edx
  2028. pop edx
  2029. pop ebx
  2030. ret
  2031. VcnFromMappingPair endp
  2032. ;****************************************************************************
  2033. ;
  2034. ; LcnFromMappingPair
  2035. ;
  2036. ; ENTRY: EBX -> Mapping Pair count byte
  2037. ;
  2038. ; EXIT: ECX == DeltaLcn from mapping pair
  2039. ;
  2040. ; USES: ECX
  2041. ;
  2042. LcnFromMappingPair proc near
  2043. push ebx
  2044. push edx
  2045. sub edx, edx ; edx = 0
  2046. mov dl, byte ptr[ebx] ; edx = count byte
  2047. and edx, 0fh ; edx = v
  2048. sub ecx, ecx ; ecx = 0
  2049. mov cl, byte ptr[ebx] ; ecx = count byte
  2050. shr cl, 4 ; ecx = l
  2051. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  2052. jne LFMP5
  2053. sub ecx, ecx
  2054. pop edx
  2055. pop ebx
  2056. ret
  2057. LFMP5:
  2058. ; ebx -> count byte
  2059. ; ecx == l
  2060. ; edx == v
  2061. ;
  2062. add ebx, edx ; ebx -> last byte of compressed vcn
  2063. add ebx, ecx ; ebx -> last byte of compressed lcn
  2064. movsx edx, byte ptr[ebx]
  2065. dec ecx
  2066. dec ebx
  2067. ; ebx -> Next byte to add in
  2068. ; ecx == Number of bytes remaining
  2069. ; edx == Accumulated value
  2070. ;
  2071. LFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  2072. je LFMP20
  2073. shl edx, 8
  2074. mov dl, byte ptr[ebx]
  2075. dec ebx ; Back up through bytes to process.
  2076. dec ecx ; One less byte to process.
  2077. jmp LFMP10
  2078. LFMP20:
  2079. ; edx == Accumulated value to return
  2080. mov ecx, edx
  2081. pop edx
  2082. pop ebx
  2083. ret
  2084. LcnFromMappingPair endp
  2085. ;****************************************************************************
  2086. ;
  2087. ; UpcaseName - Converts the name of the file to all upper-case
  2088. ;
  2089. ; ENTRY: ESI -> Name
  2090. ; ECX -> Length of name
  2091. ;
  2092. ; USES: none
  2093. ;
  2094. UpcaseName proc near
  2095. or ecx, ecx
  2096. jnz UN5
  2097. ret
  2098. UN5:
  2099. push ecx
  2100. push esi
  2101. UN10:
  2102. cmp word ptr[esi], 'a' ; if it's less than 'a'
  2103. jl UN20 ; leave it alone
  2104. cmp word ptr[esi], 'z' ; if it's greater than 'z'
  2105. jg UN20 ; leave it alone.
  2106. sub word ptr[esi], 'a'-'A' ; the letter is lower-case--convert it.
  2107. UN20:
  2108. add esi, 2 ; move on to next unicode character
  2109. loop UN10
  2110. pop esi
  2111. pop ecx
  2112. ret
  2113. UpcaseName endp
  2114. ;****************************************************************************
  2115. ;
  2116. ; FindFile - Locates the index entry for a file in the root index.
  2117. ;
  2118. ; ENTRY: EAX -> name to find
  2119. ; ECX == length of file name in characters
  2120. ;
  2121. ; EXIT: EAX -> Index Entry. NULL to indicate failure.
  2122. ;
  2123. ; USES: ALL
  2124. ;
  2125. FindFile proc near
  2126. push eax ; name address
  2127. push ecx ; name length
  2128. ; First, search the index root.
  2129. ;
  2130. ; eax -> name to find
  2131. ; ecx == name length
  2132. ; TOS == name length
  2133. ; TOS+4 -> name to find
  2134. ;
  2135. mov edx, eax ; edx -> name to find
  2136. mov eax, IndexRoot ; eax -> &INDEX_ROOT attribute
  2137. lea ebx, [eax].ATTR_FormUnion ; ebx -> resident info
  2138. add ax, [ebx].RES_ValueOffset ; eax -> Index Root value
  2139. lea eax, [eax].IR_IndexHeader ; eax -> Index Header
  2140. mov ebx, edx ; ebx -> name to find
  2141. call LocateIndexEntry
  2142. or eax, eax
  2143. jz FindFile20
  2144. ; Found it in the root! The result is already in eax.
  2145. ; Clean up the stack and return.
  2146. ;
  2147. pop ecx
  2148. pop ecx
  2149. ret
  2150. FindFile20:
  2151. ;
  2152. ; We didn't find the index entry we want in the root, so we have to
  2153. ; crawl through the index allocation buffers.
  2154. ;
  2155. ; TOS == name length
  2156. ; TOS+4 -> name to find
  2157. ;
  2158. mov eax, IndexAllocation
  2159. or eax, eax
  2160. jnz FindFile30
  2161. ; There is no index allocation attribute; clean up
  2162. ; the stack and return failure.
  2163. ;
  2164. pop ecx
  2165. pop ecx
  2166. xor eax, eax
  2167. ret
  2168. FindFile30:
  2169. ;
  2170. ; Search the index allocation blocks for the name we want.
  2171. ; Instead of searching in tree order, we'll just start with
  2172. ; the last one and work our way backwards.
  2173. ;
  2174. ; TOS == name length
  2175. ; TOS+4 -> name to find
  2176. ;
  2177. mov edx, IndexAllocation ; edx -> index allocation attr.
  2178. lea edx, [edx].ATTR_FormUnion ; edx -> nonresident form info
  2179. mov eax, [edx].NONRES_HighestVcn.LowPart; eax = HighestVcn
  2180. inc eax ; eax = clusters in attribute
  2181. mov ebx, BytesPerCluster
  2182. mul ebx ; eax = bytes in attribute
  2183. xor edx, edx
  2184. div BytesPerIndexBlock ; convert bytes to index blocks
  2185. push eax ; number of blocks to process
  2186. FindFile40:
  2187. ;
  2188. ; TOS == remaining index blocks to search
  2189. ; TOS + 4 == name length
  2190. ; TOS + 8 -> name to find
  2191. ;
  2192. pop eax ; eax == number of remaining blocks
  2193. or eax, eax
  2194. jz FindFile90
  2195. dec eax ; eax == number of next block to process
  2196. ; and number of remaining blocks
  2197. push eax
  2198. ; eax == block number to process
  2199. ; TOS == remaining index blocks to search
  2200. ; TOS + 4 == name length
  2201. ; TOS + 8 -> name to find
  2202. ;
  2203. ; See if the block is in use; if not, go on to next.
  2204. call IsBlockInUse
  2205. jc FindFile40 ; c set if not in use
  2206. ; eax == block number to process
  2207. ; TOS == remaining index blocks to search
  2208. ; TOS + 4 == name length
  2209. ; TOS + 8 -> name to find
  2210. ;
  2211. call ReadIndexBlock
  2212. pop edx ; edx == remaining buffers to search
  2213. pop ecx ; ecx == name length
  2214. pop ebx ; ebx -> name
  2215. push ebx
  2216. push ecx
  2217. push edx
  2218. ; ebx -> name to find
  2219. ; ecx == name length in characters
  2220. ; TOS == remaining blocks to process
  2221. ; TOS + 4 == name length
  2222. ; TOS + 8 -> name
  2223. ;
  2224. ; Index buffer to search is in index allocation block buffer.
  2225. ;
  2226. mov eax, IndexBlockBuffer ; eax -> Index allocation block
  2227. lea eax, [eax].IB_IndexHeader ; eax -> Index Header
  2228. call LocateIndexEntry ; eax -> found entry
  2229. or eax, eax
  2230. jz FindFile40
  2231. ; Found it!
  2232. ;
  2233. ; eax -> Found entry
  2234. ; TOS == remaining blocks to process
  2235. ; TOS + 4 == name length
  2236. ; TOS + 8 -> name
  2237. ;
  2238. pop ecx
  2239. pop ecx
  2240. pop ecx ; clean up stack
  2241. ret
  2242. FindFile90:
  2243. ;
  2244. ; Name not found.
  2245. ;
  2246. ; TOS == name length
  2247. ; TOS + 4 -> name to find
  2248. ;
  2249. pop ecx
  2250. pop ecx ; clean up stack.
  2251. xor eax, eax ; zero out eax.
  2252. ret
  2253. FindFile endp
  2254. ifdef DEBUG
  2255. ;****************************************************************************
  2256. ;
  2257. ; DumpIndexBlock - dumps the index block buffer
  2258. ;
  2259. DumpIndexBlock proc near
  2260. SAVE_ALL
  2261. mov esi, IndexBlockBuffer
  2262. mov ecx, 20h ; dwords to dump
  2263. DIB10:
  2264. test ecx, 3
  2265. jnz DIB20
  2266. call DebugNewLine
  2267. DIB20:
  2268. lodsd
  2269. call PrintNumber
  2270. loop DIB10
  2271. RESTORE_ALL
  2272. ret
  2273. DumpIndexBlock endp
  2274. ;****************************************************************************
  2275. ;
  2276. ; DebugNewLine
  2277. ;
  2278. DebugNewLine proc near
  2279. SAVE_ALL
  2280. xor eax, eax
  2281. xor ebx, ebx
  2282. mov al, 0dh
  2283. mov ah, 14
  2284. mov bx, 7
  2285. int 10h
  2286. mov al, 0ah
  2287. mov ah, 14
  2288. mov bx, 7
  2289. int 10h
  2290. RESTORE_ALL
  2291. ret
  2292. DebugNewLine endp
  2293. ;****************************************************************************
  2294. ;
  2295. ; PrintName - Display a unicode name
  2296. ;
  2297. ; ENTRY: DS:ESI -> null-terminated string
  2298. ; ECX == characters in string
  2299. ;
  2300. ; USES: None.
  2301. ;
  2302. PrintName proc near
  2303. IFDEF NEC_98
  2304. else
  2305. SAVE_ALL
  2306. or ecx, ecx
  2307. jnz PrintName10
  2308. call DebugNewLine
  2309. RESTORE_ALL
  2310. ret
  2311. PrintName10:
  2312. xor eax, eax
  2313. xor ebx, ebx
  2314. lodsw
  2315. mov ah, 14 ; write teletype
  2316. mov bx, 7 ; attribute
  2317. int 10h ; print it
  2318. loop PrintName10
  2319. call DebugNewLine
  2320. RESTORE_ALL
  2321. endif
  2322. ret
  2323. PrintName endp
  2324. ;****************************************************************************
  2325. ;
  2326. ; DebugPrint - Display a debug string.
  2327. ;
  2328. ; ENTRY: DS:SI -> null-terminated string
  2329. ;
  2330. ; USES: None.
  2331. ;
  2332. .286
  2333. DebugPrint proc near
  2334. pusha
  2335. DbgPr20:
  2336. lodsb
  2337. cmp al, 0
  2338. je DbgPr30
  2339. mov ah, 14 ; write teletype
  2340. mov bx, 7 ; attribute
  2341. int 10h ; print it
  2342. jmp DbgPr20
  2343. DbgPr30:
  2344. popa
  2345. nop
  2346. ret
  2347. DebugPrint endp
  2348. ;****************************************************************************
  2349. ;
  2350. ;
  2351. ; PrintNumber
  2352. ;
  2353. ; ENTRY: EAX == number to print
  2354. ;
  2355. ; PRESERVES ALL REGISTERS
  2356. ;
  2357. .386
  2358. PrintNumber proc near
  2359. IFDEF NEC_98
  2360. else
  2361. SAVE_ALL
  2362. mov ecx, 8 ; number of digits in a DWORD
  2363. PrintNumber10:
  2364. mov edx, eax
  2365. and edx, 0fh ; edx = lowest-order digit
  2366. push edx ; put it on the stack
  2367. shr eax, 4 ; drop low-order digit
  2368. loop PrintNumber10
  2369. mov ecx, 8 ; number of digits on stack.
  2370. PrintNumber20:
  2371. pop eax ; eax = next digit to print
  2372. cmp eax, 9
  2373. jg PrintNumber22
  2374. add eax, '0'
  2375. jmp PrintNumber25
  2376. PrintNumber22:
  2377. sub eax, 10
  2378. add eax, 'A'
  2379. PrintNumber25:
  2380. xor ebx, ebx
  2381. mov ah, 14
  2382. mov bx, 7
  2383. int 10h
  2384. loop PrintNumber20
  2385. ; Print a space to separate numbers
  2386. mov al, ' '
  2387. mov ah, 14
  2388. mov bx, 7
  2389. int 10h
  2390. RESTORE_ALL
  2391. call Pause
  2392. endif
  2393. ret
  2394. PrintNumber endp
  2395. IFDEF NEC_98
  2396. Debug0 proc near
  2397. Debug1:
  2398. Debug2:
  2399. Debug3:
  2400. Debug4:
  2401. ret
  2402. Debug0 endp
  2403. else
  2404. ;****************************************************************************
  2405. ;
  2406. ; Debug0 - Print debug string 0 -- used for checkpoints in mainboot
  2407. ;
  2408. Debug0 proc near
  2409. SAVE_ALL
  2410. mov si, offset DbgString0
  2411. call BootErr$Print1
  2412. RESTORE_ALL
  2413. ret
  2414. Debug0 endp
  2415. ;****************************************************************************
  2416. ;
  2417. ; Debug1 - Print debug string 1 --
  2418. ;
  2419. Debug1 proc near
  2420. SAVE_ALL
  2421. mov si, offset DbgString1
  2422. call BootErr$Print1
  2423. RESTORE_ALL
  2424. ret
  2425. Debug1 endp
  2426. ;****************************************************************************
  2427. ;
  2428. ; Debug2 - Print debug string 2
  2429. ;
  2430. Debug2 proc near
  2431. SAVE_ALL
  2432. mov si, offset DbgString2
  2433. call BootErr$Print1
  2434. RESTORE_ALL
  2435. ret
  2436. Debug2 endp
  2437. ;****************************************************************************
  2438. ;
  2439. ; Debug3 - Print debug string 3 --
  2440. ;
  2441. Debug3 proc near
  2442. SAVE_ALL
  2443. mov si, offset DbgString3
  2444. call BootErr$Print1
  2445. RESTORE_ALL
  2446. ret
  2447. Debug3 endp
  2448. ;****************************************************************************
  2449. ;
  2450. ; Debug4 - Print debug string 4
  2451. ;
  2452. Debug4 proc near
  2453. SAVE_ALL
  2454. mov si, offset DbgString4
  2455. call BootErr$Print1
  2456. RESTORE_ALL
  2457. ret
  2458. Debug4 endp
  2459. ;****************************************************************************
  2460. ;
  2461. ; Pause - Pause for about 1/2 a second. Simply count until you overlap
  2462. ; to zero.
  2463. ;
  2464. Pause proc near
  2465. push eax
  2466. mov eax, 0fff10000h
  2467. PauseLoopy:
  2468. inc eax
  2469. or eax, eax
  2470. jnz PauseLoopy
  2471. pop eax
  2472. ret
  2473. Pause endp
  2474. endif ; DEBUG
  2475. endif
  2476. ;*************************************************************************
  2477. ;
  2478. ; LoadIndexFrs - For the requested index type code locate and
  2479. ; load the associated Frs.
  2480. ;
  2481. ; ENTRY: EAX - requested index type code
  2482. ; ECX - Points to empty Frs buffer
  2483. ;
  2484. ; EXIT: EAX - points to offset in Frs buffer of requested index type
  2485. ; code or Zero if not found.
  2486. ; USES: All
  2487. ;
  2488. LoadIndexFrs proc near
  2489. push ecx ; save FRS buffer for later
  2490. push eax ; save index type code for later
  2491. mov eax, ROOT_FILE_NAME_INDEX_NUMBER
  2492. push ds
  2493. pop es
  2494. mov edi, ecx ; es:edi = target buffer
  2495. call ReadFrs
  2496. mov eax, ecx ; FRS to search
  2497. pop ebx ; Attribute type code
  2498. push ebx
  2499. movzx ecx, index_name_length ; Attribute name length
  2500. mov edx, offset index_name ; Attribute name
  2501. call LocateAttributeRecord
  2502. pop ebx
  2503. pop ecx
  2504. or eax, eax
  2505. jnz LoadIndexFrs$Exit ; if found in root return
  2506. ;
  2507. ; if not found in current Frs, search in attribute list
  2508. ;
  2509. ; EBX - holds Attribute type code
  2510. mov eax, ecx ; FRS to search
  2511. mov ecx, ebx ; type code
  2512. push eax ; save Frs
  2513. push ebx ; save type code
  2514. call SearchAttrList ; search attribute list for FRN
  2515. ; of specified ($INDEX_ROOT,
  2516. ; $INDEX_ALLOCATION, or $BITMAP)
  2517. ; EAX - holds FRN for Frs, or Zero
  2518. pop ebx ; Attribute type code (used later)
  2519. pop edi ; es:edi = target buffer
  2520. or eax, eax ; if we cann't find it in attribute
  2521. jz LoadIndexFrs$Exit ; list then we are hosed
  2522. ; We should now have the File Record Number where the index for the
  2523. ; specified type code we are searching for is, load this into the
  2524. ; Frs target buffer.
  2525. ;
  2526. ; EAX - holds FRN
  2527. ; EBX - holds type code
  2528. ; EDI - holds target buffer
  2529. push ds
  2530. pop es
  2531. call ReadFrs
  2532. ;
  2533. ; Now determine the offset in the Frs of the index
  2534. ;
  2535. ; EBX - holds type code
  2536. mov eax, edi ; Frs to search
  2537. movzx ecx, index_name_length ; Attribute name length
  2538. mov edx, offset index_name ; Attribute name
  2539. call LocateAttributeRecord
  2540. ; EAX - holds offset or Zero.
  2541. LoadIndexFrs$Exit:
  2542. ret
  2543. LoadIndexFrs endp
  2544. ;****************************************************************************
  2545. ;
  2546. ; SearchAttrList
  2547. ;
  2548. ; Search the Frs for the attribute list. Then search the attribute list
  2549. ; for the specifed type code. When you find it return the FRN in the
  2550. ; attribute list entry found or Zero if no match found.
  2551. ;
  2552. ; ENTRY: ECX - type code to search attrib list for
  2553. ; EAX - Frs buffer holding head of attribute list
  2554. ; EXIT: EAX - FRN file record number to load, Zero if none.
  2555. ;
  2556. ; USES: All
  2557. ;
  2558. SearchAttrList proc near
  2559. push ecx ; type code to search for in
  2560. ; attrib list
  2561. ; EAX - holds Frs to search
  2562. mov ebx, $ATTRIBUTE_LIST ; Attribute type code
  2563. mov ecx, 0 ; Attribute name length
  2564. mov edx, 0 ; Attribute name
  2565. call LocateAttributeRecord
  2566. or eax, eax ; If there's no Attribute list,
  2567. jz SearchAttrList$NotFoundIndex1 ; We are done
  2568. ; Read the attribute list.
  2569. ; eax -> attribute list attribute
  2570. mov ebx, eax ; ebx -> attribute list attribute
  2571. push ds
  2572. pop es ; copy ds into es
  2573. mov edi, AttrList ; ds:edi->attribute list buffer
  2574. call ReadWholeAttribute
  2575. push ds
  2576. pop es
  2577. mov ebx, AttrList ; es:ebx -> first attribute list entry
  2578. ; Now, traverse the attribute list looking for the entry for
  2579. ; the Index type code.
  2580. ;
  2581. ; ebx -> first attribute list entry
  2582. ;
  2583. pop ecx ; Get Index Type code
  2584. SearchAttrList$LookingForIndex:
  2585. ifdef DEBUG
  2586. SAVE_ALL
  2587. mov eax, es:[bx].ATTRLIST_TypeCode
  2588. call PrintNumber
  2589. movzx eax, es:[bx].ATTRLIST_Length
  2590. call PrintNumber
  2591. mov eax, es
  2592. call PrintNumber
  2593. mov eax, ebx
  2594. call PrintNumber
  2595. push es
  2596. pop ds
  2597. movzx ecx, es:[bx].ATTRLIST_NameLength ; ecx = chars in name
  2598. lea esi, es:[bx].ATTRLIST_Name ; esi -> name
  2599. call PrintName
  2600. RESTORE_ALL
  2601. endif ; DEBUG
  2602. cmp es:[bx].ATTRLIST_TypeCode, ecx
  2603. je SearchAttrList$FoundIndex
  2604. cmp es:[bx].ATTRLIST_TypeCode, $END ; reached invalid attribute
  2605. je SearchAttrList$NotFoundIndex2 ; so must be at end
  2606. cmp es:[bx].ATTRLIST_Length, 0
  2607. je SearchAttrList$NotFoundIndex2 ; reached end of list and
  2608. ; nothing found
  2609. movzx eax, es:[bx].ATTRLIST_Length
  2610. add bx, ax
  2611. mov ax, bx
  2612. and ax, 08000h ; test for roll over
  2613. jz SearchAttrList$LookingForIndex
  2614. ; If we rolled over then increment to the next es 32K segment and
  2615. ; zero off the high bits of bx
  2616. mov ax, es
  2617. add ax, 800h
  2618. mov es, ax
  2619. and bx, 07fffh
  2620. jmp SearchAttrList$LookingForIndex
  2621. SearchAttrList$FoundIndex:
  2622. ; found the index, return the FRN
  2623. mov eax, es:[bx].ATTRLIST_SegmentReference.REF_LowPart
  2624. ret
  2625. SearchAttrList$NotFoundIndex1:
  2626. pop ecx
  2627. SearchAttrList$NotFoundIndex2:
  2628. xor eax, eax
  2629. ret
  2630. SearchAttrList endp
  2631. ;
  2632. ; Boot message printing, relocated from sector 0 to sace space
  2633. ;
  2634. ifdef NEC_98
  2635. BootErr$ntc:
  2636. endif
  2637. BootErr$fnf:
  2638. mov al,byte ptr TXT_MSG_SYSINIT_FILE_NOT_FD
  2639. jmp BootErr2
  2640. ifdef NEC_98
  2641. else
  2642. BootErr$ntc:
  2643. mov al,byte ptr TXT_MSG_SYSINIT_NTLDR_CMPRS
  2644. jmp BootErr2
  2645. endif
  2646. ifdef DEBUG
  2647. DbgString0 db "Debug Point 0", 0Dh, 0Ah, 0
  2648. DbgString1 db "Debug Point 1", 0Dh, 0Ah, 0
  2649. DbgString2 db "Debug Point 2", 0Dh, 0Ah, 0
  2650. DbgString3 db "Debug Point 3", 0Dh, 0Ah, 0
  2651. DbgString4 db "Debug Point 4", 0Dh, 0Ah, 0
  2652. endif ; DEBUG
  2653. .errnz ($-_ntfsboot) GT 8192 ; <FATAL PROBLEM: main boot record exceeds available space>
  2654. org 8192
  2655. BootCode ends
  2656. end _ntfsboot