Kernel: Rewrite basically all current disk io stuff

This is a big commit that was kinda hard to split to smaller ones.

Essentially we now look at all the mass storage devices from PCI
and initialize (P)ATA devices. This doesn't provide any more functionality
but better abstractions and everything doesn't have to be on its old
default port that might be different for modern computers.
This commit is contained in:
Bananymous 2023-02-26 03:00:29 +02:00
parent 048a2ebb95
commit ee5d02aa70
13 changed files with 570 additions and 475 deletions

View File

@ -33,11 +33,9 @@ KERNEL_OBJS= \
$(KERNEL_ARCH_OBJS) \
font/prefs.o \
kernel/APIC.o \
kernel/ATA.o \
kernel/build_libc.o \
kernel/CPUID.o \
kernel/Debug.o \
kernel/DiskIO.o \
kernel/Font.o \
kernel/FS/Ext2.o \
kernel/FS/VirtualFileSystem.o \
@ -54,6 +52,8 @@ kernel/Serial.o \
kernel/Shell.o \
kernel/SpinLock.o \
kernel/SSP.o \
kernel/Storage/ATAController.o \
kernel/Storage/StorageDevice.o \
kernel/Thread.o \
kernel/TTY.o \
kernel/VesaTerminalDriver.o \
@ -103,6 +103,7 @@ always:
mkdir -p $(BUILDDIR)/$(ARCHDIR)
mkdir -p $(BUILDDIR)/kernel
mkdir -p $(BUILDDIR)/kernel/FS
mkdir -p $(BUILDDIR)/kernel/Storage
mkdir -p $(BUILDDIR)/font
clean:

View File

@ -1,61 +0,0 @@
#pragma once
#include <BAN/Vector.h>
#include <kernel/DiskIO.h>
namespace Kernel
{
class ATADevice : public DiskDevice
{
public:
static ATADevice* create(uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit);
virtual ~ATADevice() {}
uint16_t io_base() const { return m_io_base; }
uint16_t ctl_base() const { return m_ctl_base; }
uint8_t slave_bit() const { return m_slave_bit; }
virtual const char* type() const = 0;
protected:
ATADevice(uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit)
: m_io_base(io_base)
, m_ctl_base(ctl_base)
, m_slave_bit(slave_bit)
{}
private:
const uint16_t m_io_base;
const uint16_t m_ctl_base;
const uint8_t m_slave_bit;
};
class PATADevice final : public ATADevice
{
public:
PATADevice(uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit)
: ATADevice(io_base, ctl_base, slave_bit)
{}
virtual uint32_t sector_size() const override { return m_sector_words * 2; }
virtual const char* type() const override { return "PATA"; }
virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) override;
protected:
virtual bool initialize() override;
private:
bool read_lba28(uint32_t lba, uint8_t sector_count, uint8_t* buffer);
bool wait_while_buzy();
bool wait_for_transfer();
void flush();
private:
bool m_lba_48 = false;
uint32_t m_sector_words = 256;
};
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <BAN/String.h>
#include <kernel/DiskIO.h>
#include <kernel/Storage/StorageDevice.h>
#include <kernel/FS/FileSystem.h>
namespace Kernel
@ -156,12 +156,12 @@ namespace Kernel
class Ext2FS : public FileSystem
{
public:
static BAN::ErrorOr<Ext2FS*> create(DiskDevice::Partition&);
static BAN::ErrorOr<Ext2FS*> create(StorageDevice::Partition&);
virtual const BAN::RefCounted<Inode> root_inode() const override { return m_root_inode; }
private:
Ext2FS(DiskDevice::Partition& partition)
Ext2FS(StorageDevice::Partition& partition)
: m_partition(partition)
{}
@ -177,7 +177,7 @@ namespace Kernel
const Ext2::Inode& ext2_root_inode() const;
private:
DiskDevice::Partition& m_partition;
StorageDevice::Partition& m_partition;
BAN::RefCounted<Inode> m_root_inode;

View File

@ -1,6 +1,7 @@
#pragma once
#include <kernel/FS/FileSystem.h>
#include <kernel/Storage/StorageController.h>
namespace Kernel
{
@ -8,7 +9,7 @@ namespace Kernel
class VirtualFileSystem : public FileSystem
{
public:
static void initialize(BAN::RefCounted<Inode> root_inode);
static BAN::ErrorOr<void> initialize();
static VirtualFileSystem& get();
static bool is_initialized();
@ -17,12 +18,13 @@ namespace Kernel
BAN::ErrorOr<BAN::RefCounted<Inode>> from_absolute_path(BAN::StringView);
private:
VirtualFileSystem(BAN::RefCounted<Inode> root_inode)
: m_root_inode(root_inode)
{}
VirtualFileSystem() = default;
BAN::ErrorOr<void> initialize_impl();
private:
BAN::RefCounted<Inode> m_root_inode;
BAN::Vector<StorageController*> m_storage_controllers;
};
}

View File

@ -0,0 +1,67 @@
#pragma once
#include <BAN/Errors.h>
#include <kernel/Storage/StorageController.h>
namespace Kernel
{
struct ATABus;
class ATADevice : public StorageDevice
{
public:
virtual BAN::ErrorOr<void> read_sectors(uint64_t, uint8_t, uint8_t*) override;
virtual uint32_t sector_size() const override { return sector_words * 2; }
virtual uint64_t total_size() const override { return lba_count * sector_size(); }
private:
enum class Type
{
Unknown,
ATA,
ATAPI,
};
Type type;
uint8_t slave_bit; // 0x00 for master, 0x10 for slave
uint16_t signature;
uint16_t capabilities;
uint32_t command_set;
uint32_t sector_words;
uint64_t lba_count;
char model[41];
ATABus* bus;
friend class ATAController;
};
struct ATABus
{
uint16_t base;
uint16_t ctrl;
ATADevice devices[2];
uint8_t read(uint16_t);
void read_buffer(uint16_t, uint16_t*, size_t);
void write(uint16_t, uint8_t);
BAN::ErrorOr<void> wait(bool);
BAN::Error error();
};
class ATAController : public StorageController
{
public:
static BAN::ErrorOr<ATAController*> create(const PCIDevice&);
private:
ATAController(const PCIDevice& device) : m_pci_device(device) {}
BAN::ErrorOr<void> initialize();
private:
ATABus m_buses[2];
const PCIDevice& m_pci_device;
};
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <kernel/PCI.h>
#include <kernel/Storage/StorageDevice.h>
namespace Kernel
{
class StorageController
{
public:
BAN::Vector<StorageDevice*>& devices() { return m_devices; }
const BAN::Vector<StorageDevice*>& devices() const { return m_devices; }
protected:
BAN::Vector<StorageDevice*> m_devices;
};
}

View File

@ -13,12 +13,12 @@ namespace Kernel
uint8_t data4[8];
};
class DiskDevice
class StorageDevice
{
public:
struct Partition
{
Partition(DiskDevice&, const GUID&, const GUID&, uint64_t, uint64_t, uint64_t, const char*);
Partition(StorageDevice&, const GUID&, const GUID&, uint64_t, uint64_t, uint64_t, const char*);
const GUID& type() const { return m_type; }
const GUID& guid() const { return m_guid; }
@ -26,29 +26,29 @@ namespace Kernel
uint64_t lba_end() const { return m_lba_end; }
uint64_t attributes() const { return m_attributes; }
const char* name() const { return m_name; }
const DiskDevice& device() const { return m_device; }
const StorageDevice& device() const { return m_device; }
bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer);
BAN::ErrorOr<void> read_sectors(uint64_t lba, uint8_t sector_count, uint8_t* buffer);
bool is_used() const { uint8_t zero[16] {}; return memcmp(&m_type, zero, 16); }
private:
DiskDevice& m_device;
StorageDevice& m_device;
const GUID m_type;
const GUID m_guid;
const uint64_t m_lba_start;
const uint64_t m_lba_end;
const uint64_t m_attributes;
char m_name[72];
char m_name[36 * 3 + 1];
};
public:
virtual ~DiskDevice() {}
virtual ~StorageDevice() {}
virtual bool initialize() = 0;
bool initialize_partitions();
BAN::ErrorOr<void> initialize_partitions();
virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) = 0;
virtual BAN::ErrorOr<void> read_sectors(uint64_t lba, uint8_t sector_count, uint8_t* buffer) = 0;
virtual uint32_t sector_size() const = 0;
virtual uint64_t total_size() const = 0;
BAN::Vector<Partition>& partitions() { return m_partitions; }
@ -56,18 +56,4 @@ namespace Kernel
BAN::Vector<Partition> m_partitions;
};
class DiskIO
{
public:
static bool initialize();
static DiskIO& get();
private:
DiskIO();
void try_add_device(DiskDevice*);
private:
BAN::Vector<DiskDevice*> m_devices;
};
}

