From dad8952e8784d6e2a2011b0dbe8a4356fd72edd6 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Wed, 15 Sep 2021 13:50:23 +0100
Subject: [PATCH] gpt partition table parsing

---
 boot/x86/uefi_loader.s          |  6 ++-
 boot/x86/{uefi.c => uefi_old.c} |  0
 desktop/os.header               |  1 -
 drivers/fat.cpp                 |  1 -
 kernel/elf.cpp                  |  2 +-
 kernel/files.cpp                | 29 ++++++++++--
 kernel/module.h                 |  3 +-
 shared/partitions.cpp           | 79 ++++++++++++++++++++++++++++++++-
 util/build_core.c               |  7 +++
 9 files changed, 116 insertions(+), 12 deletions(-)
 rename boot/x86/{uefi.c => uefi_old.c} (100%)

diff --git a/boot/x86/uefi_loader.s b/boot/x86/uefi_loader.s
index 57efc90..76d52d9 100644
--- a/boot/x86/uefi_loader.s
+++ b/boot/x86/uefi_loader.s
@@ -78,8 +78,10 @@ run_kernel64:
 	mov	rcx,[rbx + 24]
 
 	; Map the first MB at 0xFFFFFE0000000000 --> 0xFFFFFE0000100000 
-	mov	rax,[0xFFFFFF7FBFDFE000]
-	mov	[0xFFFFFF7FBFDFEFE0],rax
+	mov	rdi,0xFFFFFF7FBFDFE000
+	mov	rax,[rdi]
+	mov	rdi,0xFFFFFF7FBFDFEFE0
+	mov	[rdi],rax
 	mov	rax,cr3
 	mov	cr3,rax
 
