feat: support for USB mass storage devices, hotplugging
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
@@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user