View File

@ -1,245 +0,0 @@
#include <BAN/Errors.h>
#include <kernel/ATA.h>
#include <kernel/Debug.h>
#include <kernel/IO.h>
#include <kernel/kprint.h>
#include <ctype.h>
#define ATA_IO_PORT_DATA 0
#define ATA_IO_PORT_ERROR 1
#define ATA_IO_PORT_FEATURES 1
#define ATA_IO_PORT_SECTOR_COUNT 2
#define ATA_IO_PORT_LBA_LOW 3
#define ATA_IO_PORT_LBA_MID 4
#define ATA_IO_PORT_LBA_HIGH 5
#define ATA_IO_PORT_DRIVE_SELECT 6
#define ATA_IO_PORT_STATUS 7
#define ATA_IO_PORT_COMMAND 7
#define ATA_CTL_PORT_STATUS 0
#define ATA_CTL_PORT_CONTROL 0
#define ATA_CTL_PORT_ADDRESS 1
#define ATA_COMMAND_READ_SECTORS 0x20
#define ATA_COMMAND_IDENTIFY 0xEC
#define ATA_COMMAND_FLUSH 0xE7
#define ATA_STATUS_ERR (1 << 0)
#define ATA_STATUS_DRQ (1 << 3)
#define ATA_STATUS_DF (1 << 3)
#define ATA_STATUS_BSY (1 << 7)
#define ATA_DEBUG_PRINT 0
namespace Kernel
{
enum class ATADeviceType
{
UNKNOWN,
PATA,
PATAPI,
SATA,
SATAPI,
};
static void soft_reset(uint16_t ctl_base)
{
IO::outb(ctl_base + ATA_CTL_PORT_CONTROL, 0b00000110);
PIT::sleep(2);
IO::outb(ctl_base + ATA_CTL_PORT_CONTROL, 0b00000010);
PIT::sleep(2);
}
static ATADeviceType detect_device_type(uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit)
{
soft_reset(ctl_base);
IO::outb(io_base + ATA_IO_PORT_DRIVE_SELECT, 0xA0 | slave_bit);
PIT::sleep(2);
uint8_t lba_mid = IO::inb(io_base + ATA_IO_PORT_LBA_MID);
uint8_t lba_high = IO::inb(io_base + ATA_IO_PORT_LBA_HIGH);
if (lba_mid == 0x00 && lba_high == 0x00)
return ATADeviceType::PATA;
if (lba_mid == 0x14 && lba_high == 0xEB)
return ATADeviceType::PATAPI;
if (lba_mid == 0x3C && lba_high == 0xC3)
return ATADeviceType::SATA;
if (lba_mid == 0x69 && lba_high == 0x96)
return ATADeviceType::SATAPI;
return ATADeviceType::UNKNOWN;
}
ATADevice* ATADevice::create(uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit)
{
uint8_t status = IO::inb(io_base + ATA_IO_PORT_STATUS);
if (status == 0xFF)
return nullptr;
ATADeviceType type = detect_device_type(io_base, ctl_base, slave_bit);
if (type == ATADeviceType::UNKNOWN)
return nullptr;
ATADevice* ata_device = nullptr;
switch (type)
{
case ATADeviceType::PATA:
ata_device = new PATADevice(io_base, ctl_base, slave_bit);
break;
case ATADeviceType::PATAPI:
#if ATA_DEBUG_PRINT
dwarnln("Unsupported PATAPI device");
#endif
break;
case ATADeviceType::SATA:
#if ATA_DEBUG_PRINT
dwarnln("Unsupported SATA device");
#endif
break;
case ATADeviceType::SATAPI:
#if ATA_DEBUG_PRINT
dwarnln("Unsupported SATAPI device");
#endif
break;
case ATADeviceType::UNKNOWN:
break;
}
if (ata_device && !ata_device->initialize())
{
delete ata_device;
ata_device = nullptr;
}
return ata_device;
}
bool PATADevice::initialize()
{
IO::outb(io_base() + ATA_IO_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
if (!wait_while_buzy() || !wait_for_transfer())
return false;
uint16_t response[256];
for (int i = 0; i < 256; i++)
response[i] = IO::inw(io_base() + ATA_IO_PORT_DATA);
m_lba_48 = response[83] & (1 << 10);
if (!(response[106] & (1 << 15))
&& (response[106] & (1 << 14))
&& (response[106] & (1 << 12)))
{
m_sector_words = ((uint32_t)response[117] << 16) | response[118];
dprintln("using {} sector size", m_sector_words * 2);
}
return true;
}
bool PATADevice::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer)
{
return read_lba28(lba, sector_count, buffer);
}
bool PATADevice::read_lba28(uint32_t lba, uint8_t sector_count, uint8_t* buffer)
{
// 1. Send 0xE0 for the "master" or 0xF0 for the "slave", ORed with the highest 4 bits of the LBA to port 0x1F6:
// outb(0x1F6, 0xE0 | (slavebit << 4) | ((LBA >> 24) & 0x0F))
IO::outb(io_base() + ATA_IO_PORT_DRIVE_SELECT, 0xE0 | slave_bit() | ((lba >> 24) & 0xF));
// 2. Send a NULL byte to port 0x1F1, if you like (it is ignored and wastes lots of CPU time): outb(0x1F1, 0x00)
// 3. Send the sectorcount to port 0x1F2: outb(0x1F2, (unsigned char) count)
IO::outb(io_base() + ATA_IO_PORT_SECTOR_COUNT, sector_count);
// 4. Send the low 8 bits of the LBA to port 0x1F3: outb(0x1F3, (unsigned char) LBA))
IO::outb(io_base() + ATA_IO_PORT_LBA_LOW, lba & 0xFF);
// 5. Send the next 8 bits of the LBA to port 0x1F4: outb(0x1F4, (unsigned char)(LBA >> 8))
IO::outb(io_base() + ATA_IO_PORT_LBA_MID, (lba >> 8) & 0xFF);
// 6. Send the next 8 bits of the LBA to port 0x1F5: outb(0x1F5, (unsigned char)(LBA >> 16))
IO::outb(io_base() + ATA_IO_PORT_LBA_HIGH, (lba >> 16) & 0xFF);
// 7. Send the "READ SECTORS" command (0x20) to port 0x1F7: outb(0x1F7, 0x20)
IO::outb(io_base() + ATA_IO_PORT_COMMAND, ATA_COMMAND_READ_SECTORS);
memset(buffer, 0, sector_count * m_sector_words * sizeof(uint16_t));
for (int i = 0; i < sector_count; i++)
{
// 8. Wait for an IRQ or poll.
if (!wait_while_buzy() || !wait_for_transfer())
return false;
// 9. Transfer 256 16-bit values, a uint16_t at a time, into your buffer from I/O port 0x1F0.
// (In assembler, REP INSW works well for this.)
for (size_t j = 0; j < m_sector_words; j++)
{
uint16_t word = IO::inw(io_base() + ATA_IO_PORT_DATA);
buffer[(i * m_sector_words + j) * 2 + 0] = word & 0xFF;
buffer[(i * m_sector_words + j) * 2 + 1] = word >> 8;
}
// 10. Then loop back to waiting for the next IRQ (or poll again -- see next note) for each successive sector.
}
return true;
}
bool PATADevice::wait_while_buzy()
{
uint64_t timeout = PIT::ms_since_boot() + 1000;
while (IO::inb(ctl_base() + ATA_CTL_PORT_STATUS) & ATA_STATUS_BSY)
{
if (PIT::ms_since_boot() > timeout)
{
#if ATA_DEBUG_PRINT
dwarnln("timeout on device 0x{3H} slave: {}", io_base(), !!slave_bit());
#endif
return false;
}
}
return true;
}
bool PATADevice::wait_for_transfer()
{
uint8_t status;
uint64_t timeout = PIT::ms_since_boot() + 1000;
while (!((status = IO::inb(ctl_base() + ATA_CTL_PORT_STATUS)) & ATA_STATUS_DRQ))
{
if (status & ATA_STATUS_ERR)
{
#if ATA_DEBUG_PRINT
dwarnln("error on device 0x{3H} slave: {}", io_base(), !!slave_bit());
#endif
return false;
}
if (status & ATA_STATUS_DF)
{
#if ATA_DEBUG_PRINT
dwarnln("drive fault on device 0x{3H} slave: {}", io_base(), !!slave_bit());
#endif
return false;
}
if (PIT::ms_since_boot() > timeout)
{
#if ATA_DEBUG_PRINT
dwarnln("timeout on device 0x{3H} slave: {}", io_base(), !!slave_bit());
#endif
return false;
}
}
return true;
}
void PATADevice::flush()
{
IO::outb(io_base() + ATA_IO_PORT_COMMAND, ATA_COMMAND_FLUSH);
wait_while_buzy();
}
}

