feat: support for USB mass storage devices, hotplugging

This commit is contained in:
2026-05-18 17:47:12 +02:00
parent d340e01e56
commit c20579afd3
36 changed files with 1537 additions and 130 deletions
+177 -2
View File
@@ -62,6 +62,18 @@ namespace Drivers::Storage::Gpt {
a.Data4[6] == b.Data4[6] && a.Data4[7] == b.Data4[7];
}
static int FindExistingPartition(int blockDevIndex, uint64_t startLba, uint64_t endLba) {
for (int i = 0; i < g_partitionCount; i++) {
if (g_partitions[i].BlockDevIndex == blockDevIndex &&
g_partitions[i].StartLba == startLba &&
g_partitions[i].EndLba == endLba) {
return i;
}
}
return -1;
}
// -------------------------------------------------------------------------
// UTF-16LE to ASCII narrowing
// -------------------------------------------------------------------------
@@ -76,6 +88,53 @@ namespace Drivers::Storage::Gpt {
dst[j] = '\0';
}
static void CopyAsciiName(const char* src, char* dst, int dstMax) {
int i = 0;
for (; src && src[i] && i < dstMax - 1; i++) {
dst[i] = src[i];
}
dst[i] = '\0';
}
static bool IsExtendedMbrType(uint8_t type) {
return type == 0x05 || type == 0x0F || type == 0x85;
}
static Guid GuidForMbrType(uint8_t type) {
switch (type) {
case 0x82:
return GUID_LINUX_SWAP;
case 0x83:
return GUID_LINUX_FS;
default:
return GUID_BASIC_DATA;
}
}
static int AppendMbrPartition(int blockDevIndex, uint64_t diskSectorCount,
uint64_t startLba, uint64_t sectorCount,
uint8_t type, const char* name) {
if (sectorCount == 0 || g_partitionCount >= MaxPartitions) return 0;
if (startLba >= diskSectorCount || sectorCount > diskSectorCount - startLba) return 0;
uint64_t endLba = startLba + sectorCount - 1;
if (endLba < startLba) return 0;
if (FindExistingPartition(blockDevIndex, startLba, endLba) >= 0) return 0;
PartitionInfo& part = g_partitions[g_partitionCount];
part.BlockDevIndex = blockDevIndex;
part.StartLba = startLba;
part.EndLba = endLba;
part.SectorCount = sectorCount;
part.TypeGuid = GuidForMbrType(type);
part.UniqueGuid = GUID_UNUSED;
part.Attributes = 0;
CopyAsciiName(name, part.Name, 72);
g_partitionCount++;
return 1;
}
// -------------------------------------------------------------------------
// Validate protective MBR
// -------------------------------------------------------------------------
@@ -237,6 +296,7 @@ namespace Drivers::Storage::Gpt {
if (GuidIsZero(entry->TypeGuid)) continue;
if (entry->StartingLba == 0 || entry->EndingLba == 0) continue;
if (entry->StartingLba > entry->EndingLba) continue;
if (FindExistingPartition(blockDevIndex, entry->StartingLba, entry->EndingLba) >= 0) continue;
PartitionInfo& part = g_partitions[g_partitionCount];
part.BlockDevIndex = blockDevIndex;
@@ -258,6 +318,88 @@ namespace Drivers::Storage::Gpt {
return found;
}
static int ParseExtendedMbr(const BlockDevice* dev, int blockDevIndex,
uint64_t extendedBase, int& logicalNumber) {
int found = 0;
uint64_t ebrLba = extendedBase;
for (int guard = 0; guard < 32 && g_partitionCount < MaxPartitions; guard++) {
uint8_t ebr[512];
if (!dev->ReadSectors(dev->Ctx, ebrLba, 1, ebr)) break;
const ProtectiveMbr* mbr = (const ProtectiveMbr*)ebr;
if (mbr->Signature != 0xAA55) break;
const MbrPartitionEntry& logical = mbr->Partitions[0];
if (logical.Type != 0 && logical.SectorCount != 0 &&
!IsExtendedMbrType(logical.Type)) {
char name[16] = {};
name[0] = 'M'; name[1] = 'B'; name[2] = 'R';
int n = logicalNumber++;
name[3] = (char)('0' + ((n / 10) % 10));
name[4] = (char)('0' + (n % 10));
name[5] = '\0';
found += AppendMbrPartition(blockDevIndex,
dev->SectorCount,
ebrLba + logical.LbaFirst,
logical.SectorCount,
logical.Type,
name);
}
const MbrPartitionEntry& next = mbr->Partitions[1];
if (next.Type == 0 || next.SectorCount == 0 || !IsExtendedMbrType(next.Type)) {
break;
}
ebrLba = extendedBase + next.LbaFirst;
}
return found;
}
static int ParseMbrPartitions(const BlockDevice* dev, const uint8_t* sector0,
int blockDevIndex) {
const ProtectiveMbr* mbr = (const ProtectiveMbr*)sector0;
if (mbr->Signature != 0xAA55) {
KernelLogStream(INFO, "MBR") << "No partition table on device "
<< blockDevIndex << " (LBA 0 signature absent)";
return 0;
}
int found = 0;
int logicalNumber = 5;
for (int i = 0; i < 4 && g_partitionCount < MaxPartitions; i++) {
const MbrPartitionEntry& entry = mbr->Partitions[i];
if (entry.Type == 0 || entry.SectorCount == 0) continue;
if (IsExtendedMbrType(entry.Type)) {
found += ParseExtendedMbr(dev, blockDevIndex, entry.LbaFirst, logicalNumber);
continue;
}
char name[8] = {};
name[0] = 'M'; name[1] = 'B'; name[2] = 'R';
name[3] = (char)('1' + i);
name[4] = '\0';
found += AppendMbrPartition(blockDevIndex, dev->SectorCount, entry.LbaFirst,
entry.SectorCount, entry.Type, name);
}
if (found > 0) {
KernelLogStream(OK, "MBR") << "Found " << found
<< " partition(s) on device " << blockDevIndex;
} else {
KernelLogStream(INFO, "MBR") << "Device " << blockDevIndex
<< " has MBR signature but no partition entries";
}
return found;
}
// -------------------------------------------------------------------------
// Public API
// -------------------------------------------------------------------------
@@ -281,12 +423,15 @@ namespace Drivers::Storage::Gpt {
uint8_t sectorBuf[1024];
if (!dev->ReadSectors(dev->Ctx, 0, 2, sectorBuf)) {
KernelLogStream(ERROR, "GPT") << "Failed to read LBA 0-1 from device "
<< blockDevIndex;
return 0;
}
// Validate protective MBR
// Validate protective MBR. If this is a legacy-partitioned disk instead
// of GPT, keep using the same partition registry for MBR entries.
if (!ValidateProtectiveMbr(sectorBuf)) {
return 0;
return ParseMbrPartitions(dev, sectorBuf, blockDevIndex);
}
// Validate primary GPT header (LBA 1)
@@ -375,6 +520,36 @@ namespace Drivers::Storage::Gpt {
return &g_partitions[index];
}
int RemovePartitionsForBlockDevice(int blockDevIndex) {
int removed = 0;
int dst = 0;
for (int src = 0; src < g_partitionCount; src++) {
if (g_partitions[src].BlockDevIndex == blockDevIndex) {
removed++;
continue;
}
if (dst != src) {
g_partitions[dst] = g_partitions[src];
}
dst++;
}
for (int i = dst; i < g_partitionCount; i++) {
g_partitions[i] = {};
}
g_partitionCount = dst;
if (removed > 0) {
KernelLogStream(INFO, "GPT") << "Removed " << removed
<< " partition record(s) for device " << blockDevIndex;
}
return removed;
}
// -------------------------------------------------------------------------
// ASCII to UTF-16LE for partition names
// -------------------------------------------------------------------------