forked from KolibriOS/kolibrios
Support GUID Partition Table (GPT) disk layout
git-svn-id: svn://kolibrios.org@6827 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
46ae9b5169
commit
34df6be0da
kernel/trunk
@ -216,6 +216,50 @@ struct PARTITION_TABLE_ENTRY
|
||||
; Length of the partition in sectors.
|
||||
ends
|
||||
|
||||
; GUID Partition Table Header, UEFI 2.6, Table 18
|
||||
struct GPTH
|
||||
Signature rb 8
|
||||
; 'EFI PART'
|
||||
Revision dd ?
|
||||
; 0x00010000
|
||||
HeaderSize dd ?
|
||||
; Size of this header in bytes, must fit to one sector.
|
||||
HeaderCRC32 dd ?
|
||||
; Set this field to zero, compute CRC32 via 0xEDB88320, compare.
|
||||
Reserved dd ?
|
||||
; Myst be zero.
|
||||
MyLBA dq ?
|
||||
; LBA of the sector containing this GPT header.
|
||||
AlternateLBA dq ?
|
||||
; LBA of the sector containing the other GPT header.
|
||||
; AlternateLBA of Primary GPTH points to Backup one and vice versa.
|
||||
FirstUsableLBA dq ?
|
||||
; Only sectors between first and last UsableLBA may form partitions
|
||||
LastUsableLBA dq ?
|
||||
DiskGUID rb 16
|
||||
; Globally Unique IDentifier
|
||||
PartitionEntryLBA dq ?
|
||||
; First LBA of Partition Entry Array.
|
||||
; Length in bytes is computed as a product of two following fields.
|
||||
NumberOfPartitionEntries dd ?
|
||||
; Actual number of partitions depends on the contents of Partition Entry Array.
|
||||
; A partition entry is unused if zeroed.
|
||||
SizeOfPartitionEntry dd ? ; in bytes
|
||||
PartitionEntryArrayCRC32 dd ?
|
||||
; Same CRC as for GPT header.
|
||||
ends
|
||||
|
||||
; GPT Partition Entry, UEFI 2.6, Table 19
|
||||
struct GPE
|
||||
PartitionTypeGUID rb 16
|
||||
UniquePartitionGUID rb 16
|
||||
StartingLBA dq ?
|
||||
EndingLBA dq ?
|
||||
; Length in sectors is EndingLBA - StartingLBA + 1.
|
||||
Attributes dq ?
|
||||
PartitionName rb 72
|
||||
ends
|
||||
|
||||
; =============================================================================
|
||||
; ================================ Global data ================================
|
||||
; =============================================================================
|
||||
@ -667,7 +711,7 @@ disk_scan_partitions:
|
||||
xor ebp, ebp ; start from sector zero
|
||||
push ebp ; no extended partition yet
|
||||
; 4. MBR is 512 bytes long. If sector size is less than 512 bytes,
|
||||
; assume no MBR, no partitions and go to 10.
|
||||
; assume no MBR, no partitions and go to 11.
|
||||
cmp [esi+DISK.MediaInfo.SectorSize], 512
|
||||
jb .notmbr
|
||||
.new_mbr:
|
||||
@ -689,9 +733,19 @@ disk_scan_partitions:
|
||||
cmp word [ecx+0x40], 0xaa55
|
||||
jnz .mbr_failed
|
||||
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to
|
||||
; execute step 9 and possibly step 10.
|
||||
; execute step 10 and possibly step 11.
|
||||
test ebp, ebp
|
||||
jnz .mbr
|
||||
; 9. Handle GUID Partition Table
|
||||
; 9a. Check if MBR is protective
|
||||
call is_protective_mbr
|
||||
jnz .no_gpt
|
||||
; 9b. If so, try to scan GPT headers
|
||||
call disk_scan_gpt
|
||||
; 9c. If any GPT header is valid, ignore MBR
|
||||
jz .done
|
||||
; Otherwise process legacy/protective MBR
|
||||
.no_gpt:
|
||||
; The partition table can be present or not present. In the first case, we just
|
||||
; read the MBR. In the second case, we just read the bootsector for a
|
||||
; filesystem.
|
||||
@ -703,8 +757,8 @@ disk_scan_partitions:
|
||||
; byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to
|
||||
; have zeros in the place of partition table.
|
||||
; C. Otherwise, this is an MBR.
|
||||
; 9. Test for MBR vs bootsector.
|
||||
; 9a. Check entries. If any is invalid, go to 10 (rule A).
|
||||
; 10. Test for MBR vs bootsector.
|
||||
; 10a. Check entries. If any is invalid, go to 11 (rule A).
|
||||
call is_partition_table_entry
|
||||
jc .notmbr
|
||||
add ecx, 10h
|
||||
@ -716,25 +770,25 @@ disk_scan_partitions:
|
||||
add ecx, 10h
|
||||
call is_partition_table_entry
|
||||
jc .notmbr
|
||||
; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C).
|
||||
; 10b. Check types of the entries. If at least one is nonzero, go to 12 (rule C).
|
||||
mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type]
|
||||
or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type]
|
||||
or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type]
|
||||
or al, [ecx+PARTITION_TABLE_ENTRY.Type]
|
||||
jnz .mbr
|
||||
; 9c. Empty partition table or bootsector with many zeroes? (rule B)
|
||||
; 10c. Empty partition table or bootsector with many zeroes? (rule B)
|
||||
cmp byte [ebx], 0EBh
|
||||
jz .notmbr
|
||||
cmp byte [ebx], 0E9h
|
||||
jnz .mbr
|
||||
.notmbr:
|
||||
; 10. This is not an MBR. The media is not partitioned. Create one partition
|
||||
; 11. This is not an MBR. The media is not partitioned. Create one partition
|
||||
; which covers all the media and abort the loop.
|
||||
stdcall disk_add_partition, 0, 0, \
|
||||
dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4], esi
|
||||
jmp .done
|
||||
.mbr:
|
||||
; 11. Process all entries of the new MBR/EBR
|
||||
; 12. Process all entries of the new MBR/EBR
|
||||
lea ecx, [ebx+0x1be] ; ecx -> partition table
|
||||
push 0 ; assume no extended partition
|
||||
call process_partition_table_entry
|
||||
@ -745,12 +799,12 @@ disk_scan_partitions:
|
||||
add ecx, 10h
|
||||
call process_partition_table_entry
|
||||
pop ebp
|
||||
; 12. Test whether we found a new EBR and should continue the loop.
|
||||
; 12a. If there was no next EBR, return.
|
||||
; 13. Test whether we found a new EBR and should continue the loop.
|
||||
; 13a. If there was no next EBR, return.
|
||||
test ebp, ebp
|
||||
jz .done
|
||||
; Ok, we have EBR.
|
||||
; 12b. EBRs addresses are relative to the start of extended partition.
|
||||
; 13b. EBRs addresses are relative to the start of extended partition.
|
||||
; For simplicity, just abort if an 32-bit overflow occurs; large disks
|
||||
; are most likely partitioned with GPT, not MBR scheme, since the precise
|
||||
; calculation here would increase limit just twice at the price of big
|
||||
@ -758,34 +812,231 @@ disk_scan_partitions:
|
||||
pop eax ; load extended partition
|
||||
add ebp, eax
|
||||
jc .mbr_failed
|
||||
; 12c. If extended partition has not yet started, start it.
|
||||
; 13c. If extended partition has not yet started, start it.
|
||||
test eax, eax
|
||||
jnz @f
|
||||
mov eax, ebp
|
||||
@@:
|
||||
; 12c. If the limit is not exceeded, continue the loop.
|
||||
; 13d. If the limit is not exceeded, continue the loop.
|
||||
dec dword [esp]
|
||||
push eax ; store extended partition
|
||||
jnz .new_mbr
|
||||
.mbr_failed:
|
||||
.done:
|
||||
; 13. Cleanup after the loop.
|
||||
; 14. Cleanup after the loop.
|
||||
pop eax ; not important anymore
|
||||
pop eax ; not important anymore
|
||||
pop ebp ; restore ebp
|
||||
; 14. Release the buffer.
|
||||
; 14a. Test whether it is the global buffer or we have allocated it.
|
||||
; 15. Release the buffer.
|
||||
; 15a. Test whether it is the global buffer or we have allocated it.
|
||||
cmp ebx, mbr_buffer
|
||||
jz .release_partition_buffer
|
||||
; 14b. If we have allocated it, free it.
|
||||
; 15b. If we have allocated it, free it.
|
||||
xchg eax, ebx
|
||||
call free
|
||||
jmp .nothing
|
||||
; 14c. Otherwise, release reference.
|
||||
; 15c. Otherwise, release reference.
|
||||
.release_partition_buffer:
|
||||
lock dec [partition_buffer_users]
|
||||
.nothing:
|
||||
; 15. Return.
|
||||
; 16. Return.
|
||||
ret
|
||||
|
||||
|
||||
; This function is called from disk_scan_partitions to validate and parse
|
||||
; primary and backup GPTs.
|
||||
proc disk_scan_gpt
|
||||
; Scan primary GPT (second sector)
|
||||
stdcall scan_gpt, 1, 0
|
||||
test eax, eax
|
||||
; There is no code to restore backup GPT if it's corrupt.
|
||||
; Therefore just exit if Primary GPT has been parsed successfully.
|
||||
jz .exit
|
||||
DEBUGF 1, 'K : Primary GPT is corrupt, trying backup one\n'
|
||||
mov eax, dword[esi+DISK.MediaInfo.Capacity+0]
|
||||
mov edx, dword[esi+DISK.MediaInfo.Capacity+4]
|
||||
sub eax, 1
|
||||
sbb edx, 0
|
||||
; Scan backup GPT (last sector)
|
||||
stdcall scan_gpt, eax, edx
|
||||
test eax, eax
|
||||
jz .exit
|
||||
DEBUGF 1, 'K : Backup GPT is also corrupt, fallback to legacy MBR\n'
|
||||
.exit:
|
||||
; Return value is ZF
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
; This function is called from disk_scan_gpt to process a single GPT.
|
||||
proc scan_gpt _mylba:qword
|
||||
locals
|
||||
GPEA_len dd ? ; Length of GPT Partition Entry Array in bytes
|
||||
endl
|
||||
push ebx edi
|
||||
; Allocalte memory for GPT header
|
||||
mov eax, [esi+DISK.MediaInfo.SectorSize]
|
||||
stdcall kernel_alloc, eax
|
||||
test eax, eax
|
||||
jz .fail
|
||||
; Save pointer to stack, just in case
|
||||
push eax
|
||||
mov ebx, eax
|
||||
; Read GPT header
|
||||
mov al, DISKFUNC.read
|
||||
push 1
|
||||
stdcall disk_call_driver, ebx, dword[_mylba+0], dword[_mylba+4], esp
|
||||
pop ecx
|
||||
test eax, eax
|
||||
jnz .fail_free_gpt
|
||||
; Check signature
|
||||
cmp dword[ebx+GPTH.Signature+0], 'EFI '
|
||||
jnz .fail_free_gpt
|
||||
cmp dword[ebx+GPTH.Signature+4], 'PART'
|
||||
jnz .fail_free_gpt
|
||||
; Check Revision
|
||||
cmp [ebx+GPTH.Revision], 0x00010000
|
||||
jnz .fail_free_gpt
|
||||
; Compute and check CRC32
|
||||
xor edx, edx
|
||||
xchg edx, [ebx+GPTH.HeaderCRC32]
|
||||
mov eax, -1
|
||||
stdcall crc_32, 0xEDB88320, ebx, [ebx+GPTH.HeaderSize]
|
||||
xor eax, -1
|
||||
cmp eax, edx
|
||||
jnz .fail_free_gpt
|
||||
; Reserved must be zero
|
||||
cmp [ebx+GPTH.Reserved], 0
|
||||
jnz .fail_free_gpt
|
||||
; MyLBA of GPT header at LBA X must equal X
|
||||
mov eax, dword[ebx+GPTH.MyLBA+0]
|
||||
mov edx, dword[ebx+GPTH.MyLBA+4]
|
||||
cmp eax, dword[_mylba+0]
|
||||
jnz .fail_free_gpt
|
||||
cmp edx, dword[_mylba+4]
|
||||
jnz .fail_free_gpt
|
||||
; Capacity - MyLBA = AlternateLBA
|
||||
mov eax, dword[esi+DISK.MediaInfo.Capacity+0]
|
||||
mov edx, dword[esi+DISK.MediaInfo.Capacity+4]
|
||||
sub eax, dword[_mylba+0]
|
||||
sbb edx, dword[_mylba+4]
|
||||
cmp eax, dword[ebx+GPTH.AlternateLBA+0]
|
||||
; DISK.MediaInfo.Capacity is -1 for ATA devices, disable this check for now.
|
||||
; jnz .fail_free_gpt
|
||||
cmp edx, dword[ebx+GPTH.AlternateLBA+4]
|
||||
; jnz .fail_free_gpt
|
||||
|
||||
; Compute GPT Partition Entry Array (GPEA) length in bytes
|
||||
mov eax, [ebx+GPTH.NumberOfPartitionEntries]
|
||||
mul [ebx+GPTH.SizeOfPartitionEntry]
|
||||
test edx, edx ; far too big
|
||||
jnz .fail_free_gpt
|
||||
; Round up to sector boundary
|
||||
mov ecx, [esi+DISK.MediaInfo.SectorSize] ; power of two
|
||||
dec ecx
|
||||
add eax, ecx
|
||||
jc .fail_free_gpt ; too big
|
||||
not ecx
|
||||
and eax, ecx
|
||||
; We will need this length to compute CRC32 of GPEA
|
||||
mov [GPEA_len], eax
|
||||
; Allocate memory for GPEA
|
||||
stdcall kernel_alloc, eax
|
||||
test eax, eax
|
||||
jz .fail_free_gpt
|
||||
; Save to not juggle with registers
|
||||
push eax
|
||||
mov edi, eax
|
||||
mov eax, [GPEA_len]
|
||||
xor edx, edx
|
||||
; Get the number of sectors GPEA fits into
|
||||
div [esi+DISK.MediaInfo.SectorSize]
|
||||
push eax ; esp = pointer to the number of sectors
|
||||
mov al, DISKFUNC.read
|
||||
stdcall disk_call_driver, edi, dword[ebx+GPTH.PartitionEntryLBA+0], \
|
||||
dword[ebx+GPTH.PartitionEntryLBA+4], esp
|
||||
pop ecx
|
||||
test eax, eax
|
||||
jnz .fail_free_gpea_gpt
|
||||
; Compute and check CRC32 of GPEA
|
||||
mov edx, [ebx+GPTH.PartitionEntryArrayCRC32]
|
||||
mov eax, -1
|
||||
stdcall crc_32, 0xEDB88320, edi, [GPEA_len]
|
||||
xor eax, -1
|
||||
cmp eax, edx
|
||||
jnz .fail_free_gpea_gpt
|
||||
|
||||
; Process partitions, skip zeroed ones.
|
||||
.next_gpe:
|
||||
xor eax, eax
|
||||
mov ecx, [ebx+GPTH.SizeOfPartitionEntry]
|
||||
repz scasb
|
||||
jz .skip
|
||||
add edi, ecx
|
||||
sub edi, [ebx+GPTH.SizeOfPartitionEntry]
|
||||
; Length of a partition in sectors is EndingLBA - StartingLBA + 1
|
||||
mov eax, dword[edi+GPE.EndingLBA+0]
|
||||
mov edx, dword[edi+GPE.EndingLBA+4]
|
||||
sub eax, dword[edi+GPE.StartingLBA+0]
|
||||
sbb edx, dword[edi+GPE.StartingLBA+4]
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
stdcall disk_add_partition, dword[edi+GPE.StartingLBA+0], \
|
||||
dword[edi+GPE.StartingLBA+4], eax, edx, esi
|
||||
add edi, [ebx+GPTH.SizeOfPartitionEntry]
|
||||
.skip:
|
||||
dec [ebx+GPTH.NumberOfPartitionEntries]
|
||||
jnz .next_gpe
|
||||
|
||||
; Pointers to GPT header and GPEA are on the stack
|
||||
stdcall kernel_free
|
||||
stdcall kernel_free
|
||||
pop edi ebx
|
||||
xor eax, eax
|
||||
ret
|
||||
.fail_free_gpea_gpt:
|
||||
stdcall kernel_free
|
||||
.fail_free_gpt:
|
||||
stdcall kernel_free
|
||||
.fail:
|
||||
pop edi ebx
|
||||
xor eax, eax
|
||||
inc eax
|
||||
ret
|
||||
endp
|
||||
|
||||
; ecx = pointer to partition records array (MBR + 446)
|
||||
is_protective_mbr:
|
||||
push ecx edi
|
||||
xor eax, eax
|
||||
cmp [ecx-6], eax
|
||||
jnz .exit
|
||||
cmp [ecx-2], eax
|
||||
jnz .exit
|
||||
; Partition record 0 has specific fields
|
||||
cmp dword[ecx+0], 0x00020000
|
||||
jnz .exit
|
||||
cmp byte[ecx+4], 0xEE
|
||||
jnz .exit
|
||||
cmp dword[ecx+8], 1
|
||||
jnz .exit
|
||||
; DISK.MediaInfo.Capacity is -1 for ATA devices, disable this check for now.
|
||||
; cmp dword[esi+DISK.MediaInfo.Capacity+4], eax
|
||||
; mov edi, 0xFFFFFFFF
|
||||
; jnz @f
|
||||
; mov edi, dword[esi+DISK.MediaInfo.Capacity+0]
|
||||
; dec edi
|
||||
;@@:
|
||||
; cmp dword[ecx+12], edi
|
||||
; jnz .exit
|
||||
|
||||
; Check that partition records 1-3 are filled with zero
|
||||
lea edi, [ecx+16]
|
||||
mov ecx, 16*3/2 ; 3 partitions
|
||||
repz scasw
|
||||
.exit:
|
||||
pop edi ecx
|
||||
; Return value is ZF
|
||||
ret
|
||||
|
||||
; This is an internal function called from disk_scan_partitions. It checks
|
||||
|
40
kernel/trunk/crc.inc
Normal file
40
kernel/trunk/crc.inc
Normal file
@ -0,0 +1,40 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; Copyright (C) KolibriOS team 2004-2017. All rights reserved. ;;
|
||||
;; Distributed under terms of the GNU General Public License. ;;
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; This crc32 routine doesn't use precomputed table to allow different
|
||||
; polynomials, which is the first param.
|
||||
; Partial hash in assumed to be eax (both in and out).
|
||||
; Usage:
|
||||
; 1. mov eax, -1
|
||||
; 2. stdcall crypto.crc32 zero or more times
|
||||
; 3. xor eax, -1
|
||||
proc crc_32 _poly, _buffer, _length
|
||||
push ebx ecx edx esi
|
||||
|
||||
mov esi, [_buffer]
|
||||
.next_byte:
|
||||
dec [_length]
|
||||
js .done
|
||||
movzx ebx, byte[esi]
|
||||
inc esi
|
||||
mov ecx, 8
|
||||
.next_bit:
|
||||
mov edx, eax
|
||||
xor edx, ebx
|
||||
shr eax, 1
|
||||
test edx, 1
|
||||
jz @f
|
||||
xor eax, [_poly]
|
||||
@@:
|
||||
shr ebx, 1
|
||||
dec ecx
|
||||
jnz .next_bit
|
||||
jmp .next_byte
|
||||
.done:
|
||||
pop esi edx ecx ebx
|
||||
ret
|
||||
endp
|
@ -65,6 +65,8 @@ include "fs/fs_lfn.inc" ; sysfunction 70
|
||||
|
||||
include "network/stack.inc"
|
||||
|
||||
include "crc.inc" ; checksums
|
||||
|
||||
; include "imports.inc"
|
||||
; include "core/ext_lib.inc"
|
||||
; include "core/conf_lib.inc"
|
||||
|
Loading…
Reference in New Issue
Block a user