View File

@ -318,7 +318,7 @@ namespace Kernel
return inodes;
}
BAN::ErrorOr<Ext2FS*> Ext2FS::create(DiskDevice::Partition& partition)
BAN::ErrorOr<Ext2FS*> Ext2FS::create(StorageDevice::Partition& partition)
{
Ext2FS* ext2fs = new Ext2FS(partition);
if (ext2fs == nullptr)
@ -344,8 +344,7 @@ namespace Kernel
uint32_t lba = 1024 / sector_size;
uint32_t sector_count = 1024 / sector_size;
if (!m_partition.read_sectors(lba, sector_count, superblock_buffer))
return BAN::Error::from_string("Could not read from partition");
TRY(m_partition.read_sectors(lba, sector_count, superblock_buffer));
memcpy(&m_superblock, superblock_buffer, sizeof(Ext2::Superblock));
}
@ -398,13 +397,11 @@ namespace Kernel
return BAN::Error::from_string("Could not allocate memory for block group descriptor table");
BAN::ScopeGuard _([block_group_descriptor_table_buffer] { kfree(block_group_descriptor_table_buffer); });
if (!m_partition.read_sectors(
TRY(m_partition.read_sectors(
block_group_descriptor_table_block * sectors_per_block,
block_group_descriptor_table_sector_count,
block_group_descriptor_table_buffer
))
return BAN::Error::from_string("Could not read from partition");
));
TRY(m_block_group_descriptors.resize(number_of_block_groups));
for (uint32_t i = 0; i < number_of_block_groups; i++)
@ -464,8 +461,7 @@ namespace Kernel
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
if (!m_partition.read_sectors(block * sectors_per_block, sectors_per_block, block_buffer.data()))
return BAN::Error::from_string("Could not read from partition");
TRY(m_partition.read_sectors(block * sectors_per_block, sectors_per_block, block_buffer.data()));
return block_buffer;
}

