2 * GRUB Utilities -- Utilities for GRUB Legacy, GRUB2 and GRUB for DOS
3 * Copyright (C) 2007 Bean (bean123@126.com)
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.
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.
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.
20 /* NTFS boot sector for loading GRLDR , written by bean
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
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
30 * To install the standalone ntfsbs.bin:
31 * grubinst --restore=ntfsbs.bin DEVICE_OR_FILE
33 * Where DEVICE_OR_FILE specify a NTFS partition
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
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
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
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
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)
122 .byte 0x90 /* for CHS. Another possible value is 0x0e for LBA */
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 */
137 .long 0 /* 1C - hidden sectors */
138 .long 0 /* 20 - total sectors for FAT32, unused */
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
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 */
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.
168 movb $0xff, %dh /* boot partition number */
175 movw %ax, %ss /* stack and BP-relative moves up, too */
179 movw %dx, nt_boot_drive
181 /* Test if your BIOS support LBA mode */
187 jne 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
198 movl (nt_part_ofs - Entry_nt)(%bp), %eax
200 call readDisk_nt // Load the second sector from disk
201 call readDisk_nt // Load the third sector from disk
206 movw 0xb(%bp), %ax // Bytes per sector (blocksize)
207 movw %ax, nt_blocksize
209 call convert_to_power_2
211 movb 0xd(%bp), %al // Sectors per cluster
212 call convert_to_power_2
215 subb $9, %ch // 1<<ch = sectors per cluster
217 movb 0x44(%bp), %al // Index record size (high bits of eax is 0)
220 cmpb $MAX_IDX_SIZE, %cl
223 NTFS_Large_Structure_Error:
224 movb $NTFS_Large_Structure_Error_Code, %al
228 movb %cl, nt_idx_size
230 movb 0x40(%bp), %al // MFT record size
233 cmpb $MAX_MFT_SIZE, %cl
234 jnz NTFS_Large_Structure_Error
236 movb %cl, nt_mft_size
241 movb %ch, %cl // ch still contains nt_spc
243 shldl %cl, %eax, %edx
245 jnz NTFS_Large_Structure_Error
248 addl (nt_part_ofs - Entry_nt)(%bp), %eax
249 movl %eax, nt_mft_start
252 movb nt_mft_size, %cl
263 cmpw $0x4946, (%bx) // "FI"
264 jnz NTFS_Corrupt_Error
266 // dx should still contain the number of sectors in the MFT record
271 movb $AT_DATA, %al // find $DATA
273 call ntfs_locate_attr
274 jc NTFS_Corrupt_Error
284 // Convert the size of MFT and IDX block
294 jmp 2f // Jump to 2 in convert_to_power_2
301 // Convert number to a power of 2
317 // Fixup the "FILE" and "INDX" record
319 // DS:BX - data buffer
320 // CX - buffer length in sectors
328 movw 6(%bx), %ax // Size of Update Sequence
335 jnz NTFS_Corrupt_Error // blocksize * count != size
337 movw %bx, %cx // cx = count
340 addw 4(%bx), %bx // Offset to the update sequence
341 movw (%bx), %ax // Update Sequence Number
345 addw nt_blocksize, %di
348 jnz NTFS_Corrupt_Error
358 movb $NTFS_Corrupt_Error_Code, %al
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.
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 */
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 */
393 popw %cx /* pop sector number from stack */
394 xchgb %al, %ch /* lo 8bit cylinder should be in CH */
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 */
406 movw %sp, %si /* DS:SI points to disk address packet */
407 movb nt_boot_drive, %dl /* hard disk drive number */
411 popaw /* remove parameter block from stack */
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 */
419 addb $0x10, %dh /* add 1000h to ES */
420 /* here, carry is cleared */
424 /* carry stored on disk read error */
427 msg_DiskReadError_nt:
431 msg_NTFS_Not_Found_Error:
437 . = nt_boot_image + 8
442 addb %al, (msg_DiskReadError_nt - Entry_nt)(%bp)
445 // Kernel load address, located at 0x1E8
452 // Boot image offset and length, located at 0x1EE
453 // Lower 11 bit is offset, higher 5 bit is length
457 .word (nt_boot_image - Entry_nt)+(nt_boot_image_end - nt_boot_image-1)*2048
463 movw $(msg_DiskReadError_nt - Entry_nt + 0x7c00), %si
467 /* prints string DS:SI (modifies AX BX SI) */
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)
488 // Here starts sector #2
493 //movw $0, nt_file_count
495 movb $AT_INDEX_ROOT, %al
499 jc NTFS_Not_Found_Error
501 cmpl $0x180400, 8(%si) // resident
503 // name offset = 0x18
505 //cmpl $0x490024, 0x18(%si) // "$I"
507 //cmpl $0x300033, 0x1C(%si)
509 //testw $0xC001, 12(%si) // not compressed, encrypted or sparse
512 addw 0x14(%si), %si // jump to attribute
514 jnz 1b // test if it index filenames
516 addw $0x10, %si // skip the index root
526 jc NTFS_Not_Found_Error
531 //cmpl $0x490024, (%bx, %si) // "$I"
544 ja NTFS_Not_Found_Error
551 movb $AT_INDEX_ALLOCATION, %al
554 call ntfs_locate_attr
555 jc NTFS_Not_Found_Error
557 cmpl $0x400401, 8(%si) // non-resident
559 // name offset = 0x40
561 //cmpl $0x490024, 0x40(%si) // "$I"
563 //cmpl $0x300033, 0x44(%si)
565 //testw $0xC001, 12(%si) // not compressed, encrypted or sparse
568 movb nt_idx_size, %cl
571 shll %cl, %ebx // ebx - index size
572 xorl %edx, %edx // edx - index offset
595 jc NTFS_Not_Found_Error
596 cmpw $0x4E49, (%bx) // "IN"
597 jnz NTFS_Not_Found_Error
621 //pushw nt_file_count
624 NTFS_Not_Found_Error:
625 leaw (msg_NTFS_Not_Found_Error - Entry_nt)(%bp), %si
636 jnz NTFS_Large_Structure_Error
643 call ntfs_locate_attr
644 jc NTFS_No_Data_Error
646 cmpb $1, 8(%si) // non-resident / resident
649 movw 0x10(%si), %cx // Resident
650 lesw (nt_loadseg_off - Entry_nt)(%bp), %di
652 rep movsb (%si), %es:(%di)
658 movl 0x28(%si), %ecx // Use allocate size instead of real size
661 lesw (nt_loadseg_off - Entry_nt)(%bp), %bx
667 //movb $1, (do_pause - Entry_nt)(%bp)
670 movw nt_boot_drive, %dx
671 ljmp *(nt_loadseg_off - Entry_nt)(%bp)
674 movb $NTFS_No_Data_Error_Code, %al
677 // Try to find GRLDR in the index
679 // DS:SI - points to index entry
695 leaw (nt_boot_image - Entry_nt)(%bp), %si
696 addw $0x52, %bx // The value at 0xA(%bx) is wrong sometimes (0x4C)
705 addb $('a'-'A'), %ah // Convert to lowercase
729 // Locate an attribute
731 // DI - pointer to buffer
738 testb $NT_FG_ALST, nt_flag
749 // Prepare to find attribute
751 // DI - pointer to buffer
756 movw %ax, nt_attr_end
757 movw nt_attr_ofs, %ax
759 movw %ax, nt_attr_nxt
763 orb $NT_FG_MMFT, nt_flag
769 // DI - pointer to buffer
775 movw nt_attr_nxt, %bx
776 testb $NT_FG_ALST, nt_flag
783 cmpb $AT_ATTRIBUTE_LIST, (%si)
785 movw %si, nt_attr_end
790 movw %bx, nt_attr_nxt
791 movw %si, nt_attr_cur
797 movw nt_attr_end, %si
808 ja NTFS_Corrupt_Error
809 leaw nt_edat_buf, %bx
818 movw %si, nt_attr_end
819 orb $NT_FG_ALST, nt_flag
820 testb $NT_FG_MMFT, nt_flag
825 cmpw nt_attr_end, %bx
839 movw %si, nt_attr_cur
840 movw %bx, nt_attr_nxt
842 leaw nt_emft_buf, %bx
843 testb $NT_FG_MMFT, nt_flag
853 cmpw $0x4946, (%bx) // "FI"
854 jnz NTFS_Corrupt_Error
874 // DI - pointer to buffer
878 orb $NT_FG_GPOS, nt_flag
881 cmpw nt_attr_end, %bx
882 jae NTFS_Corrupt_Error
889 movw %bx, nt_attr_cur
891 movl nt_mft_start, %eax
898 cmpw nt_attr_end, %bx
904 movb nt_mft_size, %cl
910 jz NTFS_Corrupt_Error
915 movw nt_attr_cur, %bx
916 andb $(~NT_FG_GPOS), nt_flag
928 movb nt_mft_size, %cl
935 jc NTFS_Corrupt_Error
936 cmpw $0x4946, (%bx) // "FI"
937 jnz NTFS_Corrupt_Error
944 // DI - pointer to buffer
946 // EDX - start sector
947 // ECX - sector count
956 movw nt_attr_cur, %si
959 testb $NT_FG_ALST, nt_flag
966 cmpw nt_attr_end, %bx
977 movw %si, nt_attr_nxt
992 // DI: pointer to buffer
1001 jz NTFS_Corrupt_Error
1006 movl %ecx, nt_read_count
1011 movl %eax, nt_target_vcn
1014 movl %edx, nt_vcn_offset
1016 xorw %dx, %dx // edx - next VCN
1017 movl %edx, nt_curr_lcn
1019 movl 0x10(%si), %edx
1023 call ntfs_runlist_read_block
1025 cmpl nt_target_vcn, %edx
1030 orl %eax, %eax // sparse
1033 movl nt_target_vcn, %eax
1034 subl nt_curr_vcn, %eax
1035 addl nt_curr_lcn, %eax
1038 addl nt_vcn_offset, %eax
1040 testb $NT_FG_GPOS, nt_flag
1044 subl nt_curr_vcn, %edx
1045 addl nt_curr_lcn, %edx
1050 call ntfs_runlist_read_block
1052 movl nt_curr_lcn, %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
1068 subl nt_target_vcn, %ebx
1073 subl nt_vcn_offset, %ecx
1074 movl $0, nt_vcn_offset
1075 cmpl nt_read_count, %ecx
1077 movl nt_read_count, %ecx
1084 call ntfs_sparse_block
1093 subl %ecx, nt_read_count
1096 movl %edx, nt_target_vcn
1097 call ntfs_runlist_read_block
1105 // Read run list data
1107 // CL = number of bytes
1110 // SI points to the next unhandled byte
1112 ntfs_runlist_read_data:
1131 NTFS_Run_Overflow_Error:
1132 movb $NTFS_Run_Overflow_Error_Code, %al
1135 // Read run list block
1138 // SI points to the next unhandled byte
1140 ntfs_runlist_read_block:
1144 andb $0xF, %cl // cl - Size of length field
1146 shrb $0x4, %ch // ch - Size of offset field
1148 call ntfs_runlist_read_data
1151 movl %edx, nt_curr_vcn
1155 call ntfs_runlist_read_data
1158 addl %eax, nt_curr_lcn
1163 testb $NT_FG_ALST, nt_flag
1164 jz NTFS_Run_Overflow_Error
1168 movw nt_attr_cur, %si
1171 jc NTFS_Run_Overflow_Error
1173 jz NTFS_Run_Overflow_Error
1174 movl $0, nt_curr_lcn
1178 jmp ntfs_runlist_read_block
1180 // Convert seg:ofs to linear address
1182 // On stack: seg:ofs
1195 // Convert linear address to seg:ofs
1197 // on stack: linear address
1199 // On stack: seg:ofs
1227 // Handle sparse block
1228 // DI: points to buffer
1229 // ES:BX: points to buffer
1230 // ECX: number of sectors
1237 shll $9, %ecx // ecx - totel number of bytes
1239 testb $1, nt_flag // Not compressed
1243 movb nt_target_vcn, %dl
1253 shll %cl, %edx // edx: offset from the start of cluster
1258 subl %edx, %eax // eax: linear address
1260 movl $16, nt_remain_len
1261 shll %cl, nt_remain_len
1266 subl nt_remain_len, %ecx
1269 call ntfs_decomp_block
1272 addl nt_remain_len, %ecx
1294 rep stosl %eax, %es:(%di)
1310 // eax: linear address at the beginning of the compressed block
1312 // ES:DI: points to the end of the block
1321 movl nt_remain_len, %edx
1343 rep movsl (%si), %es:(%di)
1355 xorl %edx, %edx // edx - copied bytes
1361 rep movsw (%si), %es:(%di)
1363 jmp 7f // The block is not compressed
1368 incw %cx // ecx = block length
1369 addw %si, %cx // cx: end marker
1374 ja NTFS_Decompress_Error
1379 movb %al, %bl // bl: tag, bh: count
1403 shrw %cl, %bx // bx: delta
1413 movw %ax, %cx // cx: length
1418 movb %es:(%bx, %di), %al
1419 stosb %al, %es:(%di)
1428 movsb (%si), %es:(%di)
1440 subl %edx, nt_remain_len // End of block
1452 NTFS_Decompress_Error:
1455 movb $NTFS_Decompress_Error_Code, %al
1463 cmpb $0, (do_pause - Entry_nt)(%bp)
1487 subb $('0'-'A'+10), %al
1499 . = Entry_nt + 0x7fc
1502 .long 0x42555247 // "GRUB"