xref: /reactos/boot/freeldr/bootsect/ntfs.S (revision f0d73e0f)
1*f0d73e0fSSylvain Deverre/*
2*f0d73e0fSSylvain Deverre * PROJECT:         ReactOS Bootsector
3*f0d73e0fSSylvain Deverre * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4*f0d73e0fSSylvain Deverre * FILE:            boot/freeldr/bootsect/fat32.S
5*f0d73e0fSSylvain Deverre * PURPOSE:         Implementing boot sector for NTFS
6*f0d73e0fSSylvain Deverre * COPYRIGHT:       Copyright 2020 Sylvain Deverre (deverre.sylv@gmail.com)
7*f0d73e0fSSylvain Deverre */
8*f0d73e0fSSylvain Deverre
9*f0d73e0fSSylvain Deverre/* INCLUDES ******************************************************************/
10*f0d73e0fSSylvain Deverre
11*f0d73e0fSSylvain Deverre#include <asm.inc>
12*f0d73e0fSSylvain Deverre#include <freeldr/include/arch/pc/x86common.h>
13*f0d73e0fSSylvain Deverre
14*f0d73e0fSSylvain Deverre.code16
15*f0d73e0fSSylvain Deverre
16*f0d73e0fSSylvain Deverre//ORG HEX(7c00)
17*f0d73e0fSSylvain Deverre
18*f0d73e0fSSylvain Deverrestart:
19*f0d73e0fSSylvain Deverre    jmp short main
20*f0d73e0fSSylvain Deverre    nop
21*f0d73e0fSSylvain DeverreOEMName:
22*f0d73e0fSSylvain Deverre    .ASCII "NTFS    "   // NTFS signature
23*f0d73e0fSSylvain DeverreBytesPerSector:
24*f0d73e0fSSylvain Deverre    .word 512
25*f0d73e0fSSylvain DeverreSectsPerCluster:
26*f0d73e0fSSylvain Deverre    .byte 8
27*f0d73e0fSSylvain DeverreReservedSectors:
28*f0d73e0fSSylvain Deverre    .word 0             // Unused by NTFS
29*f0d73e0fSSylvain DeverreNumberOfFats:
30*f0d73e0fSSylvain Deverre    .byte 0             // Unused by NTFS
31*f0d73e0fSSylvain DeverreMaxRootEntries:
32*f0d73e0fSSylvain Deverre    .word 0             // Unused by NTFS
33*f0d73e0fSSylvain DeverreTotalSectors:
34*f0d73e0fSSylvain Deverre    .word 0             // Unused by NTFS
35*f0d73e0fSSylvain DeverreMediaDescriptor:
36*f0d73e0fSSylvain Deverre    .byte HEX(0f8)
37*f0d73e0fSSylvain DeverreSectorsPerFat:
38*f0d73e0fSSylvain Deverre    .word 0             // Unused by NTFS
39*f0d73e0fSSylvain DeverreSectorsPerTrack:
40*f0d73e0fSSylvain Deverre    .word 0             // Should contain disk geometry
41*f0d73e0fSSylvain DeverreNumberOfHeads:
42*f0d73e0fSSylvain Deverre    .word 0             // Should contain disk geometry
43*f0d73e0fSSylvain DeverreHiddenSectors:
44*f0d73e0fSSylvain Deverre    .long 0             // Filled by format program
45*f0d73e0fSSylvain DeverreTotalSectorsBig:
46*f0d73e0fSSylvain Deverre    .long 0             // Unused by NTFS
47*f0d73e0fSSylvain Deverre// NTFS inserted info
48*f0d73e0fSSylvain DeverreBootDrive:
49*f0d73e0fSSylvain Deverre    .byte HEX(80)
50*f0d73e0fSSylvain DeverreCurrentHead:
51*f0d73e0fSSylvain Deverre    .byte 0
52*f0d73e0fSSylvain DeverreBootSignature:
53*f0d73e0fSSylvain Deverre    .byte HEX(80)
54*f0d73e0fSSylvain DeverreUnused:
55*f0d73e0fSSylvain Deverre    .byte 0
56*f0d73e0fSSylvain DeverreVolumeSectorCount:
57*f0d73e0fSSylvain Deverre    .quad 0            // Must be patched by format program !
58*f0d73e0fSSylvain DeverreMftLocation:
59*f0d73e0fSSylvain Deverre    .quad 0            // Must be patched by format program !
60*f0d73e0fSSylvain DeverreMftMirrorLocation:
61*f0d73e0fSSylvain Deverre    .quad 0            // Must be patched by format program !
62*f0d73e0fSSylvain DeverreClustersPerMftRecord:
63*f0d73e0fSSylvain Deverre    .long 0
64*f0d73e0fSSylvain DeverreClustersPerIndexRecord:
65*f0d73e0fSSylvain Deverre    .long 0
66*f0d73e0fSSylvain DeverreVolumeSerialNumber:
67*f0d73e0fSSylvain Deverre    .quad 0
68*f0d73e0fSSylvain DeverreChecksum:
69*f0d73e0fSSylvain Deverre    .long 0
70*f0d73e0fSSylvain Deverre
71*f0d73e0fSSylvain Deverremain:
72*f0d73e0fSSylvain Deverre    xor ax,ax               // Setup segment registers
73*f0d73e0fSSylvain Deverre    mov ds,ax               // Make DS correct
74*f0d73e0fSSylvain Deverre    mov es,ax               // Make ES correct
75*f0d73e0fSSylvain Deverre    mov ss,ax               // Make SS correct
76*f0d73e0fSSylvain Deverre    mov sp, HEX(7c00)
77*f0d73e0fSSylvain Deverre    mov bp, sp              // Setup a stack
78*f0d73e0fSSylvain Deverre
79*f0d73e0fSSylvain Deverre    cmp byte ptr [BootDrive], HEX(0ff)    // If they have specified a boot drive then use it
80*f0d73e0fSSylvain Deverre    jne GetDriveParameters
81*f0d73e0fSSylvain Deverre
82*f0d73e0fSSylvain Deverre    mov byte ptr [BootDrive], dl          // Save the boot drive
83*f0d73e0fSSylvain Deverre
84*f0d73e0fSSylvain DeverreGetDriveParameters:
85*f0d73e0fSSylvain Deverre    mov  ah, 8
86*f0d73e0fSSylvain Deverre    mov  dl, byte ptr [BootDrive]               // Get boot drive in dl
87*f0d73e0fSSylvain Deverre    int  HEX(13)                                // Request drive parameters from the bios
88*f0d73e0fSSylvain Deverre    jnc  CalcDriveSize                          // If the call succeeded then calculate the drive size
89*f0d73e0fSSylvain Deverre
90*f0d73e0fSSylvain Deverre    // If we get here then the call to the BIOS failed
91*f0d73e0fSSylvain Deverre    // so just set CHS equal to the maximum addressable
92*f0d73e0fSSylvain Deverre    // size
93*f0d73e0fSSylvain Deverre    mov  cx, HEX(0ffff)
94*f0d73e0fSSylvain Deverre    mov  dh, cl
95*f0d73e0fSSylvain Deverre
96*f0d73e0fSSylvain DeverreCalcDriveSize:
97*f0d73e0fSSylvain Deverre    // Now that we have the drive geometry
98*f0d73e0fSSylvain Deverre    // lets calculate the drive size
99*f0d73e0fSSylvain Deverre    mov  bl, ch         // Put the low 8-bits of the cylinder count into BL
100*f0d73e0fSSylvain Deverre    mov  bh, cl         // Put the high 2-bits in BH
101*f0d73e0fSSylvain Deverre    shr  bh, 6          // Shift them into position, now BX contains the cylinder count
102*f0d73e0fSSylvain Deverre    and  cl, HEX(3f)    // Mask off cylinder bits from sector count
103*f0d73e0fSSylvain Deverre    // CL now contains sectors per track and DH contains head count
104*f0d73e0fSSylvain Deverre    movzx eax, dh       // Move the heads into EAX
105*f0d73e0fSSylvain Deverre    movzx ebx, bx       // Move the cylinders into EBX
106*f0d73e0fSSylvain Deverre    movzx ecx, cl       // Move the sectors per track into ECX
107*f0d73e0fSSylvain Deverre    inc   eax           // Make it one based because the bios returns it zero based
108*f0d73e0fSSylvain Deverre    inc   ebx           // Make the cylinder count one based also
109*f0d73e0fSSylvain Deverre    mul   ecx           // Multiply heads with the sectors per track, result in edx:eax
110*f0d73e0fSSylvain Deverre    mul   ebx           // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
111*f0d73e0fSSylvain Deverre
112*f0d73e0fSSylvain Deverre    // We now have the total number of sectors as reported
113*f0d73e0fSSylvain Deverre    // by the bios in eax, so store it in our variable
114*f0d73e0fSSylvain Deverre    mov dword ptr ds:[BiosCHSDriveSize], eax
115*f0d73e0fSSylvain Deverre
116*f0d73e0fSSylvain DeverreLoadExtraBootCode:
117*f0d73e0fSSylvain Deverre    // First we have to load our extra boot code at
118*f0d73e0fSSylvain Deverre    // next sector into memory at [0000:7e00h]
119*f0d73e0fSSylvain Deverre    mov  eax, HEX(1)
120*f0d73e0fSSylvain Deverre    xor  edx, edx
121*f0d73e0fSSylvain Deverre    mov  cx, 4
122*f0d73e0fSSylvain Deverre    xor  bx, bx
123*f0d73e0fSSylvain Deverre    mov  es, bx                                 // Read sector to [0000:7e00h]
124*f0d73e0fSSylvain Deverre    mov  bx, HEX(7e00)
125*f0d73e0fSSylvain Deverre    call ReadSectors
126*f0d73e0fSSylvain Deverre    jmp  StartSearch
127*f0d73e0fSSylvain Deverre
128*f0d73e0fSSylvain Deverre
129*f0d73e0fSSylvain Deverre// Reads logical sectors into [ES:BX]
130*f0d73e0fSSylvain Deverre// EDX:EAX has logical sector number to read
131*f0d73e0fSSylvain Deverre// CX has number of sectors to read
132*f0d73e0fSSylvain DeverreReadSectors:
133*f0d73e0fSSylvain Deverre    push es
134*f0d73e0fSSylvain Deverre    add  eax, dword ptr [HiddenSectors]   // Add offset from the disk beginning
135*f0d73e0fSSylvain Deverre    test edx, edx
136*f0d73e0fSSylvain Deverre    jnz  ReadSectorsLBA
137*f0d73e0fSSylvain Deverre    cmp  eax, dword ptr ds:[BiosCHSDriveSize]   // Check if they are reading a sector outside CHS range
138*f0d73e0fSSylvain Deverre    jae  ReadSectorsLBA                         // Yes - go to the LBA routine
139*f0d73e0fSSylvain Deverre                                                // If at all possible we want to use LBA routines because
140*f0d73e0fSSylvain Deverre                                                // They are optimized to read more than 1 sector per read
141*f0d73e0fSSylvain Deverre
142*f0d73e0fSSylvain Deverre    pushad                                      // Save logical sector number & sector count
143*f0d73e0fSSylvain Deverre
144*f0d73e0fSSylvain DeverreCheckInt13hExtensions:                          // Now check if this computer supports extended reads
145*f0d73e0fSSylvain Deverre    mov  ah, HEX(41)                            // AH = 41h
146*f0d73e0fSSylvain Deverre    mov  bx, HEX(55aa)                          // BX = 55AAh
147*f0d73e0fSSylvain Deverre    mov  dl, byte ptr [BootDrive]               // DL = drive (80h-FFh)
148*f0d73e0fSSylvain Deverre    int  HEX(13)                                // IBM/MS INT 13 Extensions - INSTALLATION CHECK
149*f0d73e0fSSylvain Deverre    jc   ReadSectorsCHS                         // CF set on error (extensions not supported)
150*f0d73e0fSSylvain Deverre    cmp  bx, HEX(0aa55)                         // BX = AA55h if installed
151*f0d73e0fSSylvain Deverre    jne  ReadSectorsCHS
152*f0d73e0fSSylvain Deverre    test cl,1                                   // CX = API subset support bitmap
153*f0d73e0fSSylvain Deverre    jz   ReadSectorsCHS                         // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
154*f0d73e0fSSylvain Deverre
155*f0d73e0fSSylvain Deverre    popad                                       // Restore sector count & logical sector number
156*f0d73e0fSSylvain Deverre
157*f0d73e0fSSylvain DeverreReadSectorsLBA:
158*f0d73e0fSSylvain Deverre    pushad                                      // Save logical sector number & sector count
159*f0d73e0fSSylvain Deverre
160*f0d73e0fSSylvain Deverre    cmp  cx, 64                                 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
161*f0d73e0fSSylvain Deverre    jbe  ReadSectorsSetupDiskAddressPacket      // If we are reading less than 65 sectors then just do the read
162*f0d73e0fSSylvain Deverre    mov  cx, 64                                 // Otherwise read only 64 sectors on this loop iteration
163*f0d73e0fSSylvain Deverre
164*f0d73e0fSSylvain DeverreReadSectorsSetupDiskAddressPacket:
165*f0d73e0fSSylvain Deverre    mov word ptr ds:[LBASectorsRead],cx
166*f0d73e0fSSylvain Deverre    push edx
167*f0d73e0fSSylvain Deverre    push eax                                // Put 64-bit logical block address on stack
168*f0d73e0fSSylvain Deverre    push es                                 // Put transfer segment on stack
169*f0d73e0fSSylvain Deverre    push bx                                 // Put transfer offset on stack
170*f0d73e0fSSylvain Deverre    push cx                                 // Set transfer count
171*f0d73e0fSSylvain Deverre    push 16                                 // Set size of packet to 10h
172*f0d73e0fSSylvain Deverre    mov  si, sp                             // Setup disk address packet on stack
173*f0d73e0fSSylvain Deverre
174*f0d73e0fSSylvain Deverre    mov  dl, byte ptr [BootDrive]           // Drive number
175*f0d73e0fSSylvain Deverre    mov  ah, HEX(42)                        // Int 13h, AH = 42h - Extended Read
176*f0d73e0fSSylvain Deverre    int  HEX(13)                            // Call BIOS
177*f0d73e0fSSylvain Deverre    jc   PrintDiskError                     // If the read failed then abort
178*f0d73e0fSSylvain Deverre
179*f0d73e0fSSylvain Deverre    add  sp, 16                             // Remove disk address packet from stack
180*f0d73e0fSSylvain Deverre
181*f0d73e0fSSylvain Deverre    popad                                   // Restore sector count & logical sector number
182*f0d73e0fSSylvain Deverre
183*f0d73e0fSSylvain Deverre    push bx
184*f0d73e0fSSylvain Deverre    mov  ebx, dword ptr ds:[LBASectorsRead]
185*f0d73e0fSSylvain Deverre    add  eax, ebx                           // Increment sector to read
186*f0d73e0fSSylvain Deverre    adc  edx, 0
187*f0d73e0fSSylvain Deverre    shl  ebx, 5
188*f0d73e0fSSylvain Deverre    mov  dx, es
189*f0d73e0fSSylvain Deverre    add  dx, bx                             // Setup read buffer for next sector
190*f0d73e0fSSylvain Deverre    mov  es, dx
191*f0d73e0fSSylvain Deverre    pop  bx
192*f0d73e0fSSylvain Deverre
193*f0d73e0fSSylvain Deverre    sub  cx, word ptr ds:[LBASectorsRead]
194*f0d73e0fSSylvain Deverre    jnz  ReadSectorsLBA                     // Read next sector
195*f0d73e0fSSylvain Deverre
196*f0d73e0fSSylvain Deverre    pop es
197*f0d73e0fSSylvain Deverre    ret
198*f0d73e0fSSylvain Deverre
199*f0d73e0fSSylvain DeverreLBASectorsRead:
200*f0d73e0fSSylvain Deverre    .long    0
201*f0d73e0fSSylvain Deverre
202*f0d73e0fSSylvain Deverre
203*f0d73e0fSSylvain Deverre// Reads logical sectors into [ES:BX]
204*f0d73e0fSSylvain Deverre// EAX has logical sector number to read
205*f0d73e0fSSylvain Deverre// CX has number of sectors to read
206*f0d73e0fSSylvain DeverreReadSectorsCHS:
207*f0d73e0fSSylvain Deverre    popad                                        // Get logical sector number & sector count off stack
208*f0d73e0fSSylvain Deverre
209*f0d73e0fSSylvain DeverreReadSectorsCHSLoop:
210*f0d73e0fSSylvain Deverre    pushad
211*f0d73e0fSSylvain Deverre    xor  edx, edx
212*f0d73e0fSSylvain Deverre    movzx ecx, word ptr [SectorsPerTrack]
213*f0d73e0fSSylvain Deverre    div  ecx                                    // Divide logical by SectorsPerTrack
214*f0d73e0fSSylvain Deverre    inc  dl                                     // Sectors numbering starts at 1 not 0
215*f0d73e0fSSylvain Deverre    mov  cl, dl                                 // Sector in CL
216*f0d73e0fSSylvain Deverre    mov  edx, eax
217*f0d73e0fSSylvain Deverre    shr  edx, 16
218*f0d73e0fSSylvain Deverre    div  word ptr [NumberOfHeads]               // Divide logical by number of heads
219*f0d73e0fSSylvain Deverre    mov  dh, dl                                 // Head in DH
220*f0d73e0fSSylvain Deverre    mov  dl, byte ptr [BootDrive]               // Drive number in DL
221*f0d73e0fSSylvain Deverre    mov  ch, al                                 // Cylinder in CX
222*f0d73e0fSSylvain Deverre    ror  ah, 2                                  // Low 8 bits of cylinder in CH, high 2 bits
223*f0d73e0fSSylvain Deverre                                                //  in CL shifted to bits 6 & 7
224*f0d73e0fSSylvain Deverre    or   cl, ah                                 // Or with sector number
225*f0d73e0fSSylvain Deverre    mov  ax, HEX(0201)
226*f0d73e0fSSylvain Deverre    int  HEX(13)    // DISK - READ SECTORS INTO MEMORY
227*f0d73e0fSSylvain Deverre                     // AL = number of sectors to read, CH = track, CL = sector
228*f0d73e0fSSylvain Deverre                     // DH = head, DL = drive, ES:BX -> buffer to fill
229*f0d73e0fSSylvain Deverre                     // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
230*f0d73e0fSSylvain Deverre
231*f0d73e0fSSylvain Deverre    jc   PrintDiskError                         // If the read failed then abort
232*f0d73e0fSSylvain Deverre
233*f0d73e0fSSylvain Deverre    popad
234*f0d73e0fSSylvain Deverre
235*f0d73e0fSSylvain Deverre    inc  eax                                    // Increment Sector to Read
236*f0d73e0fSSylvain Deverre
237*f0d73e0fSSylvain Deverre    mov  dx, es
238*f0d73e0fSSylvain Deverre    add  dx, 32                                 // Increment read buffer for next sector
239*f0d73e0fSSylvain Deverre    mov  es, dx
240*f0d73e0fSSylvain Deverre
241*f0d73e0fSSylvain Deverre    loop ReadSectorsCHSLoop                     // Read next sector
242*f0d73e0fSSylvain Deverre
243*f0d73e0fSSylvain Deverre    pop es
244*f0d73e0fSSylvain Deverre    ret
245*f0d73e0fSSylvain Deverre
246*f0d73e0fSSylvain Deverre// Displays a disk error message
247*f0d73e0fSSylvain Deverre// And reboots
248*f0d73e0fSSylvain DeverrePrintDiskError:
249*f0d73e0fSSylvain Deverre    mov  si, offset msgDiskError        // Bad boot disk message
250*f0d73e0fSSylvain Deverre    call PutChars                       // Display it
251*f0d73e0fSSylvain Deverre
252*f0d73e0fSSylvain Deverre    jmp  Reboot
253*f0d73e0fSSylvain Deverre
254*f0d73e0fSSylvain Deverre// Displays a file system error message
255*f0d73e0fSSylvain Deverre// And reboots
256*f0d73e0fSSylvain DeverrePrintFileSystemError:
257*f0d73e0fSSylvain Deverre    mov  si, offset msgFileSystemError  // FreeLdr not found message
258*f0d73e0fSSylvain Deverre    call PutChars                       // Display it
259*f0d73e0fSSylvain Deverre
260*f0d73e0fSSylvain DeverreReboot:
261*f0d73e0fSSylvain Deverre    mov  si, offset msgAnyKey           // Press any key message
262*f0d73e0fSSylvain Deverre    call PutChars                       // Display it
263*f0d73e0fSSylvain Deverre    xor  ax, ax
264*f0d73e0fSSylvain Deverre    int  HEX(16)                        // Wait for a keypress
265*f0d73e0fSSylvain Deverre    int  HEX(19)                        // Reboot
266*f0d73e0fSSylvain Deverre
267*f0d73e0fSSylvain DeverrePutChars:
268*f0d73e0fSSylvain Deverre    lodsb
269*f0d73e0fSSylvain Deverre    or   al, al
270*f0d73e0fSSylvain Deverre    jz   short Done
271*f0d73e0fSSylvain Deverre    mov  ah, HEX(0e)
272*f0d73e0fSSylvain Deverre    mov  bx, 7
273*f0d73e0fSSylvain Deverre    int  HEX(10)
274*f0d73e0fSSylvain Deverre    jmp  short PutChars
275*f0d73e0fSSylvain DeverreDone:
276*f0d73e0fSSylvain Deverre    ret
277*f0d73e0fSSylvain Deverre
278*f0d73e0fSSylvain DeverremsgDiskError:
279*f0d73e0fSSylvain Deverre    .ascii "Disk error", CR, LF, NUL
280*f0d73e0fSSylvain DeverremsgFileSystemError:
281*f0d73e0fSSylvain Deverre    .ascii "File system error", CR, LF, NUL
282*f0d73e0fSSylvain DeverremsgAnyKey:
283*f0d73e0fSSylvain Deverre    .ascii "Press any key to restart", CR, LF, NUL
284*f0d73e0fSSylvain Deverre
285*f0d73e0fSSylvain DeverreSectsPerMFT:
286*f0d73e0fSSylvain Deverre    .word 0
287*f0d73e0fSSylvain Deverre
288*f0d73e0fSSylvain DeverreMFTStartSector:
289*f0d73e0fSSylvain Deverre    .quad 0
290*f0d73e0fSSylvain Deverre
291*f0d73e0fSSylvain DeverreBiosCHSDriveSize:
292*f0d73e0fSSylvain Deverre    .long 0
293*f0d73e0fSSylvain Deverre
294*f0d73e0fSSylvain Deverre.org 509 // Pad to 509 bytes
295*f0d73e0fSSylvain DeverreBootPartition:
296*f0d73e0fSSylvain Deverre    .byte 1
297*f0d73e0fSSylvain Deverre
298*f0d73e0fSSylvain DeverreBootFlagSignature:
299*f0d73e0fSSylvain Deverre    .word HEX(0aa55)    // BootSector signature
300*f0d73e0fSSylvain Deverre
301*f0d73e0fSSylvain Deverre// End of Bootsector
302*f0d73e0fSSylvain Deverre// Next sector starts as sector 2, since boot sector is
303*f0d73e0fSSylvain Deverre// as a file under NTFS ($Boot, which has inode number 7)
304*f0d73e0fSSylvain Deverre// and takes the two first clusters of the volume (which means
305*f0d73e0fSSylvain Deverre// 16 sectors, checked on Windows and mkfs.ntfs from ntfsutils)
306*f0d73e0fSSylvain Deverre
307*f0d73e0fSSylvain Deverre#define ATTRIBUTE_DATA HEX(80)
308*f0d73e0fSSylvain Deverre#define ATTRIBUTE_INDEX_ROOT HEX(90)
309*f0d73e0fSSylvain Deverre#define ATTRIBUTE_INDEX_ALLOCATION HEX(A0)
310*f0d73e0fSSylvain Deverre#define FILE_MAGIC HEX(454c4946)
311*f0d73e0fSSylvain Deverre#define INDX_MAGIC HEX(58444e49)
312*f0d73e0fSSylvain Deverre#define NTFS_FILE_ROOT 5
313*f0d73e0fSSylvain DeverreStartSearch:
314*f0d73e0fSSylvain Deverre    // Compute MFT start sector
315*f0d73e0fSSylvain Deverre    mov   eax, dword ptr [MftLocation]
316*f0d73e0fSSylvain Deverre    mov   cl, byte ptr [SectsPerCluster]
317*f0d73e0fSSylvain Deverre    movzx ecx, cx
318*f0d73e0fSSylvain Deverre    mul   ecx
319*f0d73e0fSSylvain Deverre    mov   dword ptr [MFTStartSector], eax
320*f0d73e0fSSylvain Deverre
321*f0d73e0fSSylvain Deverre    // Compute size of MFT entry in sectors
322*f0d73e0fSSylvain Deverre    xor   ax, ax
323*f0d73e0fSSylvain Deverre    mov   al, byte ptr [ClustersPerMftRecord]
324*f0d73e0fSSylvain Deverre    test  al, al
325*f0d73e0fSSylvain Deverre    js    NegativeOffset
326*f0d73e0fSSylvain Deverre    mov   cx, word ptr [SectsPerCluster]
327*f0d73e0fSSylvain Deverre    mul   cx
328*f0d73e0fSSylvain Deverre    mov   word ptr [SectsPerMFT], cx
329*f0d73e0fSSylvain Deverre    jmp   SearchNext
330*f0d73e0fSSylvain Deverre    NegativeOffset:
331*f0d73e0fSSylvain Deverre    // ClustersPerMftRecord is negative, so we need to perform 1 << (-ClustersPerMftRecord)
332*f0d73e0fSSylvain Deverre    // to get the number of bytes needed for a MFT entry
333*f0d73e0fSSylvain Deverre    not   al
334*f0d73e0fSSylvain Deverre    inc   al
335*f0d73e0fSSylvain Deverre    mov   cl, al
336*f0d73e0fSSylvain Deverre    xor   ax, ax
337*f0d73e0fSSylvain Deverre    inc   ax
338*f0d73e0fSSylvain Deverre    shl   ax, cl
339*f0d73e0fSSylvain Deverre
340*f0d73e0fSSylvain Deverre    // But here want to store sectors, so we need to divide by BytesPerSector
341*f0d73e0fSSylvain Deverre    xor   dx, dx
342*f0d73e0fSSylvain Deverre    mov   cx, word ptr [BytesPerSector]
343*f0d73e0fSSylvain Deverre    div   cx
344*f0d73e0fSSylvain Deverre    mov   word ptr [SectsPerMFT], ax
345*f0d73e0fSSylvain Deverre
346*f0d73e0fSSylvain Deverre    SearchNext:
347*f0d73e0fSSylvain Deverre    mov   bp, sp
348*f0d73e0fSSylvain Deverre    sub   sp, HEX(10)
349*f0d73e0fSSylvain Deverre
350*f0d73e0fSSylvain Deverre    // Read Root Directory MFT into [2000:0]
351*f0d73e0fSSylvain Deverre    mov   ax, HEX(2000)
352*f0d73e0fSSylvain Deverre    mov   es, ax
353*f0d73e0fSSylvain Deverre    xor   bx, bx
354*f0d73e0fSSylvain Deverre    xor   edx, edx
355*f0d73e0fSSylvain Deverre    mov   eax, NTFS_FILE_ROOT
356*f0d73e0fSSylvain Deverre    call  ReadInode
357*f0d73e0fSSylvain Deverre
358*f0d73e0fSSylvain Deverre    // Finds freeldr.sys into index root's B-Tree
359*f0d73e0fSSylvain Deverre    // and return its MFT index
360*f0d73e0fSSylvain Deverre    xor   ax, ax
361*f0d73e0fSSylvain Deverre    call  ExploreIndexRoot
362*f0d73e0fSSylvain Deverre
363*f0d73e0fSSylvain Deverre    // Read the MFT entry of freeldr.sys into [A00:0]
364*f0d73e0fSSylvain Deverre    push  eax
365*f0d73e0fSSylvain Deverre    mov   ax, HEX(A00)
366*f0d73e0fSSylvain Deverre    mov   es, ax
367*f0d73e0fSSylvain Deverre    pop   eax
368*f0d73e0fSSylvain Deverre    call  ReadInode
369*f0d73e0fSSylvain Deverre
370*f0d73e0fSSylvain Deverre    xor   ax, ax
371*f0d73e0fSSylvain Deverre    mov   ebx, HEX(30)
372*f0d73e0fSSylvain Deverre    call  FindAttributeHdr
373*f0d73e0fSSylvain Deverre    mov   si, ax
374*f0d73e0fSSylvain Deverre    // Move to the attribute data
375*f0d73e0fSSylvain Deverre    add   si, word ptr es:[si + HEX(14)]
376*f0d73e0fSSylvain Deverre
377*f0d73e0fSSylvain Deverre    // We don't support compressed, sparse or encrypted Freeldr
378*f0d73e0fSSylvain Deverre    test  dword ptr es:[si + HEX(38)], HEX(4a00)
379*f0d73e0fSSylvain Deverre    jnz   CompressedFreeldr
380*f0d73e0fSSylvain Deverre
381*f0d73e0fSSylvain Deverre    // Compute size in clusters
382*f0d73e0fSSylvain Deverre    mov   eax, dword ptr es:[si + HEX(28)]
383*f0d73e0fSSylvain Deverre    mov   cl, byte ptr [SectsPerCluster]
384*f0d73e0fSSylvain Deverre    xor   edx, edx
385*f0d73e0fSSylvain Deverre    div   ecx
386*f0d73e0fSSylvain Deverre    mov   cx, word ptr [BytesPerSector]
387*f0d73e0fSSylvain Deverre    movzx ecx, cx
388*f0d73e0fSSylvain Deverre    xor   edx, edx
389*f0d73e0fSSylvain Deverre    div   ecx
390*f0d73e0fSSylvain Deverre    mov   edx, eax
391*f0d73e0fSSylvain Deverre
392*f0d73e0fSSylvain Deverre    push  ax
393*f0d73e0fSSylvain Deverre    mov   si, offset msgLoading
394*f0d73e0fSSylvain Deverre    call  PutChars
395*f0d73e0fSSylvain Deverre    pop   ax
396*f0d73e0fSSylvain Deverre
397*f0d73e0fSSylvain Deverre    xor   ax, ax
398*f0d73e0fSSylvain Deverre    mov   ebx, HEX(80)
399*f0d73e0fSSylvain Deverre    call  FindAttributeHdr
400*f0d73e0fSSylvain Deverre    mov   si, ax
401*f0d73e0fSSylvain Deverre    add   ax, word ptr es:[si + HEX(20)]
402*f0d73e0fSSylvain Deverre    xor   ecx, ecx
403*f0d73e0fSSylvain Deverre    xor   bx, bx
404*f0d73e0fSSylvain Deverre    push  FREELDR_BASE / 16
405*f0d73e0fSSylvain Deverre    pop   fs
406*f0d73e0fSSylvain Deverre    FreeLdrLoad:
407*f0d73e0fSSylvain Deverre        pushad
408*f0d73e0fSSylvain Deverre        call  ReadNonResidentAttribute
409*f0d73e0fSSylvain Deverre        popad
410*f0d73e0fSSylvain Deverre        mov   bx, fs
411*f0d73e0fSSylvain Deverre        add   bx, HEX(100)
412*f0d73e0fSSylvain Deverre        mov   fs, bx
413*f0d73e0fSSylvain Deverre        xor   bx, bx
414*f0d73e0fSSylvain Deverre        inc   ecx
415*f0d73e0fSSylvain Deverre        cmp   ecx, edx
416*f0d73e0fSSylvain Deverre        jbe   FreeLdrLoad
417*f0d73e0fSSylvain Deverre
418*f0d73e0fSSylvain Deverre    mov    dl, byte ptr [BootDrive]
419*f0d73e0fSSylvain Deverre    mov    dh, byte ptr [BootPartition]
420*f0d73e0fSSylvain Deverre    ljmp16 0, FREELDR_BASE
421*f0d73e0fSSylvain Deverre
422*f0d73e0fSSylvain Deverre// Error message if Freeldr is compressed, encrypted or sparse
423*f0d73e0fSSylvain DeverreCompressedFreeldr:
424*f0d73e0fSSylvain Deverre    mov   si, offset msgFreeldrCompressed
425*f0d73e0fSSylvain Deverre    call  PutChars
426*f0d73e0fSSylvain Deverre    jmp   Reboot
427*f0d73e0fSSylvain Deverre
428*f0d73e0fSSylvain Deverre// Finds Freeldr.sys into the directory tree and returns the
429*f0d73e0fSSylvain Deverre// inode index
430*f0d73e0fSSylvain Deverre// INPUT:
431*f0d73e0fSSylvain Deverre//  - ES:[AX] address of the MFT record
432*f0d73e0fSSylvain Deverre// OUTPUT:
433*f0d73e0fSSylvain Deverre//  - EDX:EAX MFT number of the found file
434*f0d73e0fSSylvain DeverreExploreIndexRoot:
435*f0d73e0fSSylvain Deverre    #define fileRecordBase 2
436*f0d73e0fSSylvain Deverre    #define fileRecord 4
437*f0d73e0fSSylvain Deverre    #define indexRootData 6
438*f0d73e0fSSylvain Deverre    #define allocationRunList 8
439*f0d73e0fSSylvain Deverre
440*f0d73e0fSSylvain Deverre    push  bp
441*f0d73e0fSSylvain Deverre    mov   bp, sp
442*f0d73e0fSSylvain Deverre    push  bx
443*f0d73e0fSSylvain Deverre    push  si
444*f0d73e0fSSylvain Deverre    push  di
445*f0d73e0fSSylvain Deverre    sub   sp, HEX(10)
446*f0d73e0fSSylvain Deverre    mov   word ptr [bp - fileRecordBase], es
447*f0d73e0fSSylvain Deverre    mov   word ptr [bp - fileRecord], ax
448*f0d73e0fSSylvain Deverre
449*f0d73e0fSSylvain Deverre    mov   ebx, ATTRIBUTE_INDEX_ROOT
450*f0d73e0fSSylvain Deverre    call  FindAttributeHdr
451*f0d73e0fSSylvain Deverre    test  ax, ax
452*f0d73e0fSSylvain Deverre    jz    PrintFileSystemError         // fail if no INDEX_ROOT
453*f0d73e0fSSylvain Deverre
454*f0d73e0fSSylvain Deverre    mov   si, ax
455*f0d73e0fSSylvain Deverre    cmp   byte ptr es:[si + 8], HEX(0)
456*f0d73e0fSSylvain Deverre    jnz   PrintFileSystemError         // fail if attribute is non-resident
457*f0d73e0fSSylvain Deverre
458*f0d73e0fSSylvain Deverre    add   si, word ptr es:[si + HEX(14)]
459*f0d73e0fSSylvain Deverre    cmp   dword ptr es:[si], HEX(30)
460*f0d73e0fSSylvain Deverre    jnz   PrintFileSystemError                  // fail if FILE_NAME attribute isn't indexed
461*f0d73e0fSSylvain Deverre
462*f0d73e0fSSylvain Deverre    mov   word ptr [bp - indexRootData], si
463*f0d73e0fSSylvain Deverre    mov   ax, word ptr [bp - fileRecord]
464*f0d73e0fSSylvain Deverre
465*f0d73e0fSSylvain Deverre    test  byte ptr es:[si + HEX(0c)], 1
466*f0d73e0fSSylvain Deverre    jz    ExploreNext                          // Skip index allocation lookup if we don't have children
467*f0d73e0fSSylvain Deverre
468*f0d73e0fSSylvain Deverre    mov   ebx, ATTRIBUTE_INDEX_ALLOCATION
469*f0d73e0fSSylvain Deverre    call  FindAttributeHdr
470*f0d73e0fSSylvain Deverre    test  ax, ax
471*f0d73e0fSSylvain Deverre    jz    PrintFileSystemError                  // No INDEX_ALLOCATION found
472*f0d73e0fSSylvain Deverre
473*f0d73e0fSSylvain Deverre    mov   si, ax
474*f0d73e0fSSylvain Deverre    cmp   byte ptr es:[si + 8], 1
475*f0d73e0fSSylvain Deverre    jnz   PrintFileSystemError                  // Fail if attribute is resident (shouldn't be)
476*f0d73e0fSSylvain Deverre
477*f0d73e0fSSylvain Deverre    add   si, word ptr es:[si + HEX(20)]
478*f0d73e0fSSylvain Deverre    mov   word ptr [bp - allocationRunList], si // save run list
479*f0d73e0fSSylvain Deverre
480*f0d73e0fSSylvain Deverre    ExploreNext:
481*f0d73e0fSSylvain Deverre    mov   si, word ptr [bp - indexRootData]
482*f0d73e0fSSylvain Deverre    lea   si, [si + 32]                         // get the first INDEX_ENTRY
483*f0d73e0fSSylvain Deverre
484*f0d73e0fSSylvain Deverre    // Main search loop. We browse the B-Tree which contains directory entries
485*f0d73e0fSSylvain Deverre    // SI contains the current index entry.
486*f0d73e0fSSylvain Deverre    NodeCheck:
487*f0d73e0fSSylvain Deverre        test  word ptr es:[si + HEX(0C)], 2
488*f0d73e0fSSylvain Deverre        jnz   NodeCheckLastNode
489*f0d73e0fSSylvain Deverre
490*f0d73e0fSSylvain Deverre        mov   cl, byte ptr es:[si + HEX(50)]
491*f0d73e0fSSylvain Deverre        movzx cx, cl
492*f0d73e0fSSylvain Deverre        lea   si, [si + HEX(52)]
493*f0d73e0fSSylvain Deverre        mov   di, offset FreeLdr
494*f0d73e0fSSylvain Deverre        call  CompareWideStrInsensitive
495*f0d73e0fSSylvain Deverre        lea   si, [si - HEX(52)]
496*f0d73e0fSSylvain Deverre        test  ax, ax
497*f0d73e0fSSylvain Deverre        jz    RootIndexFound
498*f0d73e0fSSylvain Deverre        test  word ptr es:[si + HEX(0C)], 1
499*f0d73e0fSSylvain Deverre        jz    ContinueSearch
500*f0d73e0fSSylvain Deverre
501*f0d73e0fSSylvain Deverre        test  ax, HEX(f000)
502*f0d73e0fSSylvain Deverre        jnz   LookupChildNode                  // if result < 0 then explore child node
503*f0d73e0fSSylvain Deverre
504*f0d73e0fSSylvain Deverre        ContinueSearch:
505*f0d73e0fSSylvain Deverre        add   si, word ptr es:[si + 8]
506*f0d73e0fSSylvain Deverre        jmp   NodeCheck
507*f0d73e0fSSylvain Deverre
508*f0d73e0fSSylvain Deverre        RootIndexFound:
509*f0d73e0fSSylvain Deverre        mov   eax, dword ptr es:[si]           // We found root entry, return with its MFT number
510*f0d73e0fSSylvain Deverre        mov   dx, word ptr es:[si + 4]         // Return high part into edx.
511*f0d73e0fSSylvain Deverre                                               // We take only the first word, the high word contains the
512*f0d73e0fSSylvain Deverre                                               // sequence number
513*f0d73e0fSSylvain Deverre        movzx edx, dx
514*f0d73e0fSSylvain Deverre        jmp   ExitIndexTree
515*f0d73e0fSSylvain Deverre
516*f0d73e0fSSylvain Deverre        NodeCheckLastNode:
517*f0d73e0fSSylvain Deverre        test  word ptr es:[si + HEX(0C)], 1
518*f0d73e0fSSylvain Deverre        jz    PrintFreeldrError
519*f0d73e0fSSylvain Deverre
520*f0d73e0fSSylvain Deverre        LookupChildNode:
521*f0d73e0fSSylvain Deverre        // Take the right LCN at the end of the index record
522*f0d73e0fSSylvain Deverre        add   si, word ptr es:[si + 8]
523*f0d73e0fSSylvain Deverre        mov   ecx, dword ptr es:[si - 8]
524*f0d73e0fSSylvain Deverre
525*f0d73e0fSSylvain Deverre        // Read the fixed up LCN
526*f0d73e0fSSylvain Deverre        mov   bx, word ptr [bp - allocationRunList]
527*f0d73e0fSSylvain Deverre        mov   ax, word ptr [bp - fileRecordBase]
528*f0d73e0fSSylvain Deverre        mov   es, ax
529*f0d73e0fSSylvain Deverre        mov   ax, HEX(9000)
530*f0d73e0fSSylvain Deverre        mov   fs, ax
531*f0d73e0fSSylvain Deverre        mov   ax, bx
532*f0d73e0fSSylvain Deverre        xor   bx, bx
533*f0d73e0fSSylvain Deverre        call  ReadINDX
534*f0d73e0fSSylvain Deverre
535*f0d73e0fSSylvain Deverre        // Go again to the loop but with the child node list
536*f0d73e0fSSylvain Deverre        mov   ax, HEX(9000)
537*f0d73e0fSSylvain Deverre        mov   es, ax
538*f0d73e0fSSylvain Deverre        mov   si, 0
539*f0d73e0fSSylvain Deverre        add   si, es:[si + HEX(18)] // Go to the first node
540*f0d73e0fSSylvain Deverre        lea   si, [si + HEX(18)]
541*f0d73e0fSSylvain Deverre        jmp   NodeCheck
542*f0d73e0fSSylvain Deverre
543*f0d73e0fSSylvain Deverre    ExitIndexTree:
544*f0d73e0fSSylvain Deverre    pop   di
545*f0d73e0fSSylvain Deverre    pop   si
546*f0d73e0fSSylvain Deverre    mov   sp, bp
547*f0d73e0fSSylvain Deverre    pop   bp
548*f0d73e0fSSylvain Deverre    ret
549*f0d73e0fSSylvain Deverre
550*f0d73e0fSSylvain Deverre// 64-bit multiplication
551*f0d73e0fSSylvain Deverre// EDX:EAX operand
552*f0d73e0fSSylvain Deverre// ECX operator
553*f0d73e0fSSylvain DeverreMultiply64:
554*f0d73e0fSSylvain Deverre    push bp
555*f0d73e0fSSylvain Deverre    mov  bp, sp
556*f0d73e0fSSylvain Deverre    sub  sp, 12
557*f0d73e0fSSylvain Deverre    // Save the high part of the multiplication
558*f0d73e0fSSylvain Deverre    mov  dword ptr [bp - 4], edx
559*f0d73e0fSSylvain Deverre
560*f0d73e0fSSylvain Deverre    // Multiply the low part and save the result
561*f0d73e0fSSylvain Deverre    mul  ecx
562*f0d73e0fSSylvain Deverre    mov  dword ptr[bp - 8], eax
563*f0d73e0fSSylvain Deverre    mov  dword ptr[bp - 12], edx
564*f0d73e0fSSylvain Deverre
565*f0d73e0fSSylvain Deverre    // Multiply the high part and add the carry
566*f0d73e0fSSylvain Deverre    mov  eax, dword ptr [bp - 4]
567*f0d73e0fSSylvain Deverre    mul  ecx
568*f0d73e0fSSylvain Deverre    add  eax, dword ptr [bp - 12]
569*f0d73e0fSSylvain Deverre
570*f0d73e0fSSylvain Deverre    // Format correctly the number
571*f0d73e0fSSylvain Deverre    mov  edx, eax
572*f0d73e0fSSylvain Deverre    mov  eax, dword ptr [bp - 8]
573*f0d73e0fSSylvain Deverre    mov  sp, bp
574*f0d73e0fSSylvain Deverre    pop  bp
575*f0d73e0fSSylvain Deverre    ret
576*f0d73e0fSSylvain Deverre
577*f0d73e0fSSylvain Deverre// Compare case-insensitive strings
578*f0d73e0fSSylvain Deverre// [ES:SI] - the source file name
579*f0d73e0fSSylvain Deverre// [DS:DI] - the destination file name
580*f0d73e0fSSylvain Deverre// CX - compare length
581*f0d73e0fSSylvain DeverreCompareWideStrInsensitive:
582*f0d73e0fSSylvain Deverre    push bx
583*f0d73e0fSSylvain Deverre    push si
584*f0d73e0fSSylvain Deverre    push di
585*f0d73e0fSSylvain Deverre    movzx cx, cl
586*f0d73e0fSSylvain Deverre    CmpLoop:
587*f0d73e0fSSylvain Deverre        mov ax, word ptr es:[si]
588*f0d73e0fSSylvain Deverre        cmp ax, 'a'
589*f0d73e0fSSylvain Deverre        jl NoUpper
590*f0d73e0fSSylvain Deverre        cmp ax, 'z'
591*f0d73e0fSSylvain Deverre        jg NoUpper
592*f0d73e0fSSylvain Deverre        sub ax, HEX(20)
593*f0d73e0fSSylvain Deverre        NoUpper:
594*f0d73e0fSSylvain Deverre        mov bx, word ptr ds:[di]
595*f0d73e0fSSylvain Deverre        sub bx, ax
596*f0d73e0fSSylvain Deverre        test bx, bx
597*f0d73e0fSSylvain Deverre        jnz  CompareFail
598*f0d73e0fSSylvain Deverre        add  si, 2
599*f0d73e0fSSylvain Deverre        add  di, 2
600*f0d73e0fSSylvain Deverre        dec cx
601*f0d73e0fSSylvain Deverre        jnz CmpLoop
602*f0d73e0fSSylvain Deverre    CompareFail:
603*f0d73e0fSSylvain Deverre    mov ax, bx
604*f0d73e0fSSylvain Deverre    pop di
605*f0d73e0fSSylvain Deverre    pop si
606*f0d73e0fSSylvain Deverre    pop bx
607*f0d73e0fSSylvain Deverre    ret
608*f0d73e0fSSylvain Deverre
609*f0d73e0fSSylvain Deverre// Reads a NTFS cluster
610*f0d73e0fSSylvain Deverre// INPUT:
611*f0d73e0fSSylvain Deverre//  - EDX:EAX     : cluster number to read
612*f0d73e0fSSylvain Deverre// OUTPUT:
613*f0d73e0fSSylvain Deverre//  - ES:BX  : address to read
614*f0d73e0fSSylvain DeverreReadCluster:
615*f0d73e0fSSylvain Deverre    push  ecx
616*f0d73e0fSSylvain Deverre    mov   cl, byte ptr [SectsPerCluster]   // Convert clusters to sectors
617*f0d73e0fSSylvain Deverre    movzx ecx, cx
618*f0d73e0fSSylvain Deverre    call  Multiply64
619*f0d73e0fSSylvain Deverre    call  ReadSectors
620*f0d73e0fSSylvain Deverre    pop   ecx
621*f0d73e0fSSylvain Deverre    ret
622*f0d73e0fSSylvain Deverre
623*f0d73e0fSSylvain Deverre// Reads a MFT entry
624*f0d73e0fSSylvain Deverre// INPUT:
625*f0d73e0fSSylvain Deverre//  - EDX:EAX      : MFT inode
626*f0d73e0fSSylvain Deverre// OUTPUT:
627*f0d73e0fSSylvain Deverre//  - ES:[BX] : address to read
628*f0d73e0fSSylvain DeverreReadInode:
629*f0d73e0fSSylvain Deverre    push  ecx
630*f0d73e0fSSylvain Deverre    push  si
631*f0d73e0fSSylvain Deverre    push  di
632*f0d73e0fSSylvain Deverre
633*f0d73e0fSSylvain Deverre    push  edx
634*f0d73e0fSSylvain Deverre    mov   cx, word ptr [SectsPerMFT]       // Get the correct number of sectors for the FILE entry
635*f0d73e0fSSylvain Deverre    movzx ecx, cx
636*f0d73e0fSSylvain Deverre    mul   ecx
637*f0d73e0fSSylvain Deverre    movzx eax, ax
638*f0d73e0fSSylvain Deverre    add   eax, dword ptr [MFTStartSector]  // Add it to the start of the MFT
639*f0d73e0fSSylvain Deverre    mov   cx, word ptr [SectsPerMFT]
640*f0d73e0fSSylvain Deverre    movzx ecx, cx
641*f0d73e0fSSylvain Deverre    pop   edx
642*f0d73e0fSSylvain Deverre    call  ReadSectors
643*f0d73e0fSSylvain Deverre
644*f0d73e0fSSylvain Deverre    cmp   dword ptr es:[bx], FILE_MAGIC // Ensure we get a valid FILE record
645*f0d73e0fSSylvain Deverre    jnz   PrintFileSystemError
646*f0d73e0fSSylvain Deverre
647*f0d73e0fSSylvain Deverre    call  ApplyFixups
648*f0d73e0fSSylvain Deverre
649*f0d73e0fSSylvain Deverre    pop   di
650*f0d73e0fSSylvain Deverre    pop   si
651*f0d73e0fSSylvain Deverre    pop   ecx
652*f0d73e0fSSylvain Deverre    ret
653*f0d73e0fSSylvain Deverre
654*f0d73e0fSSylvain Deverre#define UpdateSequenceOffset 4
655*f0d73e0fSSylvain Deverre#define UpdateSequenceLen 6
656*f0d73e0fSSylvain Deverre// Apply fixups to INDX and FILE records
657*f0d73e0fSSylvain Deverre// INPUT:
658*f0d73e0fSSylvain Deverre//  - ES:[BX] - pointer to the record to fixup
659*f0d73e0fSSylvain DeverreApplyFixups:
660*f0d73e0fSSylvain Deverre    push  si
661*f0d73e0fSSylvain Deverre    push  di
662*f0d73e0fSSylvain Deverre    mov   si, bx
663*f0d73e0fSSylvain Deverre    add   si, word ptr es:[bx + UpdateSequenceOffset]
664*f0d73e0fSSylvain Deverre    xor   cx, cx
665*f0d73e0fSSylvain Deverre    inc   cx
666*f0d73e0fSSylvain Deverre    FixupLoop:
667*f0d73e0fSSylvain Deverre        cmp  cx, word ptr es:[bx + UpdateSequenceLen]
668*f0d73e0fSSylvain Deverre        jz  EndFixupLoop
669*f0d73e0fSSylvain Deverre        mov   si, bx
670*f0d73e0fSSylvain Deverre        add   si, word ptr es:[bx + UpdateSequenceOffset]
671*f0d73e0fSSylvain Deverre        mov   ax, word ptr es:[si]     // Get first fixup value
672*f0d73e0fSSylvain Deverre
673*f0d73e0fSSylvain Deverre        mov   di, cx
674*f0d73e0fSSylvain Deverre        shl   di, 9
675*f0d73e0fSSylvain Deverre        add   di, bx
676*f0d73e0fSSylvain Deverre        sub   di, 2
677*f0d73e0fSSylvain Deverre        cmp   ax, word ptr es:[di]     // Check fixup value
678*f0d73e0fSSylvain Deverre        jnz   PrintFileSystemError     // Fixup is corrupted, so print error
679*f0d73e0fSSylvain Deverre        inc   cx
680*f0d73e0fSSylvain Deverre        add   si, cx
681*f0d73e0fSSylvain Deverre        mov   ax, word ptr es:[si]     // Apply fixup
682*f0d73e0fSSylvain Deverre        mov   word ptr es:[di], ax
683*f0d73e0fSSylvain Deverre
684*f0d73e0fSSylvain Deverre        jmp   FixupLoop
685*f0d73e0fSSylvain Deverre    EndFixupLoop:
686*f0d73e0fSSylvain Deverre
687*f0d73e0fSSylvain Deverre    pop di
688*f0d73e0fSSylvain Deverre    pop si
689*f0d73e0fSSylvain Deverre    ret
690*f0d73e0fSSylvain Deverre
691*f0d73e0fSSylvain Deverre// Reads a non-resident attribute
692*f0d73e0fSSylvain Deverre// INPUT:
693*f0d73e0fSSylvain Deverre//  - ES:[AX] : Address of the data runs
694*f0d73e0fSSylvain Deverre//  - ECX     : LCN to read
695*f0d73e0fSSylvain Deverre// OUTPUT:
696*f0d73e0fSSylvain Deverre//  - FS:[BX] : Address to write to
697*f0d73e0fSSylvain DeverreReadNonResidentAttribute:
698*f0d73e0fSSylvain Deverre    #define currentLCN 4
699*f0d73e0fSSylvain Deverre    #define offsetWrite 8
700*f0d73e0fSSylvain Deverre    #define startReadLCN HEX(0C)
701*f0d73e0fSSylvain Deverre    push  bp
702*f0d73e0fSSylvain Deverre    mov   bp, sp
703*f0d73e0fSSylvain Deverre    sub   sp, HEX(10)
704*f0d73e0fSSylvain Deverre    push  edx
705*f0d73e0fSSylvain Deverre    push  si
706*f0d73e0fSSylvain Deverre    mov   dword ptr [bp - currentLCN], 0          // Store the current LCN
707*f0d73e0fSSylvain Deverre    mov   word ptr [bp - offsetWrite], bx
708*f0d73e0fSSylvain Deverre    mov   dword ptr [bp - startReadLCN], ecx
709*f0d73e0fSSylvain Deverre    mov   si, ax
710*f0d73e0fSSylvain Deverre    xor   edx, edx
711*f0d73e0fSSylvain Deverre    RunLoop:
712*f0d73e0fSSylvain Deverre        mov   al, byte ptr es:[si]
713*f0d73e0fSSylvain Deverre        test  al, al
714*f0d73e0fSSylvain Deverre        jz    NotFound
715*f0d73e0fSSylvain Deverre        call  UnpackRun
716*f0d73e0fSSylvain Deverre        add   dword ptr [bp - currentLCN], eax
717*f0d73e0fSSylvain Deverre        cmp   dword ptr [bp - startReadLCN], ecx
718*f0d73e0fSSylvain Deverre        jb    FoundRun
719*f0d73e0fSSylvain Deverre        sub   dword ptr [bp - startReadLCN], ecx  // Decrement the cluster
720*f0d73e0fSSylvain Deverre        jmp   RunLoop
721*f0d73e0fSSylvain Deverre    FoundRun:
722*f0d73e0fSSylvain Deverre    mov   ebx, dword ptr [bp - currentLCN]
723*f0d73e0fSSylvain Deverre    mov   ecx, dword ptr [bp - startReadLCN]
724*f0d73e0fSSylvain Deverre    add   ebx, ecx
725*f0d73e0fSSylvain Deverre
726*f0d73e0fSSylvain Deverre    push es
727*f0d73e0fSSylvain Deverre    mov  ax, fs
728*f0d73e0fSSylvain Deverre    mov  es, ax
729*f0d73e0fSSylvain Deverre    mov  eax, ebx
730*f0d73e0fSSylvain Deverre    mov  bx, word ptr [bp - offsetWrite]
731*f0d73e0fSSylvain Deverre    call ReadCluster
732*f0d73e0fSSylvain Deverre    pop  es
733*f0d73e0fSSylvain Deverre    jmp  RunSearchEnd
734*f0d73e0fSSylvain Deverre    NotFound:
735*f0d73e0fSSylvain Deverre    xor  ax, ax
736*f0d73e0fSSylvain Deverre    RunSearchEnd:
737*f0d73e0fSSylvain Deverre    pop  si
738*f0d73e0fSSylvain Deverre    pop  edx
739*f0d73e0fSSylvain Deverre    mov  sp, bp
740*f0d73e0fSSylvain Deverre    pop  bp
741*f0d73e0fSSylvain Deverre    ret
742*f0d73e0fSSylvain Deverre
743*f0d73e0fSSylvain Deverre// Decodes a run in the runlist
744*f0d73e0fSSylvain Deverre// INPUT:
745*f0d73e0fSSylvain Deverre//  - ES:[SI] : address of the run
746*f0d73e0fSSylvain Deverre// OUTPUT:
747*f0d73e0fSSylvain Deverre//  - EAX : Unpacked LCN
748*f0d73e0fSSylvain Deverre//  - ECX : Unpacked run length (in sectors)
749*f0d73e0fSSylvain Deverre//  - SI  : Next run in the run list
750*f0d73e0fSSylvain DeverreUnpackRun:
751*f0d73e0fSSylvain Deverre    push bp
752*f0d73e0fSSylvain Deverre    mov  bp, sp
753*f0d73e0fSSylvain Deverre    sub  sp, HEX(10)
754*f0d73e0fSSylvain Deverre    push ebx
755*f0d73e0fSSylvain Deverre
756*f0d73e0fSSylvain Deverre    // Unpack run header
757*f0d73e0fSSylvain Deverre    mov  bl, byte ptr es:[si]
758*f0d73e0fSSylvain Deverre    inc  si
759*f0d73e0fSSylvain Deverre
760*f0d73e0fSSylvain Deverre    mov  bh, bl
761*f0d73e0fSSylvain Deverre    shr  bh, 4
762*f0d73e0fSSylvain Deverre    and  bl, 7
763*f0d73e0fSSylvain Deverre    mov  byte ptr [bp-2], bh     // BH contains the LCN length
764*f0d73e0fSSylvain Deverre    mov  byte ptr [bp-1], bl     // BL contains the number of cluster length
765*f0d73e0fSSylvain Deverre
766*f0d73e0fSSylvain Deverre    mov  al, bl
767*f0d73e0fSSylvain Deverre    call UnpackLen
768*f0d73e0fSSylvain Deverre    mov  dword ptr [bp - 8], ebx
769*f0d73e0fSSylvain Deverre
770*f0d73e0fSSylvain Deverre    mov  al, byte ptr [bp-2]
771*f0d73e0fSSylvain Deverre    call UnpackLen
772*f0d73e0fSSylvain Deverre    mov  cl, byte ptr es:[si-1]  // Fixup sign if last byte is > 255
773*f0d73e0fSSylvain Deverre    test cl, HEX(80)
774*f0d73e0fSSylvain Deverre    jz   NoSign
775*f0d73e0fSSylvain Deverre    not  eax
776*f0d73e0fSSylvain Deverre    add  ebx, eax
777*f0d73e0fSSylvain Deverre
778*f0d73e0fSSylvain Deverre    NoSign:
779*f0d73e0fSSylvain Deverre    mov  eax, ebx
780*f0d73e0fSSylvain Deverre    mov  ecx, dword ptr [bp - 8]
781*f0d73e0fSSylvain Deverre    pop  ebx
782*f0d73e0fSSylvain Deverre    mov  sp, bp
783*f0d73e0fSSylvain Deverre    pop  bp
784*f0d73e0fSSylvain Deverre    ret
785*f0d73e0fSSylvain Deverre
786*f0d73e0fSSylvain Deverre
787*f0d73e0fSSylvain Deverre// Auxiliary function that unpacks n bytes in the memory
788*f0d73e0fSSylvain Deverre// INPUT:
789*f0d73e0fSSylvain Deverre//  - AL  : size to unpack (max 4 bytes)
790*f0d73e0fSSylvain Deverre// OUTPUT:
791*f0d73e0fSSylvain Deverre//  - EAX : the mask used to unpack (for negative number fixup)
792*f0d73e0fSSylvain Deverre//  - EBX : the unpacked number
793*f0d73e0fSSylvain Deverre//  - SI  : Next byte to read
794*f0d73e0fSSylvain DeverreUnpackLen:
795*f0d73e0fSSylvain Deverre    push  cx
796*f0d73e0fSSylvain Deverre    movzx ax, al
797*f0d73e0fSSylvain Deverre
798*f0d73e0fSSylvain Deverre    // Read the whole DWORD and then compute a mask to remove
799*f0d73e0fSSylvain Deverre    // unneeded bytes to get correct size
800*f0d73e0fSSylvain Deverre    xor   ebx, ebx
801*f0d73e0fSSylvain Deverre    mov   ebx, dword ptr es:[si]
802*f0d73e0fSSylvain Deverre    add   si, ax
803*f0d73e0fSSylvain Deverre
804*f0d73e0fSSylvain Deverre    cmp   al, 4
805*f0d73e0fSSylvain Deverre    jnz   UnpackLen_not4
806*f0d73e0fSSylvain Deverre    xor   eax, eax
807*f0d73e0fSSylvain Deverre    dec   eax
808*f0d73e0fSSylvain Deverre    jmp   UnpackLen_mask
809*f0d73e0fSSylvain Deverre
810*f0d73e0fSSylvain DeverreUnpackLen_not4:
811*f0d73e0fSSylvain Deverre    mov   cl, al    // Compute mask (2^(8*len) - 1)
812*f0d73e0fSSylvain Deverre    shl   cl, 3
813*f0d73e0fSSylvain Deverre    xor   eax, eax
814*f0d73e0fSSylvain Deverre    inc   eax
815*f0d73e0fSSylvain Deverre    shl   eax, cl
816*f0d73e0fSSylvain Deverre    dec   eax
817*f0d73e0fSSylvain Deverre
818*f0d73e0fSSylvain DeverreUnpackLen_mask:
819*f0d73e0fSSylvain Deverre    and   ebx, eax // Apply mask
820*f0d73e0fSSylvain Deverre    pop   cx
821*f0d73e0fSSylvain Deverre    ret
822*f0d73e0fSSylvain Deverre
823*f0d73e0fSSylvain Deverre// Reads an INDX sector and applies fixups
824*f0d73e0fSSylvain Deverre// INPUT:
825*f0d73e0fSSylvain Deverre//  - ES:[AX] : Address of the data runs
826*f0d73e0fSSylvain Deverre//  - ECX     : LCN to read
827*f0d73e0fSSylvain Deverre// OUTPUT:
828*f0d73e0fSSylvain Deverre//  - FS:[BX] : Address to write to
829*f0d73e0fSSylvain DeverreReadINDX:
830*f0d73e0fSSylvain Deverre    push  es
831*f0d73e0fSSylvain Deverre    push  bx
832*f0d73e0fSSylvain Deverre    call  ReadNonResidentAttribute
833*f0d73e0fSSylvain Deverre    test  ax, ax
834*f0d73e0fSSylvain Deverre    jz    PrintFileSystemError
835*f0d73e0fSSylvain Deverre    cmp   dword ptr fs:[0], INDX_MAGIC
836*f0d73e0fSSylvain Deverre    jnz   PrintFileSystemError  // jump if not valid
837*f0d73e0fSSylvain Deverre    pop   bx
838*f0d73e0fSSylvain Deverre
839*f0d73e0fSSylvain Deverre    mov   ax, fs
840*f0d73e0fSSylvain Deverre    mov   es, ax
841*f0d73e0fSSylvain Deverre    call  ApplyFixups
842*f0d73e0fSSylvain Deverre    pop   es
843*f0d73e0fSSylvain Deverre    ret
844*f0d73e0fSSylvain Deverre
845*f0d73e0fSSylvain Deverre// Finds an attribute header into the MFT
846*f0d73e0fSSylvain Deverre// INPUT:
847*f0d73e0fSSylvain Deverre//  - ES:[AX] : pointer to the MFT entry
848*f0d73e0fSSylvain Deverre//  - EBX     : type to find
849*f0d73e0fSSylvain Deverre// OUTPUT:
850*f0d73e0fSSylvain Deverre//  - ES:[AX] : Pointer to the attribute header in the MFT entry
851*f0d73e0fSSylvain DeverreFindAttributeHdr:
852*f0d73e0fSSylvain Deverre    push  cx
853*f0d73e0fSSylvain Deverre    push  si
854*f0d73e0fSSylvain Deverre    push  edx
855*f0d73e0fSSylvain Deverre    mov   si, ax
856*f0d73e0fSSylvain Deverre    mov   cx, word ptr es:[si+HEX(14)]  // Get offset attribute
857*f0d73e0fSSylvain Deverre    add   si, cx
858*f0d73e0fSSylvain Deverre    FindAttributeHdrLoop:
859*f0d73e0fSSylvain Deverre        mov   edx, dword ptr es:[si]    // Get attribute type
860*f0d73e0fSSylvain Deverre        cmp   edx, ebx
861*f0d73e0fSSylvain Deverre        jz    AttrFound
862*f0d73e0fSSylvain Deverre        cmp   edx, HEX(ffffffff)
863*f0d73e0fSSylvain Deverre        jz    AttrNotFound
864*f0d73e0fSSylvain Deverre        add   cx, word ptr es:[si + 4]  // Add size of the attribute
865*f0d73e0fSSylvain Deverre        add   si, word ptr es:[si + 4]
866*f0d73e0fSSylvain Deverre        jmp   FindAttributeHdrLoop
867*f0d73e0fSSylvain Deverre
868*f0d73e0fSSylvain Deverre    AttrNotFound:
869*f0d73e0fSSylvain Deverre    // Attribute not found, reset the offset
870*f0d73e0fSSylvain Deverre    xor   cx, cx
871*f0d73e0fSSylvain Deverre    AttrFound:
872*f0d73e0fSSylvain Deverre    mov   ax, cx
873*f0d73e0fSSylvain Deverre    pop   edx
874*f0d73e0fSSylvain Deverre    pop   si
875*f0d73e0fSSylvain Deverre    pop   cx
876*f0d73e0fSSylvain Deverre    ret
877*f0d73e0fSSylvain Deverre
878*f0d73e0fSSylvain DeverrePrintFreeldrError:
879*f0d73e0fSSylvain Deverre    mov  si, offset msgFreeldr
880*f0d73e0fSSylvain Deverre    call PutChars
881*f0d73e0fSSylvain Deverre    jmp  Reboot
882*f0d73e0fSSylvain Deverre
883*f0d73e0fSSylvain DeverreFreeLdr:
884*f0d73e0fSSylvain Deverre    .word 'F', 'R', 'E', 'E', 'L', 'D', 'R', '.', 'S', 'Y', 'S'
885*f0d73e0fSSylvain DeverremsgFreeldr:
886*f0d73e0fSSylvain Deverre    .ascii "FreeLdr not found, cannot boot", CR, LF, NUL
887*f0d73e0fSSylvain DeverremsgLoading:
888*f0d73e0fSSylvain Deverre    .ascii "Loading FreeLoader...", CR, LF, NUL
889*f0d73e0fSSylvain DeverremsgFreeldrCompressed:
890*f0d73e0fSSylvain Deverre    .ascii "freeldr.sys is a sparse, compressed or encrypted file, cannot boot", CR, LF, NUL
891*f0d73e0fSSylvain Deverre.endcode16
892*f0d73e0fSSylvain Deverre
893*f0d73e0fSSylvain DeverreEND
894