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, >16K INDEX record size
37
* 2. Don't support encrypted file
38
* 3. Don't support attribute list, which means root directory can't be too
50
#define MAX_MFT_RECORD_SIZE 1 // 1<<(1+9) = 1024
51
#define MAX_IDX_RECORD_SIZE 5 // 1<<(5+9) = 16384
53
#define LOADSEG_NT 0x2000 // IDX loaded at 2000:0
55
#define MFT_OFFSET 0x2000 // MFT loaded at 0:2000
56
#define MFT_FILE 0x2400 // MFT file at 0:2400
58
#define nt_boot_drive -2(%bp)
59
#define nt_blocksize -4(%bp)
60
#define nt_spc -5(%bp)
61
#define nt_MFT_size -6(%bp)
62
#define nt_ATTR_flag -7(%bp)
63
#define nt_IDX_size -8(%bp)
64
#define nt_MFT_run -12(%bp)
66
#define nt_curr_VCN -16(%bp)
67
#define nt_curr_LCN -20(%bp)
68
#define nt_target_VCN -24(%bp)
69
#define nt_read_count -28(%bp)
70
#define nt_remain_leng -32(%bp)
95
#define NTFS_Large_Structure_Error_Code 1
96
#define NTFS_Corrupt_Error_Code 2
97
#define NTFS_Run_Overflow_Error_Code 3
98
#define NTFS_No_Data_Error_Code 4
99
#define NTFS_Decompress_Error_Code 5
101
//msg_NTFS_Large_Structure_Error:
102
// .ascii "Large structure\0"
103
//msg_NTFS_Corrupt_Error:
104
// .ascii "NTFS corrupted\0"
105
//msg_NTFS_Run_Overflow_Error:
106
// .ascii "Run list overflow\0"
107
//msg_NTFS_No_Data_Error:
108
// .ascii "GRLDR have no data\0"
109
//msg_NTFS_Decompress_Error:
110
// .ascii "Decomp error\0"
119
.byte 0x90 /* for CHS. Another possible value is 0x0e for LBA */
123
.word 0 /* 0B - Bytes per sector */
124
.byte 0 /* 0D - Sectors per cluster */
125
.word 0 /* 0E - reserved sectors, unused */
126
.byte 0 /* 10 - number of FATs, unused */
127
.word 0 /* 11 - Max dir entries for FAT12/FAT16, unused */
128
.word 0 /* 13 - total sectors for FAT12/FAT16, unused */
129
.byte 0xF8 /* 15 - Media descriptor */
130
.word 0 /* 16 - sectors per FAT for FAT12/FAT16, unused */
131
.word 255 /* 18 - Sectors per track */
132
.word 63 /* 1A - Number of heads */
134
.long 0 /* 1C - hidden sectors */
135
.long 0 /* 20 - total sectors for FAT32, unused */
137
/* 24 - Usually 80 00 80 00, A value of 80 00 00 00 has
138
* been seen on a USB thumb drive which is formatted
139
* with NTFS under Windows XP. Note this is removable
140
* media and is not partitioned, the drive as a whole
143
.long 0,0 /* 28 - Number of sectors in the volume */
144
.long 0,0 /* 30 - LCN of VCN 0 of the $MFT */
145
.long 0,0 /* 38 - LCN of VCN 0 of the $MFTMirr */
146
.long 0 /* 40 - Clusters per MFT Record */
147
.long 4 /* 44 - Clusters per Index Record */
148
.long 0,0 /* 48 - Volume serial number */
149
.long 0 /* 50 - Checksum, usually 0 */
160
/* the byte at offset 0x57 stores the real partition number for read.
161
* the format program or the caller should set it to a correct value.
162
* For floppies, it should be 0xff, which stands for whole drive.
165
movb $0xff, %dh /* boot partition number */
172
movw %ax, %ss /* stack and BP-relative moves up, too */
176
movw %dx, nt_boot_drive
178
/* Test if your BIOS support LBA mode */
184
jne 1f /* No EBIOS */
187
/* EBIOS supported */
188
movb $0x42, (ebios_nt - 1 - Entry_nt)(%bp)
191
cmpl $0x42555247, (nt_sector_mark - Entry_nt)(%bp)
192
jz 1f // Must be called from GRLDR.MBR
195
movl (nt_part_ofs - Entry_nt)(%bp), %eax
197
call readDisk_nt // Load the second sector from disk
198
call readDisk_nt // Load the third sector from disk
202
movw 0xb(%bp), %ax // Bytes per sector (blocksize)
203
movw %ax, nt_blocksize
205
call convert_to_power_2
207
movb 0xd(%bp), %al // Sectors per cluster
208
call convert_to_power_2
211
subb $9, %ch // 1<<ch = sectors per cluster
213
movb 0x44(%bp), %al // Index record size (high bits of eax is 0)
216
cmpb $MAX_IDX_RECORD_SIZE, %cl
219
NTFS_Large_Structure_Error:
220
movb $NTFS_Large_Structure_Error_Code, %al
224
movb %cl, nt_IDX_size
226
movb 0x40(%bp), %al // MFT record size
229
cmpb $MAX_MFT_RECORD_SIZE, %cl
230
ja NTFS_Large_Structure_Error
232
movb %cl, nt_MFT_size
237
movb %ch, %cl // ch still contains nt_spc
239
shldl %cl, %eax, %edx
241
jnz NTFS_Large_Structure_Error
244
addl (nt_part_ofs - Entry_nt)(%bp), %eax
247
movb nt_MFT_size, %cl
259
//cmpl $0x454C4946, (%bx) // Check for the "FILE" label
260
cmpw $0x4946, (%bx) // to save space, only test FI
261
jnz NTFS_Corrupt_Error
263
// dx should still contain the number of sectors in the MFT record
267
addw 0x14(%bx), %bx // Jump to the first attribute
268
movb $0x80, %al // find $DATA
271
jc NTFS_Corrupt_Error
275
movb $0, nt_ATTR_flag
277
// Sector one is almost full, jmp to the second sector
281
// Convert the size of MFT and IDX block
291
jmp 2f // Jump to 2 in convert_to_power_2
298
// Convert number to a power of 2
324
movb nt_MFT_size, %cl
326
addl nt_MFT_start, %eax
329
movb nt_MFT_size, %cl
341
cmpl $0x454C4946, (%bx) // Check for the "FILE" label
342
jnz NTFS_Corrupt_Error
344
// dx should still contain the number of sectors in the MFT record
348
//ret // Continue at ntfs_fixup
351
// Fixup the "FILE" and "INDX" record
353
// ES:BX - data buffer
354
// CX - buffer length in sectors
362
movw %es:6(%bx), %ax // Size of Update Sequence
369
jnz NTFS_Corrupt_Error // blocksize * count != size
371
movw %bx, %cx // cx = count
374
addw %es:4(%bx), %bx // Offset to the update sequence
375
movw %es:(%bx), %ax // Update Sequence Number
379
addw nt_blocksize, %di
382
jnz NTFS_Corrupt_Error
392
movb $NTFS_Corrupt_Error_Code, %al
395
/* Read a sector from disk, using LBA or CHS
396
* input: EAX - 32-bit DOS sector number
397
* ES:BX - destination buffer
398
* (will be filled with 1 sector of data)
399
* output: ES:BX points one byte after the last byte read.
406
xorl %edx, %edx /* EDX:EAX = LBA */
407
pushl %edx /* hi 32bit of sector number */
408
pushl %eax /* lo 32bit of sector number */
409
pushw %es /* buffer segment */
410
pushw %bx /* buffer offset */
411
pushw $1 /* 1 sector to read */
412
pushw $16 /* size of this parameter block */
415
pushl 0x18(%bp) /* lo:sectors per track, hi:number of heads */
416
popw %cx /* ECX = sectors per track */
417
divl %ecx /* residue is in EDX */
418
/* quotient is in EAX */
419
incw %dx /* sector number in DL */
420
popw %cx /* ECX = number of heads */
421
pushw %dx /* push sector number into stack */
422
xorw %dx, %dx /* EDX:EAX = cylinder * TotalHeads + head */
423
divl %ecx /* residue is in EDX, head number */
424
/* quotient is in EAX, cylinder number */
425
xchgb %dl, %dh /* head number should be in DH */
427
popw %cx /* pop sector number from stack */
428
xchgb %al, %ch /* lo 8bit cylinder should be in CH */
430
shlb $6, %ah /* hi 2bit cylinder ... */
431
orb %ah, %cl /* ... should be in CL */
433
movw $0x201, %ax /* read 1 sector */
434
ebios_nt: /* ebios_nt - 1 points to 0x02 that can be changed to 0x42 */
436
// cmpb $0x0e, 2(%bp) /* force LBA? */
437
// jnz 1f /* no, continue */
438
// movb $0x42, %ah /* yes, use extended disk read */
440
movw %sp, %si /* DS:SI points to disk address packet */
441
movb nt_boot_drive, %dl /* hard disk drive number */
445
popaw /* remove parameter block from stack */
447
jc disk_error_nt /* disk read error, jc 1f if caller handles */
448
incl %eax /* next sector */
449
addw 0x0b(%bp), %bx /* bytes per sector */
450
jnc 1f /* 64K bound check */
453
addb $0x10, %dh /* add 1000h to ES */
454
/* here, carry is cleared */
458
/* carry stored on disk read error */
461
msg_DiskReadError_nt:
465
msg_NTFS_Not_Found_Error:
471
. = nt_boot_image + 8
476
addb %al, (msg_DiskReadError_nt - Entry_nt)(%bp)
479
// Kernel load address, located at 0x1E8
486
// Boot image offset and length, located at 0x1EE
487
// Lower 11 bit is offset, higher 5 bit is length
491
.word (nt_boot_image - Entry_nt)+(nt_boot_image_end - nt_boot_image-1)*2048
497
movw $(msg_DiskReadError_nt - Entry_nt + 0x7c00), %si
501
/* prints string DS:SI (modifies AX BX SI) */
505
lodsb (%si), %al /* get token */
506
//xorw %bx, %bx /* video page 0 */
507
movb $0x0e, %ah /* print it */
508
int $0x10 /* via TTY mode */
509
cmpb $0, %al /* end of string? */
510
jne 1b /* until done */
512
/* The caller will change this to
513
* ljmp $0x9400, $(try_next_partition - _start1)
522
// Here starts sector #2
525
// %eax = nt_MFT_start
528
movb $5, %al // MFT record for root directory
531
addw 0x14(%bx), %bx // Jump to the first attribute
532
movb $0x90, %al // find $INDEX_ROOT
538
jc NTFS_Not_Found_Error
540
cmpl $0x180400, 8(%si) // resident
542
// name offset = 0x18
544
//testw $0xC001, 12(%si) // not compressed, encrypted or sparse
546
cmpl $0x490024, 0x18(%si) // to save space, only test $ and I
548
//cmpl $0x300033, 0x1C(%si)
549
//jnz 1b // name = "$I30"
552
addw 0x14(%di), %di // di points to index header
554
jnz 1b // test if it index filenames
556
addw $0x10, %di // skip the index root
562
xorw %si, %si // Small index
567
movb $0xA0, %al // find $INDEX_ALLOCATION
572
cmpl $0x400401, 8(%si) // non-resident
574
// name offset = 0x40
576
//testw $0xC001, 12(%si) // not compressed, encrypted or sparse
578
cmpl $0x490024, 0x40(%si) // to save space, only test $ and I
580
//cmpl $0x300033, 0x44(%si)
581
//jnz 1b // name = "$I30"
583
addw 0x20(%si), %si // bx points to data runs
587
addw (%si), %si // skip the index header
589
pushl $0xFFFFFFFF // end mark
598
cmpl $0xFFFFFFFF, %eax
599
jz NTFS_Not_Found_Error
601
lesw (nt_loadseg_off - Entry_nt)(%bp), %bx
606
movb nt_IDX_size, %cl
614
lesw (nt_loadseg_off - Entry_nt)(%bp), %bx
615
//cmpl $0x58444E49, %es:(%bx) // "INDX"
616
cmpw $0x4E49, %es:(%bx) // to save space, only test IN
617
jnz NTFS_Corrupt_Error
626
// .byte 0x21,0x14,0x00,0x01,0x11,0x10,0x18,0x11,0x05,0x15,0x01,0x27,0x11,0x20,0x05
630
NTFS_Not_Found_Error:
631
leaw (msg_NTFS_Not_Found_Error - Entry_nt)(%bp), %si
652
// Try to find GRLDR in the index
654
// ES:SI - points to index entry
663
testb $1, %es: 0xC(%bx)
665
addw %es: 8(%bx), %bx
666
cmpl $0, %es: -4(%bx)
667
jnz NTFS_Large_Structure_Error
668
popw %ax // Return address
669
pushl %es: -8(%bx) // VCN of subnode
673
testb $2, %es: 0xC(%bx)
681
leaw (nt_boot_image - Entry_nt)(%bp), %si
682
//addw %es:0xA(%bx), %bx
683
addw $0x52, %bx // The value at %es:0xA(%bx) is wrong sometimes (0x4C)
684
movb %es:-2(%bx), %cl
692
addb $('a'-'A'), %ah // Convert to lowercase
723
movb nt_MFT_size, %cl
742
lesw (nt_loadseg_off - Entry_nt)(%bp), %bx
755
movw $MFT_OFFSET, %di
769
cmpw $0x4946, (%bx) // to save space, only test FI
770
jnz NTFS_Corrupt_Error
777
// Load data from disk
779
// DI: start of the run list
780
// SI: current run list item
782
// ES:BX: points to buffer
783
// ECX: number of sectors to read
786
movl %ecx, nt_read_count
789
movl %eax, nt_target_VCN
790
xorl %edx, %edx // edx - next VCN
791
movl %edx, nt_curr_LCN
794
call ntfs_runlist_read_block
796
cmpl nt_target_VCN, %edx
803
orl %eax, %eax // sparse
806
movl nt_target_VCN, %eax
807
subl nt_curr_VCN, %eax
808
addl nt_curr_LCN, %eax
811
addl (nt_part_ofs - Entry_nt)(%bp), %eax
817
subl nt_target_VCN, %ebx
822
cmpl nt_read_count, %ecx
824
movl nt_read_count, %ecx
841
subl %ecx, nt_read_count
844
movl %edx, nt_target_VCN
845
call ntfs_runlist_read_block
851
// Read run list data
853
// CL = number of bytes
856
// SI points to the next unhandled byte
858
ntfs_runlist_read_data:
876
sarl %cl, %eax // Don't use shr !
880
NTFS_Run_Overflow_Error:
881
movb $NTFS_Run_Overflow_Error_Code, %al
889
.long 0x42555247 // "GRUB"
891
// Here starts sector #3
893
// Read run list block
896
// SI points to the next unhandled byte
898
ntfs_runlist_read_block:
902
andb $0xF, %cl // cl - Size of length field
903
jz NTFS_Run_Overflow_Error
904
shrb $0x4, %ch // ch - Size of offset field
906
call ntfs_runlist_read_data
908
movl %edx, nt_curr_VCN
912
call ntfs_runlist_read_data
913
addl %eax, nt_curr_LCN
920
//popl %eax // Clear the stack
921
//cmpl $0xFFFFFFFF, %eax
925
jnz NTFS_Large_Structure_Error
927
//movl %es: 0x40(%si), %eax // Real size of file
929
//shrl $9, %eax // Convert to sector
931
movl %es: (%si), %eax
934
addw 0x14(%bx), %bx // Jump to the first attribute
935
movb $0x80, %al // find $DATA
939
jc NTFS_No_Data_Error
941
cmpw $0x1, 8(%si) // non-resident / resident
946
movw 0x10(%si), %cx // Resident
947
lesw (nt_loadseg_off - Entry_nt)(%bp), %di
949
rep movsb (%si), %es:(%di)
954
movb %al, nt_ATTR_flag
955
//testw $0xC001, 12(%si) // not compressed, encrypted or sparse
959
addw 0x20(%si), %di // jump to run list
961
lesw (nt_loadseg_off - Entry_nt)(%bp), %bx
963
movl 0x28(%si), %ecx // Use allocate size instead of real size
971
//movb $1, (do_pause - Entry_nt)(%bp)
974
movw nt_boot_drive, %dx
975
ljmp *(nt_loadseg_off - Entry_nt)(%bp)
978
movb $NTFS_No_Data_Error_Code, %al
981
// Convert seg:ofs to linear address
996
// Convert linear address to seg:ofs
998
// on stack: linear address
1000
// On stack: seg:ofs
1028
// Handle sparse block
1029
// DI: points to run list
1030
// SI: current run list item
1031
// ES:BX: points to buffer
1032
// ECX: number of sectors
1039
shll $9, %ecx // ecx - totel number of bytes
1042
testb $1, nt_ATTR_flag // Not compressed
1046
movb nt_target_VCN, %dl
1054
shll %cl, %edx // edx: offset from the start of cluster
1059
subl %edx, %eax // eax: linear address
1061
movl $16, nt_remain_leng
1062
shll %cl, nt_remain_leng
1067
subl nt_remain_leng, %ecx
1070
call DecompressBlock
1073
addl nt_remain_leng, %ecx
1092
rep stosl %eax, %es:(%di)
1108
// eax: linear address at the beginning of the compressed block
1110
// ES:DI: points to the end of the block
1119
movl nt_remain_leng, %edx
1141
rep movsl (%si), %es:(%di)
1153
xorl %edx, %edx // edx - copied bytes
1159
rep movsw (%si), %es:(%di)
1161
jmp 7f // The block is not compressed
1166
incw %cx // ecx = block length
1167
addw %si, %cx // cx: end marker
1172
ja NTFS_Decompress_Error
1177
movb %al, %bl // bl: tag, bh: count
1201
shrw %cl, %bx // bx: delta
1211
movw %ax, %cx // cx: length
1216
movb %es:(%bx, %di), %al
1217
stosb %al, %es:(%di)
1226
movsb (%si), %es:(%di)
1238
subl %edx, nt_remain_leng // End of block
1250
NTFS_Decompress_Error:
1253
movb $NTFS_Decompress_Error_Code, %al
1260
cmpb $0, (do_pause - Entry_nt)(%bp)
1284
subb $('0'-'A'+10), %al
1296
. = Entry_nt + 0x5fc