View File

@ -1,17 +1,22 @@
#include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <kernel/FS/Ext2.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/PCI.h>
#include <kernel/Storage/ATAController.h>
namespace Kernel
{
static VirtualFileSystem* s_instance = nullptr;
void VirtualFileSystem::initialize(BAN::RefCounted<Inode> root_inode)
BAN::ErrorOr<void> VirtualFileSystem::initialize()
{
ASSERT(s_instance == nullptr);
s_instance = new VirtualFileSystem(root_inode);
ASSERT(s_instance);
s_instance = new VirtualFileSystem();
if (s_instance == nullptr)
return BAN::Error::from_string("Could not allocate the Virtual File System");
return s_instance->initialize_impl();
}
VirtualFileSystem& VirtualFileSystem::get()
@ -25,6 +30,88 @@ namespace Kernel
return s_instance != nullptr;
}
BAN::ErrorOr<void> VirtualFileSystem::initialize_impl()
{
// Initialize all storage controllers
for (auto& device : PCI::get().devices())
{
if (device.class_code != 0x01)
continue;
switch (device.subclass)
{
case 0x0:
dwarnln("unsupported SCSI Bus Controller");
break;
case 0x1:
case 0x5:
TRY(m_storage_controllers.push_back(TRY(ATAController::create(device))));
break;
case 0x2:
dwarnln("unsupported Floppy Disk Controller");
break;
case 0x3:
dwarnln("unsupported IPI Bus Controller");
break;
case 0x4:
dwarnln("unsupported RAID Controller");
break;
case 0x6:
dwarnln("unsupported Serial ATA Controller");
break;
case 0x7:
dwarnln("unsupported Serial Attached SCSI Controller");
break;
case 0x8:
dwarnln("unsupported Non-Volatile Memory Controller");
break;
case 0x80:
dwarnln("unsupported Unknown Storage Controller");
break;
}
}
// Initialize partitions on all devices on found controllers
for (auto controller : m_storage_controllers)
{
for (auto device : controller->devices())
{
if (device->total_size() == 0)
continue;
auto result = device->initialize_partitions();
if (result.is_error())
{
dwarnln("{}", result.error());
continue;
}
for (auto& partition : device->partitions())
{
if (partition.name() == "banan-root"_sv)
{
if (m_root_inode)
dwarnln("multiple root partitions found");
else
{
auto ext2fs_or_error = Ext2FS::create(partition);
if (ext2fs_or_error.is_error())
dwarnln("{}", ext2fs_or_error.error());
else
// FIXME: We leave a dangling pointer to ext2fs. This might be okay since
// root fs sould probably be always mounted
m_root_inode = ext2fs_or_error.release_value()->root_inode();
}
}
}
}
}
if (m_root_inode)
return {};
return BAN::Error::from_string("Could not locate root partition");
}
BAN::ErrorOr<BAN::RefCounted<Inode>> VirtualFileSystem::from_absolute_path(BAN::StringView path)
{
if (path.front() != '/')

View File

@ -0,0 +1,287 @@
#include <kernel/IO.h>
#include <kernel/Storage/ATAController.h>
#include <kernel/kprint.h>
#define ATA_PRIMARY 0
#define ATA_SECONDARY 1
#define ATA_PORT_DATA 0x00
#define ATA_PORT_ERROR 0x00
#define ATA_PORT_SECTOR_COUNT 0x02
#define ATA_PORT_LBA0 0x03
#define ATA_PORT_LBA1 0x04
#define ATA_PORT_LBA2 0x05
#define ATA_PORT_DRIVE_SELECT 0x06
#define ATA_PORT_COMMAND 0x07
#define ATA_PORT_STATUS 0x07
#define ATA_PORT_CONTROL 0x10
#define ATA_PORT_ALT_STATUS 0x10
#define ATA_ERROR_AMNF 0x01
#define ATA_ERROR_TKZNF 0x02
#define ATA_ERROR_ABRT 0x04
#define ATA_ERROR_MCR 0x08
#define ATA_ERROR_IDNF 0x10
#define ATA_ERROR_MC 0x20
#define ATA_ERROR_UNC 0x40
#define ATA_ERROR_BBK 0x80
#define ATA_STATUS_ERR 0x01
#define ATA_STATUS_DF 0x02
#define ATA_STATUS_DRQ 0x08
#define ATA_STATUS_BSY 0x80
#define ATA_COMMAND_READ_SECTORS 0x20
#define ATA_COMMAND_IDENTIFY 0xEC
#define ATA_COMMAND_IDENTIFY_PACKET 0xA1
#define ATA_IDENTIFY_SIGNATURE 0
#define ATA_IDENTIFY_MODEL 27
#define ATA_IDENTIFY_CAPABILITIES 49
#define ATA_IDENTIFY_LBA_COUNT 60
#define ATA_IDENTIFY_COMMAND_SET 82
#define ATA_IDENTIFY_LBA_COUNT_EXT 100
#define ATA_IDENTIFY_SECTOR_INFO 106
#define ATA_IDENTIFY_SECTOR_WORDS 117
#define ATA_COMMANDSET_LBA48_SUPPORTED (1 << 26)
namespace Kernel
{
BAN::ErrorOr<ATAController*> ATAController::create(const PCIDevice& device)
{
ATAController* controller = new ATAController(device);
if (controller == nullptr)
return BAN::Error::from_string("Could not allocate memory for ATAController");
TRY(controller->initialize());
return controller;
}
BAN::ErrorOr<void> ATAController::initialize()
{
m_buses[ATA_PRIMARY].base = 0x1F0;
m_buses[ATA_PRIMARY].ctrl = 0x3F6;
m_buses[ATA_PRIMARY].write(ATA_PORT_CONTROL, 2);
m_buses[ATA_SECONDARY].base = 0x170;
m_buses[ATA_SECONDARY].ctrl = 0x376;
m_buses[ATA_SECONDARY].write(ATA_PORT_CONTROL, 2);
uint8_t prog_if = m_pci_device.read_byte(0x09);
if (prog_if & 0x01)
{
m_buses[ATA_PRIMARY].base = m_pci_device.read_dword(0x10) & 0xFFFFFFFC;
m_buses[ATA_PRIMARY].ctrl = m_pci_device.read_dword(0x14) & 0xFFFFFFFC;
}
if (prog_if & 0x04)
{
m_buses[ATA_SECONDARY].base = m_pci_device.read_dword(0x18) & 0xFFFFFFFC;
m_buses[ATA_SECONDARY].ctrl = m_pci_device.read_dword(0x1C) & 0xFFFFFFFC;
}
for (uint8_t bus_index = 0; bus_index < 2; bus_index++)
{
for (uint8_t device_index = 0; device_index < 2; device_index++)
{
ATABus& bus = m_buses[bus_index];
ATADevice& device = bus.devices[device_index];
device.type = ATADevice::Type::Unknown;
device.slave_bit = device_index << 4;
device.bus = &bus;
bus.write(ATA_PORT_DRIVE_SELECT, 0xA0 | device.slave_bit);
PIT::sleep(1);
bus.write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
PIT::sleep(1);
if (bus.read(ATA_PORT_STATUS) == 0)
continue;
uint8_t status = 0;
while (true)
{
status = bus.read(ATA_PORT_STATUS);
if (status & ATA_STATUS_ERR)
break;
if (!(status & ATA_STATUS_BSY) && (status && ATA_STATUS_DRQ))
break;
}
auto type = ATADevice::Type::ATA;
// Not a ATA device, maybe ATAPI
if (status & ATA_STATUS_ERR)
{
uint8_t lba1 = bus.read(ATA_PORT_LBA1);
uint8_t lba2 = bus.read(ATA_PORT_LBA2);
if (lba1 == 0x14 && lba2 == 0xEB)
type = ATADevice::Type::ATAPI;
else if (lba1 == 0x69 && lba2 == 0x96)
type = ATADevice::Type::ATAPI;
else
continue;
bus.write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY_PACKET);
PIT::sleep(1);
}
uint16_t buffer[256] {};
bus.read_buffer(ATA_PORT_DATA, buffer, 256);
device.type = type;
device.signature = *(uint16_t*)(buffer + ATA_IDENTIFY_SIGNATURE);
device.capabilities = *(uint16_t*)(buffer + ATA_IDENTIFY_CAPABILITIES);
device.command_set = *(uint32_t*)(buffer + ATA_IDENTIFY_COMMAND_SET);
device.sector_words = 256;
if ((buffer[ATA_IDENTIFY_SECTOR_INFO] & (1 << 15)) == 0 &&
(buffer[ATA_IDENTIFY_SECTOR_INFO] & (1 << 14)) != 0 &&
(buffer[ATA_IDENTIFY_SECTOR_INFO] & (1 << 12)) != 0)
{
device.sector_words = *(uint32_t*)(buffer + ATA_IDENTIFY_SECTOR_WORDS);
}
device.lba_count = 0;
if (device.command_set & ATA_COMMANDSET_LBA48_SUPPORTED)
device.lba_count = *(uint64_t*)(buffer + ATA_IDENTIFY_LBA_COUNT_EXT);
if (device.lba_count < (1 << 28))
device.lba_count = *(uint32_t*)(buffer + ATA_IDENTIFY_LBA_COUNT);
for (int i = 0; i < 20; i++)
{
uint16_t word = buffer[ATA_IDENTIFY_MODEL + i];
device.model[2 * i + 0] = word >> 8;
device.model[2 * i + 1] = word & 0xFF;
}
device.model[40] = 0;
TRY(m_devices.push_back(&device));
}
}
for (uint32_t i = 0; i < 2; i++)
{
for (uint32_t j = 0; j < 2; j++)
{
ATADevice& device = m_buses[i].devices[j];
if (device.type == ATADevice::Type::Unknown)
continue;
constexpr uint32_t words_per_mib = 1024 * 1024 / 2;
const char* device_type =
device.type == ATADevice::Type::ATA ? "ATA" :
device.type == ATADevice::Type::ATAPI ? "ATAPI" :
"Unknown";
dprintln("Found {} Drive ({} MiB) model {}", device_type, device.lba_count * device.sector_words / words_per_mib, device.model);
}
}
return {};
}
BAN::ErrorOr<void> ATADevice::read_sectors(uint64_t lba, uint8_t sector_count, uint8_t* buffer)
{
if (lba + sector_count > lba_count)
return BAN::Error::from_string("Attempted to read outside of the device boundaries");
if (lba < (1 << 28))
{
// LBA28
bus->write(ATA_PORT_DRIVE_SELECT, 0xE0 | slave_bit | ((lba >> 24) & 0x0F));
bus->write(ATA_PORT_SECTOR_COUNT, sector_count);
bus->write(ATA_PORT_LBA0, (uint8_t)(lba >> 0));
bus->write(ATA_PORT_LBA1, (uint8_t)(lba >> 8));
bus->write(ATA_PORT_LBA2, (uint8_t)(lba >> 16));
bus->write(ATA_PORT_COMMAND, ATA_COMMAND_READ_SECTORS);
for (uint32_t sector = 0; sector < sector_count; sector++)
{
TRY(bus->wait(true));
bus->read_buffer(ATA_PORT_DATA, (uint16_t*)buffer + sector * sector_words, sector_words);
}
}
else
{
// LBA48
ASSERT(false);
}
return {};
}
uint8_t ATABus::read(uint16_t port)
{
if (port <= 0x07)
return IO::inb(base + port);
if (0x10 <= port && port <= 0x11)
return IO::inb(ctrl + port - 0x10);
ASSERT_NOT_REACHED();
}
void ATABus::read_buffer(uint16_t port, uint16_t* buffer, size_t words)
{
if (port <= 0x07)
return IO::insw(base + port - 0x00, buffer, words);
if (0x10 <= port && port <= 0x11)
return IO::insw(ctrl + port - 0x10, buffer, words);
ASSERT_NOT_REACHED();
}
void ATABus::write(uint16_t port, uint8_t data)
{
if (port <= 0x07)
return IO::outb(base + port, data);
if (0x10 <= port && port <= 0x11)
return IO::outb(ctrl + port - 0x10, data);
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> ATABus::wait(bool wait_drq)
{
for (uint32_t i = 0; i < 4; i++)
read(ATA_PORT_ALT_STATUS);
uint8_t status = ATA_STATUS_BSY;
while (status & ATA_STATUS_BSY)
status = read(ATA_PORT_STATUS);
while (wait_drq && !(status & ATA_STATUS_DRQ))
{
if (status & ATA_STATUS_ERR)
return error();
if (status & ATA_STATUS_DF)
return BAN::Error::from_string("Device fault");
status = read(ATA_PORT_STATUS);
}
return {};
}
BAN::Error ATABus::error()
{
uint8_t err = read(ATA_PORT_ERROR);
if (err & ATA_ERROR_AMNF)
return BAN::Error::from_string("Address mark not found.");
if (err & ATA_ERROR_TKZNF)
return BAN::Error::from_string("Track zero not found.");
if (err & ATA_ERROR_ABRT)
return BAN::Error::from_string("Aborted command.");
if (err & ATA_ERROR_MCR)
return BAN::Error::from_string("Media change request.");
if (err & ATA_ERROR_IDNF)
return BAN::Error::from_string("ID not found.");
if (err & ATA_ERROR_MC)
return BAN::Error::from_string("Media changed.");
if (err & ATA_ERROR_UNC)
return BAN::Error::from_string("Uncorrectable data error.");
if (err & ATA_ERROR_BBK)
return BAN::Error::from_string("Bad Block detected.");
ASSERT_NOT_REACHED();
}
}

View File

@ -1,11 +1,9 @@
#include <BAN/ScopeGuard.h>
#include <BAN/StringView.h>
#include <kernel/ATA.h>
#include <kernel/FS/Ext2.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/DiskIO.h>
#include <kernel/kprint.h>
#include <kernel/PCI.h>
#include <kernel/Storage/StorageDevice.h>
#define ATA_DEVICE_PRIMARY 0x1F0
#define ATA_DEVICE_SECONDARY 0x170
@ -95,7 +93,7 @@ namespace Kernel
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
static uint32_t crc32_checksum(const uint8_t* data, size_t count)
@ -109,30 +107,12 @@ namespace Kernel
return crc32 ^ 0xFFFFFFFF;
}
template<typename T>
static T little_endian_to_host(const uint8_t* data)
{
T result = 0;
for (size_t i = 0; i < sizeof(T); i++)
result |= data[i] << (8 * i);
return result;
}
template<typename T>
static T big_endian_to_host(const uint8_t* data)
{
T result = 0;
for (size_t i = 0; i < sizeof(T); i++)
result |= data[i] << (8 * (sizeof(T) - i - 1));
return result;
}
static GUID parse_guid(const uint8_t* guid)
{
GUID result;
result.data1 = big_endian_to_host<uint32_t>(guid + 0);
result.data2 = big_endian_to_host<uint16_t>(guid + 4);
result.data3 = big_endian_to_host<uint16_t>(guid + 6);
result.data1 = BAN::Math::big_endian_to_host<uint32_t>(guid + 0);
result.data2 = BAN::Math::big_endian_to_host<uint16_t>(guid + 4);
result.data3 = BAN::Math::big_endian_to_host<uint16_t>(guid + 6);
memcpy(result.data4, guid + 8, 8);
return result;
}
@ -166,124 +146,87 @@ namespace Kernel
memset(&header, 0, sizeof(header));
memcpy(header.signature, lba1.data(), 8);
header.revision = little_endian_to_host<uint32_t>(lba1.data() + 8);
header.size = little_endian_to_host<uint32_t>(lba1.data() + 12);
header.crc32 = little_endian_to_host<uint32_t>(lba1.data() + 16);
header.my_lba = little_endian_to_host<uint64_t>(lba1.data() + 24);
header.first_lba = little_endian_to_host<uint64_t>(lba1.data() + 40);
header.last_lba = little_endian_to_host<uint64_t>(lba1.data() + 48);
header.revision = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 8);
header.size = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 12);
header.crc32 = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 16);
header.my_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 24);
header.first_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 40);
header.last_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 48);
header.guid = parse_guid(lba1.data() + 56);
header.partition_entry_lba = little_endian_to_host<uint64_t>(lba1.data() + 72);
header.partition_entry_count = little_endian_to_host<uint32_t>(lba1.data() + 80);
header.partition_entry_size = little_endian_to_host<uint32_t>(lba1.data() + 84);
header.partition_entry_array_crc32 = little_endian_to_host<uint32_t>(lba1.data() + 88);
header.partition_entry_lba = BAN::Math::little_endian_to_host<uint64_t>(lba1.data() + 72);
header.partition_entry_count = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 80);
header.partition_entry_size = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 84);
header.partition_entry_array_crc32 = BAN::Math::little_endian_to_host<uint32_t>(lba1.data() + 88);
return header;
}
bool DiskDevice::initialize_partitions()
static void utf8_encode(const uint16_t* codepoints, size_t count, char* out)
{
uint32_t len = 0;
while (*codepoints && count--)
{
uint16_t cp = *codepoints;
if (cp < 128)
{
out[len++] = cp & 0x7F;
}
else if (cp < 2048)
{
out[len++] = 0xC0 | ((cp >> 0x6) & 0x1F);
out[len++] = 0x80 | (cp & 0x3F);
}
else
{
out[len++] = 0xE0 | ((cp >> 0xC) & 0x0F);
out[len++] = 0x80 | ((cp >> 0x6) & 0x3F);
out[len++] = 0x80 | (cp & 0x3F);
}
codepoints++;
}
out[len] = 0;
}
BAN::ErrorOr<void> StorageDevice::initialize_partitions()
{
BAN::Vector<uint8_t> lba1(sector_size());
if (!read_sectors(1, 1, lba1.data()))
return false;
TRY(read_sectors(1, 1, lba1.data()));
GPTHeader header = parse_gpt_header(lba1);
if (!is_valid_gpt_header(header, sector_size()))
{
dprintln("invalid gpt header");
return false;
}
return BAN::Error::from_string("Invalid GPT header");
uint32_t size = header.partition_entry_count * header.partition_entry_size;
if (uint32_t remainder = size % sector_size())
size += sector_size() - remainder;
BAN::Vector<uint8_t> entry_array(size);
if (!read_sectors(header.partition_entry_lba, size / sector_size(), entry_array.data()))
return false;
TRY(read_sectors(header.partition_entry_lba, size / sector_size(), entry_array.data()));
if (!is_valid_gpt_crc32(header, lba1, entry_array))
{
dprintln("invalid crc3 in gpt header");
return false;
}
return BAN::Error::from_string("Invalid crc3 in the GPT header");
for (uint32_t i = 0; i < header.partition_entry_count; i++)
{
uint8_t* partition_data = entry_array.data() + header.partition_entry_size * i;
char utf8_name[36 * 3 + 1]; // 36 16-bit codepoints + nullbyte
utf8_encode((uint16_t*)(partition_data + 56), header.partition_entry_size - 56, utf8_name);
MUST(m_partitions.emplace_back(
*this,
parse_guid(partition_data + 0),
parse_guid(partition_data + 16),
little_endian_to_host<uint64_t>(partition_data + 32),
little_endian_to_host<uint64_t>(partition_data + 40),
little_endian_to_host<uint64_t>(partition_data + 48),
(const char*)(partition_data + 56)
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 32),
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 40),
BAN::Math::little_endian_to_host<uint64_t>(partition_data + 48),
utf8_name
));
}
return true;
return {};
}
static DiskIO* s_instance = nullptr;
bool DiskIO::initialize()
{
ASSERT(s_instance == nullptr);
s_instance = new DiskIO();
#if 1
for (DiskDevice* device : s_instance->m_devices)
{
for (auto& partition : device->partitions())
{
if (!partition.is_used())
continue;
if (memcmp(&partition.type(), "\x0F\xC6\x3D\xAF\x84\x83\x47\x72\x8E\x79\x3D\x69\xD8\x47\x7D\xE4", 16) == 0)
{
auto ext2fs = MUST(Ext2FS::create(partition));
VirtualFileSystem::initialize(ext2fs->root_inode());
}
}
}
#endif
return true;
}
DiskIO& DiskIO::get()
{
ASSERT(s_instance);
return *s_instance;
}
DiskIO::DiskIO()
{
try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, 0));
try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, ATA_DEVICE_SLAVE_BIT));
try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, 0));
try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, ATA_DEVICE_SLAVE_BIT));
}
void DiskIO::try_add_device(DiskDevice* device)
{
if (!device)
return;
if (!device->initialize())
{
delete device;
return;
}
if (!device->initialize_partitions())
{
delete device;
return;
}
MUST(m_devices.push_back(device));
}
DiskDevice::Partition::Partition(DiskDevice& device, const GUID& type, const GUID& guid, uint64_t start, uint64_t end, uint64_t attr, const char* name)
StorageDevice::Partition::Partition(StorageDevice& device, const GUID& type, const GUID& guid, uint64_t start, uint64_t end, uint64_t attr, const char* name)
: m_device(device)
, m_type(type)
, m_guid(guid)
@ -291,14 +234,16 @@ namespace Kernel
, m_lba_end(end)
, m_attributes(attr)
{
memcpy(m_name, name, sizeof(m_name));
memcpy(m_name, name, sizeof(m_name));
}
bool DiskDevice::Partition::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer)
BAN::ErrorOr<void> StorageDevice::Partition::read_sectors(uint64_t lba, uint8_t sector_count, uint8_t* buffer)
{
const uint32_t sectors_in_partition = m_lba_end - m_lba_start;
ASSERT(lba + sector_count < sectors_in_partition);
return m_device.read_sectors(m_lba_start + lba, sector_count, buffer);
if (lba + sector_count > sectors_in_partition)
return BAN::Error::from_string("Attempted to read outside of the partition boundaries");
TRY(m_device.read_sectors(m_lba_start + lba, sector_count, buffer));
return {};
}
}

