Fix building in a separate build directory.
[grub-extras.git] / ntfsbs.S
blob495b92238969d75ba2e092a781f0f428c878f4f3
1 /*
2  *  GRUB Utilities --  Utilities for GRUB Legacy, GRUB2 and GRUB for DOS
3  *  Copyright (C) 2007 Bean (bean123@126.com)
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
20 /* NTFS boot sector for loading GRLDR , written by bean
21  *
22  * This file can be compiled as standaolne boot sector, or it can be embeded in
23  * GRLDR.MBR at 0xA00 , right after the ext2 boot sector
24  *
25  * To compile the standalone ntfsbs.bin:
26  *     gcc -c -o ntfsbs.o ntfsbs.S
27  *     gcc -nostdlib -Wl,-N -Wl,-Ttext -Wl,7C00 -o ntfsbs_exec ntfsbs.o
28  *     objcopy -O binary ntfsbs_exec ntfsbs.bin
29  *
30  * To install the standalone ntfsbs.bin:
31  *     grubinst --restore=ntfsbs.bin DEVICE_OR_FILE
32  *
33  * Where DEVICE_OR_FILE specify a NTFS partition
34  *
35  * Limitations:
36  *  1. Don't support >1K MFT record size, >4K INDEX record size
37  *  2. Don't support encrypted file
38  *  3. Don't support >4K non-resident attribute list and $BITMAP
39  *
40  */
42 #ifndef INSIDE_GRLDR
44         .text
46         .code16
47 #endif
49 #define AT_STANDARD_INFORMATION 0x10
50 #define AT_ATTRIBUTE_LIST       0x20
51 #define AT_FILENAME             0x30
52 #define AT_OBJECT_ID            0x40
53 #define AT_SECURITY_DESCRIPTOR  0x50
54 #define AT_VOLUME_NAME          0x60
55 #define AT_VOLUME_INFORMATION   0x70
56 #define AT_DATA                 0x80
57 #define AT_INDEX_ROOT           0x90
58 #define AT_INDEX_ALLOCATION     0xA0
59 #define AT_BITMAP               0xB0
60 #define AT_SYMLINK              0xC0
61 #define AT_EA_INFORMATION       0xD0
62 #define AT_EA                   0xE0
64 #define MAX_MFT_SIZE    1               // 1<<(1+9) = 1024
65 #define MAX_IDX_SIZE    3               // 1<<(3+9) = 4096
67 #define LOADSEG_NT      0x2000
69 #define MMFT_BASE       0x2000
70 #define MMFT_EMFT       (MMFT_BASE +1024)
71 #define MMFT_EBUF       (MMFT_BASE + 2048)
73 #define CMFT_BASE       (MMFT_BASE + 6144)
74 #define CMFT_EMFT       (CMFT_BASE + 1024)
75 #define CMFT_EBUF       (CMFT_BASE + 2048)
77 #define INDX_BASE       (CMFT_BASE + 6144)
79 #define SBUF_BASE       (INDX_BASE + 4096)
81 #define NTFS_Large_Structure_Error_Code 1
82 #define NTFS_Corrupt_Error_Code         2
83 #define NTFS_Run_Overflow_Error_Code    3
84 #define NTFS_No_Data_Error_Code         4
85 #define NTFS_Decompress_Error_Code      5
87 #define NT_FG_COMP      1
88 #define NT_FG_MMFT      2
89 #define NT_FG_ALST      4
90 #define NT_FG_GPOS      8
92 #define nt_boot_drive   -2(%bp)
93 #define nt_blocksize    -4(%bp)
94 #define nt_spc          -5(%bp)
95 #define nt_mft_size     -6(%bp)
96 #define nt_idx_size     -7(%bp)
97 #define nt_mft_start    -12(%bp)
98 #define nt_remain_len   -16(%bp)
99 //#define nt_file_count -18(%bp)
101 #define nt_flag         (%di)
102 #define nt_attr_cur     2(%di)
103 #define nt_attr_nxt     4(%di)
104 #define nt_attr_end     6(%di)
105 #define nt_curr_vcn     8(%di)
106 #define nt_curr_lcn     0x10(%di)
107 #define nt_attr_ofs     0x14(%di)
108 #define nt_target_vcn   0x18(%di)
109 #define nt_read_count   0x1C(%di)
110 #define nt_vcn_offset   0x20(%di)
112 #define nt_emft_buf     1024(%di)
113 #define nt_edat_buf     2048(%di)
115         .arch   i586
117 Entry_nt:
118         jmp     1f
120         . = Entry_nt + 0x02
122         .byte   0x90    /* for CHS. Another possible value is 0x0e for LBA */
124         .ascii  "NTFS    "
126         .word   0       /* 0B - Bytes per sector */
127         .byte   0       /* 0D - Sectors per cluster */
128         .word   0       /* 0E - reserved sectors, unused */
129         .byte   0       /* 10 - number of FATs, unused */
130         .word   0       /* 11 - Max dir entries for FAT12/FAT16, unused */
131         .word   0       /* 13 - total sectors for FAT12/FAT16, unused */
132         .byte   0xF8    /* 15 - Media descriptor */
133         .word   0       /* 16 - sectors per FAT for FAT12/FAT16, unused */
134         .word   255     /* 18 - Sectors per track */
135         .word   63      /* 1A - Number of heads */
136 nt_part_ofs:
137         .long   0       /* 1C - hidden sectors */
138         .long   0       /* 20 - total sectors for FAT32, unused */
139         .long   0x800080
140                         /* 24 - Usually 80 00 80 00, A value of 80 00 00 00 has
141                          * been seen on a USB thumb drive which is formatted
142                          * with NTFS under Windows XP. Note this is removable
143                          * media and is not partitioned, the drive as a whole
144                          * is NTFS formatted.
145                          */
146         .long   0,0     /* 28 - Number of sectors in the volume */
147         .long   0,0     /* 30 - LCN of VCN 0 of the $MFT */
148         .long   0,0     /* 38 - LCN of VCN 0 of the $MFTMirr */
149         .long   0       /* 40 - Clusters per MFT Record */
150         .long   4       /* 44 - Clusters per Index Record */
151         .long   0,0     /* 48 - Volume serial number */
152         .long   0       /* 50 - Checksum, usually 0 */
156         . = Entry_nt + 0x54
158         cli
159         cld
161         . = Entry_nt + 0x56
163         /* the byte at offset 0x57 stores the real partition number for read.
164          * the format program or the caller should set it to a correct value.
165          * For floppies, it should be 0xff, which stands for whole drive.
166          */
168         movb    $0xff, %dh      /* boot partition number */
170         xorw    %ax, %ax
171         movw    %ax, %ds
172         movw    $0x7c00, %bp
173         movw    %ax, %es
175         movw    %ax, %ss        /* stack and BP-relative moves up, too */
176         leaw    -0x20(%bp), %sp
177         sti
179         movw    %dx, nt_boot_drive
181         /* Test if your BIOS support LBA mode */
182         movb    $0x41, %ah
183         movw    $0x55AA, %bx
184         int     $0x13
185         jc      1f              /* No EBIOS */
186         cmpw    $0xAA55, %bx
187         jne     1f              /* No EBIOS */
188         testb   $1, %cl
189         jz      1f              /* No EBIOS */
190         /* EBIOS supported */
191         movb    $0x42, (ebios_nt - 1 - Entry_nt)(%bp)
194         cmpl    $0x42555247, (nt_sector_mark - Entry_nt)(%bp)
195         jz      1f                      // Must be called from GRLDR.MBR
197         movw    $0x7E00, %bx
198         movl    (nt_part_ofs - Entry_nt)(%bp), %eax
199         incl    %eax
200         call    readDisk_nt             // Load the second sector from disk
201         call    readDisk_nt             // Load the third sector from disk
202         call    readDisk_nt
205         xorl    %eax, %eax
206         movw    0xb(%bp), %ax           // Bytes per sector (blocksize)
207         movw    %ax, nt_blocksize
209         call    convert_to_power_2
210         movb    %cl, %bl
211         movb    0xd(%bp), %al           // Sectors per cluster
212         call    convert_to_power_2
213         movb    %cl, %ch
214         addb    %bl, %ch
215         subb    $9, %ch                 // 1<<ch = sectors per cluster
216         movb    %ch, nt_spc
217         movb    0x44(%bp), %al          // Index record size (high bits of eax is 0)
218         call    convert_size
220         cmpb    $MAX_IDX_SIZE, %cl
221         jbe     1f
223 NTFS_Large_Structure_Error:
224         movb    $NTFS_Large_Structure_Error_Code, %al
225         jmp     NTFS_Error
228         movb    %cl, nt_idx_size
230         movb    0x40(%bp), %al          // MFT record size
231         call    convert_size
233         cmpb    $MAX_MFT_SIZE, %cl
234         jnz     NTFS_Large_Structure_Error
236         movb    %cl, nt_mft_size
238         movl    0x30(%bp), %eax
239         movl    0x34(%bp), %edx
241         movb    %ch, %cl                // ch still contains nt_spc
243         shldl   %cl, %eax, %edx
244         orl     %edx, %edx
245         jnz     NTFS_Large_Structure_Error
247         shll    %cl, %eax
248         addl    (nt_part_ofs - Entry_nt)(%bp), %eax
249         movl    %eax, nt_mft_start
251         movw    $1, %dx
252         movb    nt_mft_size, %cl
253         shlw    %cl, %dx
254         movw    %dx, %cx
256         movw    $MMFT_BASE, %bx
257         pushw   %bx
259         call    readDisk_nt
260         loop    1b
262         popw    %bx
263         cmpw    $0x4946, (%bx)          // "FI"
264         jnz     NTFS_Corrupt_Error
266         // dx should still contain the number of sectors in the MFT record
267         movw    %dx, %cx
268         call    ntfs_fixup
270         movw    %bx, %di
271         movb    $AT_DATA, %al           // find $DATA
273         call    ntfs_locate_attr
274         jc      NTFS_Corrupt_Error
276         movw    $CMFT_BASE, %bx
277         xorl    %eax, %eax
278         movb    $0x5, %al
279         call    ntfs_read_mft
280         movw    %bx, %di
282         jmp     ntfs_search
284 // Convert the size of MFT and IDX block
285 // Input:
286 //     eax: size
287 //     ch: spc
288 // Output:
289 //     cl: convert value
290 convert_size:
291         orb     %al, %al
292         js      1f
293         movb    %ch, %cl
294         jmp     2f                      // Jump to 2 in convert_to_power_2
296         negb    %al
297         subb    $9, %al
298         movb    %al, %cl
299         ret
301 // Convert number to a power of 2
302 // Input:
303 //     eax
304 // Output:
305 //     cl: 1<<cl = eax
306 //     eax: 0
308 convert_to_power_2:
309         xorb    %cl, %cl
311         incb    %cl
312         shrl    $1, %eax
313         jnc     2b
314         decb    %cl
315         ret
317 // Fixup the "FILE" and "INDX" record
318 // Input:
319 //     DS:BX - data buffer
320 //     CX - buffer length in sectors
323 ntfs_fixup:
324         push    %bx
325         push    %di
326         movw    %bx, %di
328         movw    6(%bx), %ax             // Size of Update Sequence
329         decw    %ax
330         movw    %ax, %bx
332         mulw    nt_blocksize
333         shlw    $9, %cx
334         cmpw    %ax, %cx
335         jnz     NTFS_Corrupt_Error      // blocksize * count != size
337         movw    %bx, %cx                // cx = count
339         movw    %di, %bx
340         addw    4(%bx), %bx             // Offset to the update sequence
341         movw    (%bx), %ax              // Update Sequence Number
342         subw    $2, %di
345         addw    nt_blocksize, %di
346         addw    $2, %bx
347         cmpw    (%di), %ax
348         jnz     NTFS_Corrupt_Error
349         movw    (%bx), %dx
350         movw    %dx, (%di)
351         loop    1b
353         popw    %di
354         popw    %bx
355         ret
357 NTFS_Corrupt_Error:
358         movb    $NTFS_Corrupt_Error_Code, %al
359         jmp     NTFS_Error
361 /* Read a sector from disk, using LBA or CHS
362  * input:       EAX - 32-bit DOS sector number
363  *              ES:BX - destination buffer
364  *              (will be filled with 1 sector of data)
365  * output:      ES:BX points one byte after the last byte read.
366  *              EAX - next sector
367  */
369 readDisk_nt:
371         pushal
372         xorl    %edx, %edx      /* EDX:EAX = LBA */
373         pushl   %edx            /* hi 32bit of sector number */
374         pushl   %eax            /* lo 32bit of sector number */
375         pushw   %es             /* buffer segment */
376         pushw   %bx             /* buffer offset */
377         pushw   $1              /* 1 sector to read */
378         pushw   $16             /* size of this parameter block */
380         xorl    %ecx, %ecx
381         pushl   0x18(%bp)       /* lo:sectors per track, hi:number of heads */
382         popw    %cx             /* ECX = sectors per track */
383         divl    %ecx            /* residue is in EDX */
384                                 /* quotient is in EAX */
385         incw    %dx             /* sector number in DL */
386         popw    %cx             /* ECX = number of heads */
387         pushw   %dx             /* push sector number into stack */
388         xorw    %dx, %dx        /* EDX:EAX = cylinder * TotalHeads + head */
389         divl    %ecx            /* residue is in EDX, head number */
390                                 /* quotient is in EAX, cylinder number */
391         xchgb   %dl, %dh        /* head number should be in DH */
392                                 /* DL = 0 */
393         popw    %cx             /* pop sector number from stack */
394         xchgb   %al, %ch        /* lo 8bit cylinder should be in CH */
395                                 /* AL = 0 */
396         shlb    $6, %ah         /* hi 2bit cylinder ... */
397         orb     %ah, %cl        /* ... should be in CL */
399         movw    $0x201, %ax     /* read 1 sector */
400 ebios_nt: /* ebios_nt - 1 points to 0x02 that can be changed to 0x42 */
402 //      cmpb    $0x0e, 2(%bp)   /* force LBA? */
403 //      jnz     1f              /* no, continue */
404 //      movb    $0x42, %ah      /* yes, use extended disk read */
405 //1:
406         movw    %sp, %si        /* DS:SI points to disk address packet */
407         movb    nt_boot_drive, %dl      /* hard disk drive number */
409         int     $0x13
411         popaw                   /* remove parameter block from stack */
412         popal
413         jc      disk_error_nt   /* disk read error, jc 1f if caller handles */
414         incl    %eax            /* next sector */
415         addw    0x0b(%bp), %bx  /* bytes per sector */
416         jnc     1f              /* 64K bound check */
417         pushw   %dx
418         movw    %es, %dx
419         addb    $0x10, %dh      /* add 1000h to ES */
420                                 /* here, carry is cleared */
421         movw    %dx, %es
422         popw    %dx
424         /* carry stored on disk read error */
425         ret
427 msg_DiskReadError_nt:
429         .ascii  "0\0"
431 msg_NTFS_Not_Found_Error:
432         .ascii "No "
434 nt_boot_image:
435         .ascii "grldr\0"
437         . = nt_boot_image + 8
439 nt_boot_image_end:
441 NTFS_Error:
442         addb    %al, (msg_DiskReadError_nt - Entry_nt)(%bp)
443         jmp     disk_error_nt
445 // Kernel load address, located at 0x1E8
446         . = Entry_nt + 0x1e8
448 nt_loadseg_off:
449         .word   0
450         .word   LOADSEG_NT
452 // Boot image offset and length, located at 0x1EE
453 // Lower 11 bit is offset, higher 5 bit is length
454         . = Entry_nt + 0x1ec
456 nt_boot_image_ofs:
457         .word (nt_boot_image - Entry_nt)+(nt_boot_image_end - nt_boot_image-1)*2048
459         . = Entry_nt + 0x1ee
461 disk_error_nt:
463         movw    $(msg_DiskReadError_nt - Entry_nt + 0x7c00), %si
465 boot_error_nt:
467 /* prints string DS:SI (modifies AX BX SI) */
469 //print_32:
471         lodsb   (%si), %al      /* get token */
472         //xorw  %bx, %bx        /* video page 0 */
473         movb    $0x0e, %ah      /* print it */
474         int     $0x10           /* via TTY mode */
475         cmpb    $0, %al         /* end of string? */
476         jne     1b              /* until done */
478         /* The caller will change this to
479          *      ljmp    $0x9400, $(try_next_partition - _start1)
480          */
482 1:      jmp     1b
484         . = Entry_nt + 0x1fc
486         .word   0, 0xAA55
488 // Here starts sector #2
490 // Input:
491 //     DI - current mft
492 ntfs_search:
493         //movw  $0, nt_file_count
494         call    ntfs_init_attr
495         movb    $AT_INDEX_ROOT, %al
498         call    ntfs_find_attr
499         jc      NTFS_Not_Found_Error
501         cmpl    $0x180400,  8(%si)      // resident
502                                         // namelen = 4
503                                         // name offset = 0x18
504         jnz     1b
505         //cmpl  $0x490024, 0x18(%si)    // "$I"
506         //jnz   1b
507         //cmpl  $0x300033, 0x1C(%si)
508         //jnz   1b                      // "30"
509         //testw $0xC001, 12(%si)        // not compressed, encrypted or sparse
510         //jnz   1b
512         addw    0x14(%si), %si          // jump to attribute
513         cmpb    $0x30, (%si)
514         jnz     1b                      // test if it index filenames
516         addw    $0x10, %si              // skip the index root
517         addw    (%si), %si
519         call    ntfs_find_grldr
520         jnc     ntfs_final
522         call    ntfs_init_attr
523         movb    $AT_BITMAP, %al
525         call    ntfs_find_attr
526         jc      NTFS_Not_Found_Error
527         movw    9(%si), %bx
528         cmpb    $4, %bl
529         jnz     1b
530         //shrw  $4, %bx
531         //cmpl  $0x490024, (%bx, %si)   // "$I"
532         //jnz   1b
533         cmpb    $0, 8(%si)
534         jnz     1f
535         pushw   0x10(%si)
536         addw    0x14(%si), %si
537         pushw   %si
538         jmp     2f
540         pushw   0x30(%si)
541         xorl    %edx, %edx
542         movl    0x28(%si), %ecx
543         cmpw    $4096, %cx
544         ja      NTFS_Not_Found_Error
545         shrl    $9, %ecx
546         movw    $SBUF_BASE, %bx
547         pushw   %bx
548         call    ntfs_read_data
551         movb    $AT_INDEX_ALLOCATION, %al
554         call    ntfs_locate_attr
555         jc      NTFS_Not_Found_Error
557         cmpl    $0x400401, 8(%si)       // non-resident
558                                         // namelen = 4
559                                         // name offset = 0x40
560         jnz     1b
561         //cmpl  $0x490024, 0x40(%si)    // "$I"
562         //jnz   1b
563         //cmpl  $0x300033, 0x44(%si)
564         //jnz   1b                      // "30"
565         //testw $0xC001, 12(%si)        // not compressed, encrypted or sparse
566         //jnz   1b
568         movb    nt_idx_size, %cl
569         xorl    %ebx, %ebx
570         movb    $1, %bl
571         shll    %cl, %ebx               // ebx - index size
572         xorl    %edx, %edx              // edx - index offset
575         popw    %si
576         popw    %cx
579         pushw   %cx
580         lodsb   (%si), %al
582         movw    $8, %cx
584         pushw   %cx
585         pushw   %ax
586         testb   $1, %al
587         jz      3f
588         pushw   %si
589         pushl   %edx
590         pushl   %ebx
592         movl    %ebx, %ecx
593         movw    $INDX_BASE, %bx
594         call    ntfs_read_attr
595         jc      NTFS_Not_Found_Error
596         cmpw    $0x4E49, (%bx)          // "IN"
597         jnz     NTFS_Not_Found_Error
598         call    ntfs_fixup
599         movw    %bx, %si
600         addw    $0x18, %si
601         addw    (%si), %si
603         call    ntfs_find_grldr
604         jnc     ntfs_final_0
606         popl    %ebx
607         popl    %edx
608         popw    %si
611         addl    %ebx, %edx
613         popw    %ax
614         shrb    $1, %al
615         popw    %cx
616         loop    2b
618         popw    %cx
619         loop    1b
621         //pushw nt_file_count
622         //call  hex_out
624 NTFS_Not_Found_Error:
625         leaw    (msg_NTFS_Not_Found_Error - Entry_nt)(%bp), %si
626         jmp     boot_error_nt
628 ntfs_final_0:
629         //addw  $16, %sp
631 // Input:
632 //     DI - current mft
633 //     SI - index entry
634 ntfs_final:
635         cmpw    $0, 4(%si)
636         jnz     NTFS_Large_Structure_Error
638         movl    (%si), %eax
639         movw    %di, %bx
640         call    ntfs_read_mft
642         movb    $AT_DATA, %al
643         call    ntfs_locate_attr
644         jc      NTFS_No_Data_Error
646         cmpb    $1, 8(%si)              // non-resident / resident
647         jz      1f
649         movw    0x10(%si), %cx          // Resident
650         lesw    (nt_loadseg_off - Entry_nt)(%bp), %di
651         addw    0x14(%si), %si
652         rep     movsb   (%si), %es:(%di)
653         jmp     2f
657         xorl    %edx, %edx
658         movl    0x28(%si), %ecx         // Use allocate size instead of real size
659         shrl    $9, %ecx
661         lesw    (nt_loadseg_off - Entry_nt)(%bp), %bx
662         call    ntfs_read_data
667         //movb  $1, (do_pause - Entry_nt)(%bp)
668         //call  pause
670         movw    nt_boot_drive, %dx
671         ljmp    *(nt_loadseg_off - Entry_nt)(%bp)
673 NTFS_No_Data_Error:
674         movb    $NTFS_No_Data_Error_Code, %al
675         jmp     NTFS_Error
677 // Try to find GRLDR in the index
678 // Input:
679 //     DS:SI - points to index entry
680 // Output:
681 //     CF - status
683 ntfs_find_grldr:
684         movw    %si, %bx
685         testb   $2, 0xC(%bx)
686         jz      1f
687         stc
688         ret
690         //incw  nt_file_count
692         xorb    %ch, %ch
694         pushw   %si
695         leaw    (nt_boot_image - Entry_nt)(%bp), %si
696         addw    $0x52, %bx              // The value at 0xA(%bx) is wrong sometimes (0x4C)
697         movb    -2(%bx), %cl
699         lodsb   (%si), %al
700         movb    (%bx), %ah
701         cmpb    $'A', %ah
702         jb      2f
703         cmpb    $'Z', %ah
704         ja      2f
705         addb    $('a'-'A'), %ah         // Convert to lowercase
708         cmpb    %ah, %al
709         jnz     3f                      // Not match
711         incw    %bx
712         incw    %bx
713         loop    1b
715         cmpb    $0,(%si)
716         jnz     3f
718         popw    %si
719         clc
720         ret                             // Match found
724         popw    %si
725         addw    8(%si), %si
727         jmp     ntfs_find_grldr
729 // Locate an attribute
730 // Input:
731 //     DI - pointer to buffer
732 //     AL - attribute
733 ntfs_locate_attr:
734         call    ntfs_init_attr
735         call    ntfs_find_attr
736         jc      1f
738         testb   $NT_FG_ALST, nt_flag
739         jnz     2f
740         call    ntfs_find_attr
741         jnc     2b
742         call    ntfs_init_attr
743         call    ntfs_find_attr
745         clc
747         ret
749 // Prepare to find attribute
750 // Input:
751 //     DI - pointer to buffer
752 ntfs_init_attr:
753         pushw   %ax
754         xorw    %ax, %ax
755         movw    %ax, nt_flag
756         movw    %ax, nt_attr_end
757         movw    nt_attr_ofs, %ax
758         addw    %di, %ax
759         movw    %ax, nt_attr_nxt
760         popw    %ax
761         cmpw    $MMFT_BASE, %di
762         jnz     1f
763         orb     $NT_FG_MMFT, nt_flag
765         ret
767 // Find an attribute
768 // Input:
769 //     DI - pointer to buffer
770 //     AL - attribute
771 // Output:
772 //     SI - current item
773 //     CF - status
774 ntfs_find_attr:
775         movw    nt_attr_nxt, %bx
776         testb   $NT_FG_ALST, nt_flag
777         jnz     6f
779         movw    %bx, %si
780         cmpb    $0xFF, (%si)
781         jz      3f
783         cmpb    $AT_ATTRIBUTE_LIST, (%si)
784         jnz     2f
785         movw    %si, nt_attr_end
787         addw    4(%bx), %bx
788         cmpb    %al, (%si)
789         jnz     1b
790         movw    %bx, nt_attr_nxt
791         movw    %si, nt_attr_cur
793         ret
795         cmpw    $1, nt_attr_end
796         jb      2b
797         movw    nt_attr_end, %si
798         cmpb    $0, 8(%si)
799         jnz     4f
800         movw    %si, %bx
801         addw    0x14(%bx), %bx
802         addw    4(%si), %si
803         jmp     5f
805         movl    0x28(%si), %ecx
806         shrl    $9, %ecx
807         cmpw    $8, %cx
808         ja      NTFS_Corrupt_Error
809         leaw    nt_edat_buf, %bx
810         pushw   %ax
811         xorl    %edx, %edx
812         call    ntfs_read_data
813         popw    %ax
814         jc      2b
815         movw    0x30(%si), %si
816         addw    %bx, %si
818         movw    %si, nt_attr_end
819         orb     $NT_FG_ALST, nt_flag
820         testb   $NT_FG_MMFT, nt_flag
821         jz      6f
822         call    ntfs_fix_mmft
824         movw    %bx, %si
825         cmpw    nt_attr_end, %bx
826         jb      1f
828         stc
829         ret
831         addw    4(%bx), %bx
832         cmpb    %al, (%si)
833         jnz     6b
835         pushw   %ax
836         pushw   %es
837         pushw   %ds
838         popw    %es
839         movw    %si, nt_attr_cur
840         movw    %bx, nt_attr_nxt
841         movl    0x10(%si), %eax
842         leaw    nt_emft_buf, %bx
843         testb   $NT_FG_MMFT, nt_flag
844         jnz     2f
845         call    ntfs_read_mft
846         jmp     3f
848         pushw   %bx
849         call    readDisk_nt
850         movl    0x14(%si), %eax
851         call    readDisk_nt
852         popw    %bx
853         cmpw    $0x4946, (%bx)                  // "FI"
854         jnz     NTFS_Corrupt_Error
855         movw    $2, %cx
856         call    ntfs_fixup
858         popw    %es
859         popw    %ax
860         addw    0x14(%bx), %bx
862         cmpb    $0xFF, (%bx)
863         jz      7b
864         cmpb    %al, (%bx)
865         jz      5f
866         addw    4(%bx), %bx
867         jmp     4b
869         movw    %bx, %si
870         ret
872 // Fix $MFT
873 // Input:
874 //     DI - pointer to buffer
875 //     BX - attr cur
876 ntfs_fix_mmft:
877         pushw   %ax
878         orb     $NT_FG_GPOS, nt_flag
881         cmpw    nt_attr_end, %bx
882         jae     NTFS_Corrupt_Error
883         cmpb    %al, (%bx)
884         jz      2f
885         addw    4(%bx), %bx
886         jmp     1b
889         movw    %bx, nt_attr_cur
891         movl    nt_mft_start, %eax
892         movl    %eax, 0x10(%bx)
893         incl    %eax
894         movl    %eax, 0x14(%bx)
896         addw    4(%bx), %bx
898         cmpw    nt_attr_end, %bx
899         jae     2f
900         cmpb    $AT_DATA, (%bx)
901         jnz     2f
903         movl    0x10(%bx), %edx
904         movb    nt_mft_size, %cl
905         shll    %cl, %edx
907         call    ntfs_read_attr
909         orl     %eax, %eax
910         jz      NTFS_Corrupt_Error
911         movl    %eax, 0x10(%bx)
912         movl    %edx, 0x14(%bx)
913         jmp     1b
915         movw    nt_attr_cur, %bx
916         andb    $(~NT_FG_GPOS), nt_flag
917         popw    %ax
919         ret
921 // Read MFT record
922 // Input:
923 //     DS:BX - buffer
924 //     EAX - mft number
925 ntfs_read_mft:
926         pushw   %di
927         movw    $MMFT_BASE, %di
928         movb    nt_mft_size, %cl
929         shll    %cl, %eax
930         movl    %eax, %edx
931         movl    $1, %eax
932         shll    %cl, %eax
933         movl    %eax, %ecx
934         call    ntfs_read_attr
935         jc      NTFS_Corrupt_Error
936         cmpw    $0x4946, (%bx)                  // "FI"
937         jnz     NTFS_Corrupt_Error
938         call    ntfs_fixup
939         popw    %di
940         ret
942 // Read attribute
943 // Input:
944 //     DI - pointer to buffer
945 //     ES:BX - buffer
946 //     EDX - start sector
947 //     ECX - sector count
948 // Output:
949 //     CF - status
950 ntfs_read_attr:
951         pushw   nt_attr_cur
952         pushl   %edx
953         pushl   %ecx
954         pushw   %bx
956         movw    nt_attr_cur, %si
957         movb    (%si), %al
959         testb   $NT_FG_ALST, nt_flag
960         jz      2f
961         movw    %si, %bx
962         movb    nt_spc, %cl
963         shrl    %cl, %edx
966         cmpw    nt_attr_end, %bx
967         jae     2f
968         cmpb    %al, (%bx)
969         jnz     2f
970         cmpl    %edx, 8(%bx)
971         ja      2f
972         movw    %bx, %si
973         addw    4(%bx), %bx
974         jmp     1b
977         movw    %si, nt_attr_nxt
978         call    ntfs_find_attr
980         popw    %bx
981         popl    %ecx
982         popl    %edx
983         jc      1f
984         call    ntfs_read_data
985         clc
987         popw    nt_attr_cur
988         ret
990 // Read data
991 // Input:
992 //     DI: pointer to buffer
993 //     SI: current item
994 //     ES:BX: buffer
995 //     EDX: start sector
996 //     ECX: sector count
997 ntfs_read_data:
998         pushw   %cx
999         pushw   %bx
1000         testb   $1, 8(%si)
1001         jz      NTFS_Corrupt_Error
1002         movb    0xC(%si), %al
1003         andb    $1, %al
1004         orb     %al, nt_flag
1006         movl    %ecx, nt_read_count
1007         movb    nt_spc, %cl
1009         movl    %edx, %eax
1010         shrl    %cl, %eax
1011         movl    %eax, nt_target_vcn
1012         shll    %cl, %eax
1013         subl    %eax, %edx
1014         movl    %edx, nt_vcn_offset
1016         xorw    %dx, %dx                // edx - next VCN
1017         movl    %edx, nt_curr_lcn
1019         movl    0x10(%si), %edx
1021         addw    0x20(%si), %si
1023         call    ntfs_runlist_read_block
1025         cmpl    nt_target_vcn, %edx
1026         jbe     1b
1028         movb    nt_spc, %cl
1030         orl     %eax, %eax              // sparse
1031         jz      2f
1033         movl    nt_target_vcn, %eax
1034         subl    nt_curr_vcn, %eax
1035         addl    nt_curr_lcn, %eax
1037         shll    %cl, %eax
1038         addl    nt_vcn_offset, %eax
1040         testb   $NT_FG_GPOS, nt_flag
1041         jz      3f
1042         pushl   %eax
1043         incl    %eax
1044         subl    nt_curr_vcn, %edx
1045         addl    nt_curr_lcn, %edx
1046         shll    %cl, %edx
1047         cmpl    %eax, %edx
1048         jnz     4f
1049         pushw   %cx
1050         call    ntfs_runlist_read_block
1051         popw    %cx
1052         movl    nt_curr_lcn, %eax
1053         shll    %cl, %eax
1055         movl    %eax, %edx
1056         popl    %eax
1057         addl    (nt_part_ofs - Entry_nt)(%bp), %edx
1060         addl    (nt_part_ofs - Entry_nt)(%bp), %eax
1063         testb   $NT_FG_GPOS, nt_flag
1064         jnz     1f
1066         pushl   %ebx
1067         movl    %edx, %ebx
1068         subl    nt_target_vcn, %ebx
1069         shll    %cl, %ebx
1070         movl    %ebx, %ecx
1071         popl    %ebx
1073         subl    nt_vcn_offset, %ecx
1074         movl    $0, nt_vcn_offset
1075         cmpl    nt_read_count, %ecx
1076         jbe     2f
1077         movl    nt_read_count, %ecx
1080         pushl   %ecx
1082         orl     %eax, %eax
1083         jnz     3f
1084         call    ntfs_sparse_block
1085         jmp     4f
1088         call    readDisk_nt
1089         loop    3b
1092         popl    %ecx
1093         subl    %ecx, nt_read_count
1094         jbe     1f
1096         movl    %edx, nt_target_vcn
1097         call    ntfs_runlist_read_block
1098         jmp     1b
1101         popw    %bx
1102         popw    %cx
1103         ret
1105 // Read run list data
1106 // Input:
1107 //     CL = number of bytes
1108 // Output:
1109 //     EAX = read bytes
1110 //     SI points to the next unhandled byte
1112 ntfs_runlist_read_data:
1113         pushw   %cx
1114         orb     %cl, %cl
1115         jnz     1f
1116         popw    %cx
1117         xorl    %eax, %eax
1118         ret
1120         lodsb   (%si), %al
1121         rorl    $8, %eax
1122         decb    %cl
1123         jnz     1b
1125         popw    %cx
1126         negb    %cl
1127         add     $4, %cl
1128         shlb    $3, %cl
1129         ret
1131 NTFS_Run_Overflow_Error:
1132         movb    $NTFS_Run_Overflow_Error_Code, %al
1133         jmp     NTFS_Error
1135 // Read run list block
1136 // Output:
1137 //     EDX = Next VCN
1138 //     SI points to the next unhandled byte
1140 ntfs_runlist_read_block:
1141         lodsb   (%si), %al
1142         movb    %al, %cl
1143         movb    %cl, %ch
1144         andb    $0xF, %cl               // cl - Size of length field
1145         jz      1f
1146         shrb    $0x4, %ch               // ch - Size of offset field
1148         call    ntfs_runlist_read_data
1149         shrl    %cl, %eax
1151         movl    %edx, nt_curr_vcn
1152         addl    %eax, %edx
1154         movb    %ch, %cl
1155         call    ntfs_runlist_read_data
1156         sarl    %cl, %eax
1158         addl    %eax, nt_curr_lcn
1160         ret
1163         testb   $NT_FG_ALST, nt_flag
1164         jz      NTFS_Run_Overflow_Error
1166         pushl   %edx
1167         pushw   %bx
1168         movw    nt_attr_cur, %si
1169         movb    (%si), %al
1170         call    ntfs_find_attr
1171         jc      NTFS_Run_Overflow_Error
1172         cmpb    $0, 8(%si)
1173         jz      NTFS_Run_Overflow_Error
1174         movl    $0, nt_curr_lcn
1175         popw    %bx
1176         popl    %edx
1177         addw    0x20(%si), %si
1178         jmp     ntfs_runlist_read_block
1180 // Convert seg:ofs to linear address
1181 // Input:
1182 //     On stack: seg:ofs
1183 // Output:
1184 //     eax:
1185 seg_to_lin:
1186         pushw   %bp
1187         movw    %sp, %bp
1188         xorl    %eax, %eax
1189         xchgw   6(%bp), %ax
1190         shll    $4, %eax
1191         addl    4(%bp), %eax
1192         popw    %bp
1193         ret     $4
1195 // Convert linear address to seg:ofs
1196 // Input:
1197 //     on stack: linear address
1198 // Output:
1199 //     On stack: seg:ofs
1200 lin_to_seg:
1201         pushw   %bp
1202         movw    %sp, %bp
1203         shll    $12, 4(%bp)
1204         shrw    $12, 4(%bp)
1205         popw    %bp
1206         ret
1208 fix_segs:
1209         pushw   %ds
1210         pushw   %si
1211         call    seg_to_lin
1212         pushl   %eax
1213         call    lin_to_seg
1214         popw    %si
1215         popw    %ds
1217 fix_es_di:
1218         pushw   %es
1219         pushw   %di
1220         call    seg_to_lin
1221         pushl   %eax
1222         call    lin_to_seg
1223         popw    %di
1224         popw    %es
1225         ret
1227 // Handle sparse block
1228 //     DI: points to buffer
1229 //     ES:BX: points to buffer
1230 //     ECX: number of sectors
1231 //     EDX: next VCN
1233 ntfs_sparse_block:
1234         pushw   %di
1235         pushl   %edx
1237         shll    $9, %ecx                // ecx - totel number of bytes
1239         testb   $1, nt_flag             // Not compressed
1240         jz      2f
1242         xorl    %edx, %edx
1243         movb    nt_target_vcn, %dl
1244         andb    $0xF, %dl
1245         jz      2f
1247         movw    %bx, %di
1249         pushw   %cx
1251         movb    nt_spc, %cl
1252         addb    $9, %cl
1253         shll    %cl, %edx               // edx: offset from the start of cluster
1255         push    %es
1256         push    %di
1257         call    seg_to_lin
1258         subl    %edx, %eax              // eax: linear address
1260         movl    $16, nt_remain_len
1261         shll    %cl, nt_remain_len
1263         popw    %cx
1265         addl    %edx, %ecx
1266         subl    nt_remain_len, %ecx
1268         pushl   %ecx
1269         call    ntfs_decomp_block
1270         popl    %ecx
1272         addl    nt_remain_len, %ecx
1274         jecxz   1f
1276         movw    %di, %bx
1279         movw    %bx, %di
1280         movl    %ecx, %edx
1281         xorl    %eax, %eax
1282         movl    %eax, %ecx
1283         call    fix_es_di
1286         movw    $0x8000, %cx
1287         cmpl    %edx, %ecx
1288         jbe     4f
1289         movw    %dx, %cx
1291         pushw   %cx
1292         shrw    $2, %cx
1294         rep     stosl   %eax, %es:(%di)
1295         call    fix_es_di
1296         popw    %cx
1297         subl    %ecx, %edx
1298         jnz     3b
1301         movw    %di, %bx
1303         popl    %edx
1304         popw    %di
1306         ret
1308 // Decompress block
1309 // Input:
1310 //     eax: linear address at the beginning of the compressed block
1311 // Output:
1312 //     ES:DI: points to the end of the block
1313 ntfs_decomp_block:
1314         pushw   %ds
1315         pushw   %si
1317         pushl   %eax
1318         call    lin_to_seg
1319         popw    %si
1320         popw    %ds
1321         movl    nt_remain_len, %edx
1322         addl    %edx, %eax
1323         pushl   %eax
1324         call    lin_to_seg
1325         popw    %di
1326         popw    %es
1328         pushw   %es
1329         pushw   %di
1330         pushw   %ds
1331         pushw   %si
1333         xorl    %ecx, %ecx
1336         movw    $0x8000, %cx
1337         cmpl    %edx, %ecx
1338         jbe     2f
1339         movw    %dx, %cx
1341         pushw   %cx
1342         shrw    $2, %cx
1343         rep     movsl   (%si), %es:(%di)
1344         call    fix_segs
1345         popw    %cx
1346         subl    %ecx, %edx
1347         jnz     1b
1349         popw    %di
1350         popw    %es
1351         popw    %si
1352         popw    %ds
1355         xorl    %edx, %edx                      // edx - copied bytes
1357         lodsw   (%si), %ax
1358         testb   $0x80, %ah
1359         jnz     2f
1360         movw    $0x800, %cx
1361         rep     movsw   (%si), %es:(%di)
1362         movw    $0x1000, %dx
1363         jmp     7f                              // The block is not compressed
1366         movw    %ax, %cx
1367         andw    $0xFFF, %cx
1368         incw    %cx                             // ecx = block length
1369         addw    %si, %cx                        // cx: end marker
1370         xorb    %bh, %bh
1373         cmpw    $0x1000, %dx
1374         ja      NTFS_Decompress_Error
1376         orb     %bh, %bh
1377         jnz     4f
1378         lodsb   (%si), %al
1379         movb    %al, %bl                        // bl: tag, bh: count
1380         movb    $8, %bh
1383         testb   $1, %bl
1384         jz      5f
1386         movw    %dx, %ax
1387         decw    %ax
1389         pushw   %cx
1390         pushw   %bx
1392         movb    $12, %cl
1394         cmpw    $0x10, %ax
1395         jb      6f
1396         shrw    $1, %ax
1397         decb    %cl
1398         jmp     6b
1401         lodsw   (%si), %ax
1402         movw    %ax, %bx
1403         shrw    %cl, %bx                        // bx: delta
1405         pushw   %dx
1406         movw    $1, %dx
1407         shlw    %cl, %dx
1408         decw    %dx
1409         andw    %dx, %ax
1410         popw    %dx
1412         addw    $3, %ax
1413         movw    %ax, %cx                        // cx: length
1414         negw    %bx
1415         decw    %bx
1418         movb    %es:(%bx, %di), %al
1419         stosb   %al, %es:(%di)
1420         incw    %dx
1421         loop    6b
1423         popw    %bx
1424         popw    %cx
1425         jmp     4f
1428         movsb   (%si), %es:(%di)
1429         incw    %dx
1431         shrb    $1, %bl
1432         decb    %bh
1434         cmpw    %cx, %si
1435         jb      3b
1438         call    fix_segs
1440         subl    %edx, nt_remain_len     // End of block
1441         jz      1f
1443         cmpw    $0x1000, %dx
1444         je      1b
1448         popw    %si
1449         popw    %ds
1450         ret
1452 NTFS_Decompress_Error:
1453         pushw   %ss
1454         popw    %ds
1455         movb    $NTFS_Decompress_Error_Code, %al
1456         jmp     NTFS_Error
1459 do_pause:
1460         .byte   0
1462 pause:
1463         cmpb    $0, (do_pause - Entry_nt)(%bp)
1464         jnz     1f
1465         ret
1467         xorw    %bp, %bp
1469         jmp     1b
1473 hex_out:
1474         pushw   %bp
1475         movw    %sp, %bp
1476         pushaw
1477         movb    $0xE, %ah
1478         movw    $7, %bx
1479         movw    $4, %cx
1480         movw    4(%bp), %dx
1482         rol     $4, %dx
1483         movb    %dl, %al
1484         andb    $0xF, %al
1485         cmpb    $10, %al
1486         jb      2f
1487         subb    $('0'-'A'+10), %al
1489         addb    $'0', %al
1490         int     $0x10
1491         loop    1b
1492         movb    $' ', %al
1493         int     $0x10
1494         popaw
1495         popw    %bp
1496         ret     $2
1499         . = Entry_nt + 0x7fc
1501 nt_sector_mark:
1502         .long   0x42555247              // "GRUB"