The Pedigree Project 0.1
gpt.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "Gpt.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Disk.h"
5#include "pedigree/kernel/utilities/StaticString.h"
6#include "pedigree/kernel/utilities/new"
7#include "pedigree/kernel/utilities/utility.h"
8
9#include "Partition.h"
10
11namespace {
12bool copy(Disk* disk, uint64_t offset, uint8_t* data, size_t bytes) {
13 while (bytes) {
14 const BufferView view = disk->read(offset);
15 if (!view)
16 return false;
17 const size_t count = bytes < view.size() ? bytes : view.size();
18 if (!count) {
19 disk->unpin(offset);
20 return false;
21 }
22 MemoryCopy(data, view.data(), count);
23 disk->unpin(offset);
24 offset += count;
25 data += count;
26 bytes -= count;
27 }
28 return true;
29}
30String partitionName(const uint8_t* entry, size_t stride) {
31 String result;
32 if (stride <= 56)
33 return result;
34 const size_t units = min(static_cast<size_t>(36), (stride - 56) / 2);
35 for (size_t i = 0; i < units; ++i) {
36 uint32_t character = Gpt::little(entry + 56 + i * 2, 2);
37 if (!character)
38 break;
39 if (character >= 0xd800 && character <= 0xdbff) {
40 if (i + 1 < units) {
41 const uint32_t low = Gpt::little(entry + 56 + (i + 1) * 2, 2);
42 if (low >= 0xdc00 && low <= 0xdfff) {
43 character = 0x10000 + ((character - 0xd800) << 10) + (low - 0xdc00);
44 ++i;
45 } else {
46 character = '?';
47 }
48 } else {
49 character = '?';
50 }
51 } else if (character >= 0xdc00 && character <= 0xdfff) {
52 character = '?';
53 }
54 char utf8[5] = {};
55 size_t length = String::Utf32ToUtf8(character, utf8);
56 if (!length) {
57 utf8[0] = '?';
58 length = 1;
59 }
60 result += String(utf8, length, true);
61 }
62 return result;
63}
64bool candidate(Disk* disk, size_t sectorBytes, uint64_t sectors, bool backup) {
65 uint8_t bytes[4096];
66 Gpt::Header header;
67 if (!copy(disk, (backup ? sectors - 1 : 1) * sectorBytes, bytes, sectorBytes) ||
68 !Gpt::decode(bytes, sectorBytes, sectors, backup, header))
69 return false;
70 auto* entries = new uint8_t[header.bytes];
71 if (!copy(disk, header.table * sectorBytes, entries, header.bytes) ||
72 !Gpt::validEntries(entries, header)) {
73 delete[] entries;
74 return false;
75 }
76 for (size_t i = 0; i < header.count; ++i) {
77 const uint8_t* entry = entries + i * header.stride;
78 if (!Gpt::used(entry))
79 continue;
80 NormalStaticString label("GPT ");
81 label += i + 1;
82 const uint64_t first = Gpt::little(entry + 32, 8), last = Gpt::little(entry + 40, 8);
83 auto* partition =
84 new Partition(String(label), first * sectorBytes, (last - first + 1) * sectorBytes);
85 char uuid[37];
86 Gpt::formatGuid(entry + 16, uuid);
87 partition->setPartitionIdentity(String(uuid), partitionName(entry, header.stride));
88 partition->setParent(disk);
89 disk->addChild(partition);
90 }
91 delete[] entries;
92 NOTICE("GPT: validated " << (backup ? "backup" : "primary") << " table, logical sector " << Dec
93 << sectorBytes << Hex);
94 return true;
95}
96} // namespace
97bool gptProbeDisk(Disk* disk) {
98 const size_t bytes = disk->getNativeBlockSize();
99 if (bytes < 512 || bytes > 4096 || (bytes & (bytes - 1)) || disk->getSize() % bytes)
100 return false;
101 const uint64_t sectors = disk->getSize() / bytes;
102 if (sectors < 6)
103 return false;
104 uint8_t mbr[512];
105 if (!copy(disk, 0, mbr, sizeof(mbr)) || mbr[510] != 0x55 || mbr[511] != 0xaa)
106 return false;
107 bool protective = false;
108 for (size_t i = 0; i < 4; ++i)
109 protective |= mbr[446 + 16 * i + 4] == 0xee && Gpt::little(mbr + 446 + 16 * i + 8, 4) == 1;
110 // Old backup headers can survive a reformat to MBR. Only recover a GPT disk.
111 if (!protective)
112 return false;
113 if (!candidate(disk, bytes, sectors, false) && !candidate(disk, bytes, sectors, true))
114 WARNING("GPT: no valid primary or backup table");
115 return true;
116}
void addChild(Device *pDevice)
Definition Device.cc:135
Definition Disk.h:35
virtual BufferView read(uint64_t location)
Definition Disk.cc:163
virtual size_t getSize() const
Gets the size of the disk.
Definition Disk.cc:344
virtual void unpin(uint64_t location)=0
virtual size_t getNativeBlockSize() const
Definition Disk.cc:383
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
Definition String.cc:538
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124