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
+3
View File
@@ -748,6 +748,9 @@ namespace Drivers::Storage::Ahci {
bdev.Ctx = (void*)(uintptr_t)i;
bdev.SectorCount = g_ports[i].SectorCount;
bdev.SectorSize = g_ports[i].SectorSizeLog;
bdev.Kind = (type == PortType::Satapi)
? Storage::BLOCK_KIND_SATAPI
: Storage::BLOCK_KIND_SATA;
memcpy(bdev.Model, g_ports[i].Model, 41);
Storage::RegisterBlockDevice(bdev);
}
+29 -6
View File
@@ -9,21 +9,44 @@
namespace Drivers::Storage {
static BlockDevice g_devices[MaxBlockDevices] = {};
static int g_deviceCount = 0;
static bool g_active[MaxBlockDevices] = {};
static int g_deviceHighWater = 0;
int RegisterBlockDevice(const BlockDevice& dev) {
if (g_deviceCount >= MaxBlockDevices) return -1;
g_devices[g_deviceCount] = dev;
return g_deviceCount++;
for (int i = 0; i < MaxBlockDevices; i++) {
if (g_active[i]) continue;
g_devices[i] = dev;
g_active[i] = true;
if (i >= g_deviceHighWater) {
g_deviceHighWater = i + 1;
}
return i;
}
return -1;
}
int UnregisterBlockDevice(int index) {
if (index < 0 || index >= MaxBlockDevices || !g_active[index]) return -1;
g_active[index] = false;
g_devices[index] = {};
while (g_deviceHighWater > 0 && !g_active[g_deviceHighWater - 1]) {
g_deviceHighWater--;
}
return 0;
}
const BlockDevice* GetBlockDevice(int index) {
if (index < 0 || index >= g_deviceCount) return nullptr;
if (index < 0 || index >= MaxBlockDevices || !g_active[index]) return nullptr;
return &g_devices[index];
}
int GetBlockDeviceCount() {
return g_deviceCount;
return g_deviceHighWater;
}
};
+16 -2
View File
@@ -11,22 +11,36 @@ namespace Drivers::Storage {
static constexpr int MaxBlockDevices = 32;
enum BlockDeviceKind : uint8_t {
BLOCK_KIND_UNKNOWN = 0,
BLOCK_KIND_SATA = 1,
BLOCK_KIND_SATAPI = 2,
BLOCK_KIND_NVME = 3,
BLOCK_KIND_USB_MSC = 4,
};
struct BlockDevice {
bool (*ReadSectors)(void* ctx, uint64_t lba, uint32_t count, void* buffer);
bool (*WriteSectors)(void* ctx, uint64_t lba, uint32_t count, const void* buffer);
void* Ctx;
uint64_t SectorCount;
uint16_t SectorSize;
uint8_t Kind;
uint8_t Reserved;
char Model[41];
};
// Register a block device. Returns the assigned index, or -1 on failure.
// Register a block device in the lowest free slot. Returns the assigned
// index, or -1 on failure.
int RegisterBlockDevice(const BlockDevice& dev);
// Unregister a block device by index. Returns 0 on success, -1 on failure.
int UnregisterBlockDevice(int index);
// Get a registered block device by index. Returns nullptr if invalid.
const BlockDevice* GetBlockDevice(int index);
// Get the number of registered block devices.
// Get the high-water count for registered block-device slots.
int GetBlockDeviceCount();
};
+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
// -------------------------------------------------------------------------
+7 -3
View File
@@ -110,11 +110,11 @@ namespace Drivers::Storage::Gpt {
// Public API
// =========================================================================
// Probe a block device for a GPT. Returns the number of partitions found,
// or 0 if no valid GPT was detected.
// Probe a block device for GPT, with a legacy MBR fallback. Returns the
// number of new partitions found, or 0 if no valid partition table exists.
int ProbeDevice(int blockDevIndex);
// Probe all registered block devices for GPT.
// Probe all registered block devices for partitions.
void ProbeAll();
// Get total number of discovered partitions (across all devices).
@@ -123,6 +123,10 @@ namespace Drivers::Storage::Gpt {
// Get partition info by global index.
const PartitionInfo* GetPartition(int index);
// Remove all in-memory partition records for a block device. Used when a
// hot-pluggable block device disappears.
int RemovePartitionsForBlockDevice(int blockDevIndex);
// Look up the human-readable name for a partition type GUID.
const char* GetTypeName(const Guid& typeGuid);
+1
View File
@@ -668,6 +668,7 @@ namespace Drivers::Storage::Nvme {
bdev.Ctx = (void*)(uintptr_t)i;
bdev.SectorCount = g_namespaces[i].SectorCount;
bdev.SectorSize = (uint16_t)g_namespaces[i].SectorSize;
bdev.Kind = Storage::BLOCK_KIND_NVME;
memcpy(bdev.Model, g_namespaces[i].Model, 41);
Storage::RegisterBlockDevice(bdev);
}