View File

@ -1,5 +1,5 @@
#include <kernel/Debug.h>
#include <kernel/DiskIO.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/IDT.h>
#include <kernel/Input.h>
#include <kernel/InterruptController.h>
@ -7,6 +7,7 @@
#include <kernel/kprint.h>
#include <kernel/MMU.h>
#include <kernel/multiboot.h>
#include <kernel/PCI.h>
#include <kernel/PIC.h>
#include <kernel/PIT.h>
#include <kernel/Scheduler.h>
@ -92,18 +93,28 @@ extern "C" void kernel_main()
PIT::initialize();
dprintln("PIT initialized");
if (!PCI::initialize())
Kernel::panic("Could not initialize PCI");
dprintln("PCI initialized");
if (!Input::initialize())
return;
dprintln("8042 initialized");
dprintln("Could not initialize input drivers");
dprintln("Input initialized");
Scheduler::initialize();
Scheduler& scheduler = Scheduler::get();
MUST(scheduler.add_thread(BAN::Function<void()>(
[terminal_driver]
{
DiskIO::initialize();
dprintln("Disk IO initialized");
//PCI::get().initialize_controllers();
//StorageDeviceManager::initialize();
if (auto error = VirtualFileSystem::initialize(); error.is_error())
{
derrorln("{}", error.error());
return;
}
auto font_or_error = Font::load("/usr/share/fonts/zap-ext-vga16.psf");
if (font_or_error.is_error())
dprintln("{}", font_or_error.error());