diff --git a/boot/x86/uefi.c b/boot/x86/uefi_old.c
similarity index 100%
rename from boot/x86/uefi.c
rename to boot/x86/uefi_old.c
diff --git a/desktop/os.header b/desktop/os.header
index 0892234..ac97a90 100644
--- a/desktop/os.header
+++ b/desktop/os.header
@@ -1882,7 +1882,6 @@ struct EsFontInformation {
 struct EsBlockDeviceInformation {
 	size_t sectorSize;
 	EsFileOffset sectorCount;
-	bool noMBR;
 	bool readOnly;
 	uint8_t nestLevel;
 	uint8_t driveType;
diff --git a/drivers/fat.cpp b/drivers/fat.cpp
index 78d6c63..820e6ac 100644
--- a/drivers/fat.cpp
+++ b/drivers/fat.cpp
@@ -488,7 +488,6 @@ static void DeviceAttach(KDevice *parent) {
 		}
 	}
 
-	volume->rootDirectory->driverNode = volume->root;
 	volume->directoryEntryDataBytes = sizeof(DirectoryEntryReference);
 	volume->nodeDataBytes = sizeof(FSNode);
 
diff --git a/kernel/elf.cpp b/kernel/elf.cpp
index bfc176b..0302e95 100644
--- a/kernel/elf.cpp
+++ b/kernel/elf.cpp
@@ -90,7 +90,7 @@ struct ElfSectionHeader {
 	uint64_t entrySize;
 };
 
-struct ElfProgramHeader{
+struct ElfProgramHeader {
 	uint32_t type; // 0 = unused, 1 = load, 2 = dynamic, 3 = interp, 4 = note
 	uint32_t flags; // 1 = executable, 2 = writable, 4 = readable
 	uint64_t fileOffset;
diff --git a/kernel/files.cpp b/kernel/files.cpp
index d733d90..d9389c0 100644
--- a/kernel/files.cpp
+++ b/kernel/files.cpp
@@ -1679,7 +1679,8 @@ void FSPartitionDeviceAccess(KBlockDeviceAccessRequest request) {
 	FSBlockDeviceAccess(request);
 }
 
-void FSPartitionDeviceCreate(KBlockDevice *parent, EsFileOffset offset, EsFileOffset sectorCount, unsigned flags, const char *model, size_t modelBytes) {
+void FSPartitionDeviceCreate(KBlockDevice *parent, EsFileOffset offset, EsFileOffset sectorCount, uint32_t flags, const char *model, size_t modelBytes) {
+	(void) flags;
 	PartitionDevice *child = (PartitionDevice *) KDeviceCreate("Partition", parent, sizeof(PartitionDevice));
 	if (!child) return;
 
@@ -1691,7 +1692,6 @@ void FSPartitionDeviceCreate(KBlockDevice *parent, EsFileOffset offset, EsFileOf
 	child->maxAccessSectorCount = parent->maxAccessSectorCount;
 	child->sectorOffset = offset;
 	child->information.sectorCount = sectorCount;
-	child->information.noMBR = flags & FS_PARTITION_DEVICE_NO_MBR ? true : false;
 	child->information.readOnly = parent->information.readOnly;
 	child->access = FSPartitionDeviceAccess;
 	child->information.modelBytes = modelBytes;
@@ -1739,7 +1739,26 @@ bool FSCheckMBR(KBlockDevice *device) {
 			if (partitions[i].present) {
 				KernelLog(LOG_INFO, "FS", "MBR partition", "Found MBR partition %d with offset %d and count %d.\n", 
 						i, partitions[i].offset, partitions[i].count);
-				FSPartitionDeviceCreate(device, partitions[i].offset, partitions[i].count, FS_PARTITION_DEVICE_NO_MBR, EsLiteral("MBR partition"));
+				FSPartitionDeviceCreate(device, partitions[i].offset, partitions[i].count, ES_FLAGS_DEFAULT, EsLiteral("MBR partition"));
+			}
+		}
+
+		return true;
+	} else {
+		return false;
+	}
+}
+
+bool FSCheckGPT(KBlockDevice *device) {
+	GPTPartition *partitions = (GPTPartition *) EsHeapAllocate(sizeof(GPTPartition) * GPT_PARTITION_COUNT, false, K_FIXED);
+	EsDefer(EsHeapFree(partitions, sizeof(GPTPartition) * GPT_PARTITION_COUNT, K_FIXED));
+
+	if (GPTGetPartitions(device->signatureBlock, device->information.sectorCount, device->information.sectorSize, partitions)) {
+		for (uintptr_t i = 0; i < GPT_PARTITION_COUNT; i++) {
+			if (partitions[i].present) {
+				KernelLog(LOG_INFO, "FS", "GPT partition", "Found GPT partition %d with offset %d and count %d%z.\n", 
+						i, partitions[i].offset, partitions[i].count, partitions[i].isESP ? "; this is the ESP" : "");
+				FSPartitionDeviceCreate(device, partitions[i].offset, partitions[i].count, ES_FLAGS_DEFAULT, EsLiteral("GPT partition"));
 			}
 		}
 
@@ -1809,7 +1828,9 @@ void FSDetectFileSystem(KBlockDevice *device) {
 			// We could not access the block device.
 			KernelLog(LOG_ERROR, "FS", "detect fileSystem read failure", "The signature block could not be read on block device %x.\n", device);
 		} else {
-			if (!device->information.noMBR && FSCheckMBR(device)) {
+			if (!device->information.nestLevel && FSCheckGPT(device)) {
+				// Found a GPT.
+			} else if (!device->information.nestLevel && FSCheckMBR(device)) {
 				// Found an MBR.
 			} else {
 				KDeviceAttach(device, "Files", FSSignatureCheck);
diff --git a/kernel/module.h b/kernel/module.h
index 53bf01b..a26113d 100644
--- a/kernel/module.h
+++ b/kernel/module.h
@@ -640,8 +640,7 @@ struct KBlockDevice : KDevice {
 	KMutex detectFileSystemMutex;
 };
 
-#define FS_PARTITION_DEVICE_NO_MBR (1 << 0)
-void FSPartitionDeviceCreate(KBlockDevice *parent, EsFileOffset offset, EsFileOffset sectorCount, unsigned flags, const char *name, size_t nameBytes);
+void FSPartitionDeviceCreate(KBlockDevice *parent, EsFileOffset offset, EsFileOffset sectorCount, uint32_t flags, const char *name, size_t nameBytes);
 
 // ---------------------------------------------------------------------------------------------------------------
 // PCI.
diff --git a/shared/partitions.cpp b/shared/partitions.cpp
index 3c1aed4..f899011 100644
--- a/shared/partitions.cpp
+++ b/shared/partitions.cpp
@@ -1,9 +1,13 @@
+#ifndef K_SIGNATURE_BLOCK_SIZE
+#define K_SIGNATURE_BLOCK_SIZE (65536)
+#endif
+
 typedef struct MBRPartition {
 	uint32_t offset, count;
 	bool present;
 } MBRPartition;
 
-bool MBRGetPartitions(uint8_t *firstBlock, EsFileOffset sectorCount, MBRPartition *partitions) {
+bool MBRGetPartitions(uint8_t *firstBlock, EsFileOffset sectorCount, MBRPartition *partitions /* 4 */) {
 	if (firstBlock[510] != 0x55 || firstBlock[511] != 0xAA) {
 		return false;
 	}
@@ -53,3 +57,76 @@ void MBRFixPartition(uint32_t *partitions) {
 	partitions[0] |= (partitionOffsetHead << 8) | (partitionOffsetSector << 16) | (partitionOffsetCylinder << 24) | ((partitionOffsetCylinder >> 8) << 16);
 	partitions[1] |= (partitionSizeHead << 8) | (partitionSizeSector << 16) | (partitionSizeCylinder << 24) | ((partitionSizeCylinder >> 8) << 16);
 }
+
+#define GPT_PARTITION_COUNT (0x80)
+
+typedef struct GPTPartition {
+	uint64_t offset, count;
+	bool present, isESP;
+} GPTPartition;
+
+bool GPTGetPartitions(uint8_t *block /* K_SIGNATURE_BLOCK_SIZE */, EsFileOffset sectorCount, 
+		EsFileOffset sectorBytes, GPTPartition *partitions /* GPT_PARTITION_COUNT */) {
+	for (uintptr_t i = 0; i < GPT_PARTITION_COUNT; i++) {
+		partitions[i].present = false;
+	}
+
+	if (sectorBytes * 2 >= K_SIGNATURE_BLOCK_SIZE || sectorCount <= 2 || sectorBytes < 0x200) {
+		return false;
+	}
+
+	const char *signature = "EFI PART";
+
+	for (uintptr_t i = 0; i < 8; i++) {
+		if (block[sectorBytes + i] != signature[i]) {
+			return false;
+		}
+	}
+
+	uint32_t partitionEntryCount = *(uint32_t *) (block + sectorBytes + 80);
+	uint32_t partitionEntryBytes = *(uint32_t *) (block + sectorBytes + 84);
+
+	if (partitionEntryBytes < 0x80 || partitionEntryBytes > sectorBytes || sectorBytes % partitionEntryBytes 
+			|| partitionEntryCount == 0 || partitionEntryBytes > 0x1000 || partitionEntryCount > 0x1000
+			|| partitionEntryBytes * partitionEntryCount / sectorBytes + 2 >= sectorCount) {
+		return false;
+	}
+
+	if (partitionEntryCount > GPT_PARTITION_COUNT) {
+		partitionEntryCount = GPT_PARTITION_COUNT;
+	}
+
+	bool foundESP = false;
+
+	for (uintptr_t i = 0; i < partitionEntryCount; i++) {
+		uint8_t *entry = block + sectorBytes * 2 + i * partitionEntryBytes;
+
+		uint64_t guidLow = *(uint64_t *) (entry + 0);
+		uint64_t guidHigh = *(uint64_t *) (entry + 8);
+		uint64_t firstLBA = *(uint64_t *) (entry + 32);
+		uint64_t lastLBA = *(uint64_t *) (entry + 40);
+
+		if (!guidLow && !guidHigh) {
+			continue;
+		}
+
+		if ((!guidLow && !guidHigh) || firstLBA >= sectorCount || lastLBA >= sectorCount || firstLBA >= lastLBA) {
+			return false;
+		}
+
+		partitions[i].present = true;
+		partitions[i].offset = firstLBA;
+		partitions[i].count = lastLBA - firstLBA;
+		partitions[i].isESP = guidLow == 0x11D2F81FC12A7328 && guidHigh == 0x3BC93EC9A0004BBA;
+
+		if (partitions[i].isESP) {
+			if (foundESP) {
+				return false;
+			} else {
+				foundESP = true;
+			}
+		}
+	}
+
+	return true;
+}
diff --git a/util/build_core.c b/util/build_core.c
index b696971..30dc69b 100644
--- a/util/build_core.c
+++ b/util/build_core.c
@@ -1051,6 +1051,8 @@ void BuildBootloader(Application *application) {
 	ExecuteForApp(application, toolchainNasm, "-MD", "bin/boot3.d", "-fbin", 
 			"boot/x86/loader.s", "-obin/stage2", 
 			"-Pboot/x86/esfs-stage2.s", (forEmulator && !bootUseVBE) ? "" : "-D BOOT_USE_VBE");
+	ExecuteForApp(application, toolchainNasm, "-MD", "bin/boot4.d", "-fbin", 
+			"boot/x86/uefi_loader.s", "-obin/uefi_loader");
 }
 
 File _drive;
@@ -1098,6 +1100,10 @@ void Install(const char *driveFile, uint64_t partitionSize, const char *partitio
 		installationIdentifier.d[i] = rand();
 	}
 
+	File iid = FileOpen("bin/iid.dat", 'w');
+	FileWrite(iid, 16, &installationIdentifier);
+	FileClose(iid);
+
 	File f = FileOpen(driveFile, 'w');
 
 	File mbr = FileOpen("bin/mbr", 'r');
@@ -1446,6 +1452,7 @@ int main(int argc, char **argv) {
 			ADD_DEPENDENCY_FILE(application, "bin/boot1.d", "Boot1");
 			ADD_DEPENDENCY_FILE(application, "bin/boot2.d", "Boot2");
 			ADD_DEPENDENCY_FILE(application, "bin/boot3.d", "Boot3");
+			ADD_DEPENDENCY_FILE(application, "bin/boot4.d", "Boot4");
 			arrput(applications, application);
 		}