[Denali] Add identify device command.

This commit is contained in:
Drew Galbraith 2023-12-08 14:30:08 -08:00
parent 81469496d1
commit d90c8eb1ef
9 changed files with 109 additions and 44 deletions

View File

@ -1,6 +1,6 @@
add_executable(denali
ahci/ahci_device.cpp
ahci/ahci_controller.cpp
ahci/ahci_port.cpp
ahci/command.cpp
denali.cpp
denali_server.cpp

View File

@ -64,7 +64,7 @@ const uint32_t kInterrupt_DMA_FIS = (1 << 2);
const uint32_t kInterrupt_DeviceBits_FIS = (1 << 3);
const uint32_t kInterrupt_Unknown_FIS = (1 << 4);
struct AhciPort {
struct AhciPortHba {
uint64_t command_list_base;
uint64_t fis_base;
uint32_t interrupt_status;
@ -190,6 +190,9 @@ struct PioSetupFis {
uint8_t rsv4[2]; // Reserved
} __attribute__((packed));
const uint8_t kIdentifyDevice = 0xEC;
const uint8_t kDmaReadExt = 0x25;
struct HostToDeviceRegisterFis {
uint8_t fis_type; // FIS_TYPE_REG_H2D
uint8_t pmp_and_c;
@ -216,6 +219,7 @@ struct HostToDeviceRegisterFis {
// DWORD 4
uint32_t reserved; // Reserved
} __attribute__((packed));
struct DeviceToHostRegisterFis {
// DWORD 0
uint8_t fis_type; // FIS_TYPE_REG_D2H

View File

@ -33,7 +33,7 @@ glcr::ErrorOr<glcr::UniquePtr<AhciController>> AhciController::Init(
return driver;
}
glcr::ErrorOr<AhciDevice*> AhciController::GetDevice(uint64_t id) {
glcr::ErrorOr<AhciPort*> AhciController::GetDevice(uint64_t id) {
if (id >= num_ports_) {
return glcr::INVALID_ARGUMENT;
}
@ -237,13 +237,18 @@ glcr::ErrorCode AhciController::LoadDevices() {
uint64_t port_addr =
reinterpret_cast<uint64_t>(ahci_hba_) + 0x100 + (0x80 * i);
AhciPort* port = reinterpret_cast<AhciPort*>(port_addr);
AhciPortHba* port = reinterpret_cast<AhciPortHba*>(port_addr);
if ((port->sata_status & 0x103) != 0x103) {
continue;
}
devices_[i] = new AhciDevice(reinterpret_cast<AhciPort*>(port_addr));
devices_[i]->DumpInfo();
devices_[i] = new AhciPort(reinterpret_cast<AhciPortHba*>(port_addr));
if (devices_[i]->IsSata()) {
IdentifyDeviceCommand identify(devices_[i].get());
devices_[i]->IssueCommand(&identify);
identify.WaitComplete();
}
}
return glcr::OK;
}

View File

@ -6,7 +6,7 @@
#include <ztypes.h>
#include "ahci/ahci.h"
#include "ahci/ahci_device.h"
#include "ahci/ahci_port.h"
class AhciController {
public:
@ -16,7 +16,7 @@ class AhciController {
void InterruptLoop();
glcr::ErrorOr<AhciDevice*> GetDevice(uint64_t id);
glcr::ErrorOr<AhciPort*> GetDevice(uint64_t id);
void DumpCapabilities();
void DumpPorts();
@ -27,7 +27,7 @@ class AhciController {
mmth::OwnedMemoryRegion ahci_region_;
volatile AhciHba* ahci_hba_ = nullptr;
glcr::UniquePtr<AhciDevice> devices_[32];
glcr::UniquePtr<AhciPort> devices_[32];
Thread irq_thread_;
uint64_t irq_port_cap_ = 0;

View File

@ -1,10 +1,10 @@
#include "ahci/ahci_device.h"
#include "ahci/ahci_port.h"
#include <glacier/status/error.h>
#include <mammoth/util/debug.h>
#include <zcall.h>
AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) {
AhciPort::AhciPort(AhciPortHba* port) : port_struct_(port) {
if ((port_struct_->sata_status & 0x103) != 0x103) {
crash("Creating device on port without a device",
glcr::FAILED_PRECONDITION);
@ -34,14 +34,14 @@ AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) {
(paddr + 0x500) + (0x100 * i);
commands_[i] = nullptr;
}
port_struct_->interrupt_enable =
kInterrupt_D2H_FIS | kInterrupt_PIO_FIS | kInterrupt_DMA_FIS |
kInterrupt_DeviceBits_FIS | kInterrupt_Unknown_FIS;
port_struct_->interrupt_enable = 0xFFFFFFFF;
// kInterrupt_D2H_FIS | kInterrupt_PIO_FIS | kInterrupt_DMA_FIS |
// kInterrupt_DeviceBits_FIS | kInterrupt_Unknown_FIS;
port_struct_->sata_error = -1;
port_struct_->command |= kCommand_Start;
}
glcr::ErrorCode AhciDevice::IssueCommand(Command* command) {
glcr::ErrorCode AhciPort::IssueCommand(Command* command) {
uint64_t slot;
for (slot = 0; slot < 32; slot++) {
if (commands_[slot] == nullptr) {
@ -56,7 +56,7 @@ glcr::ErrorCode AhciDevice::IssueCommand(Command* command) {
command->PopulatePrdt(command_tables_[slot].prdt);
command_list_->command_headers[slot].command =
(sizeof(HostToDeviceRegisterFis) / 2) & 0x1F;
(sizeof(HostToDeviceRegisterFis) / 2) & 0x1F | (1 << 7);
command_list_->command_headers[slot].prd_table_length = 1;
command_list_->command_headers[slot].prd_byte_count = 0;
@ -68,7 +68,7 @@ glcr::ErrorCode AhciDevice::IssueCommand(Command* command) {
return glcr::OK;
}
void AhciDevice::DumpInfo() {
void AhciPort::DumpInfo() {
dbgln("Comlist: {x}", port_struct_->command_list_base);
dbgln("FIS: {x}", port_struct_->fis_base);
dbgln("Command: {x}", port_struct_->command);
@ -88,7 +88,7 @@ bool CheckFisType(FIS_TYPE expected, uint8_t actual) {
return false;
}
void AhciDevice::HandleIrq() {
void AhciPort::HandleIrq() {
uint32_t int_status = port_struct_->interrupt_status;
port_struct_->interrupt_status = int_status;
@ -103,7 +103,9 @@ void AhciDevice::HandleIrq() {
}
if (fis.error) {
dbgln("D2H err: {x}", fis.error);
dbgln("status: {x}", fis.status);
dbgln("Error: {x}", port_struct_->sata_error);
has_error = true;
}
}
@ -114,6 +116,7 @@ void AhciDevice::HandleIrq() {
if (!CheckFisType(FIS_TYPE_PIO_SETUP, fis.fis_type)) {
return;
}
dbgln("Count: {x} {x} {x}", fis.counth, fis.countl, fis.e_status);
if (fis.error) {
dbgln("PIO err: {x}", fis.error);
dbgln("status: {x}", fis.status);

View File

@ -8,25 +8,26 @@
#include "ahci/ahci.h"
#include "ahci/command.h"
class AhciDevice {
class AhciPort {
public:
AhciDevice() {}
AhciPort() {}
// Caller retains ownership of the pointer.
AhciDevice(AhciPort* port_struct);
AhciPort(AhciPortHba* port_struct);
void DumpInfo();
bool IsSata() { return port_struct_->signature == 0x101; }
bool IsInit() { return port_struct_ != nullptr && command_structures_; }
glcr::ErrorCode IssueCommand(Command* command);
void HandleIrq();
AhciDevice(const AhciDevice&) = delete;
AhciDevice& operator=(const AhciDevice&) = delete;
AhciPort(const AhciPort&) = delete;
AhciPort& operator=(const AhciPort&) = delete;
private:
volatile AhciPort* port_struct_ = nullptr;
volatile AhciPortHba* port_struct_ = nullptr;
mmth::OwnedMemoryRegion command_structures_;
volatile CommandList* command_list_ = nullptr;

View File

@ -1,5 +1,7 @@
#include "ahci/command.h"
#include <mammoth/util/debug.h>
#include "ahci/ahci.h"
namespace {
@ -17,12 +19,46 @@ void* memcpy(void* dest, const void* src, uint64_t count) {
Command::~Command() {}
void Command::SignalComplete() {
OnComplete();
callback_semaphore_.Signal();
}
void Command::WaitComplete() { callback_semaphore_.Wait(); }
void IdentifyDeviceCommand::PopulateFis(uint8_t* command_fis) {
HostToDeviceRegisterFis fis __attribute__((aligned(16))){
.fis_type = FIS_TYPE_REG_H2D,
.pmp_and_c = 0x80,
.command = kIdentifyDevice,
.device = 0,
};
memcpy(command_fis, &fis, sizeof(fis));
}
void IdentifyDeviceCommand::PopulatePrdt(PhysicalRegionDescriptor* prdt) {
prdt[0].region_address = paddr_;
prdt[0].byte_count = 0x200 - 1;
dbgln("paddr: {x}", paddr_);
uint16_t* ident = reinterpret_cast<uint16_t*>(identify_.vaddr());
dbgln("vaddr: {x}", identify_.vaddr());
for (uint32_t i = 0; i < 256; i++) {
ident[i] = 0;
}
}
void IdentifyDeviceCommand::OnComplete() {
uint16_t* ident = reinterpret_cast<uint16_t*>(identify_.vaddr());
uint32_t* sector_size = reinterpret_cast<uint32_t*>(ident + 117);
dbgln("Sector size: {}", *sector_size);
uint64_t* lbas = reinterpret_cast<uint64_t*>(ident + 100);
dbgln("LBA Count: {}", *lbas);
// TODO tell the port its sector size.
}
DmaReadCommand::DmaReadCommand(uint64_t lba, uint64_t sector_cnt,
uint64_t paddr)
: lba_(lba),
sector_cnt_(sector_cnt),
paddr_(paddr),
callback_semaphore_() {}
: lba_(lba), sector_cnt_(sector_cnt), paddr_(paddr) {}
DmaReadCommand::~DmaReadCommand() {}
@ -30,7 +66,7 @@ void DmaReadCommand::PopulateFis(uint8_t* command_fis) {
HostToDeviceRegisterFis fis{
.fis_type = FIS_TYPE_REG_H2D,
.pmp_and_c = 0x80,
.command = 0x25,
.command = kDmaReadExt,
.featurel = 0,
.lba0 = static_cast<uint8_t>(lba_ & 0xFF),
@ -50,14 +86,10 @@ void DmaReadCommand::PopulateFis(uint8_t* command_fis) {
.reserved = 0,
};
uint64_t bytes = sector_cnt_ * 512;
memcpy(command_fis, &fis, sizeof(fis));
}
void DmaReadCommand::PopulatePrdt(PhysicalRegionDescriptor* prdt) {
prdt[0].region_address = paddr_;
prdt[0].byte_count = sector_cnt_ * 512;
}
void DmaReadCommand::SignalComplete() { callback_semaphore_.Signal(); }
void DmaReadCommand::WaitComplete() { callback_semaphore_.Wait(); }

View File

@ -1,17 +1,43 @@
#pragma once
#include <mammoth/sync/semaphore.h>
#include <mammoth/util/memory_region.h>
#include <stdint.h>
#include "ahci/ahci.h"
class AhciPort;
class Command {
public:
Command() = default;
virtual ~Command();
virtual void PopulateFis(uint8_t* command_fis) = 0;
virtual void PopulatePrdt(PhysicalRegionDescriptor* prdt) = 0;
virtual void WaitComplete() = 0;
virtual void SignalComplete() = 0;
void WaitComplete();
void SignalComplete();
virtual void OnComplete() {}
private:
// TODO: Make this owned by the device so that we don't have to create a new
// one with the kernel every time a command is issued.
mmth::Semaphore callback_semaphore_;
};
class IdentifyDeviceCommand : public Command {
public:
IdentifyDeviceCommand(AhciPort* port) : port_(port) {}
virtual void PopulateFis(uint8_t* command_fis) override;
virtual void PopulatePrdt(PhysicalRegionDescriptor* prdt) override;
virtual void OnComplete() override;
private:
AhciPort* port_;
uint64_t paddr_;
mmth::OwnedMemoryRegion identify_ =
mmth::OwnedMemoryRegion::ContiguousPhysical(0x200, &paddr_);
};
class DmaReadCommand : public Command {
@ -23,14 +49,8 @@ class DmaReadCommand : public Command {
void PopulateFis(uint8_t* command_fis) override;
void PopulatePrdt(PhysicalRegionDescriptor* prdt) override;
void WaitComplete() override;
void SignalComplete() override;
private:
uint64_t lba_;
uint64_t sector_cnt_;
uint64_t paddr_;
// TODO: Make this owned by the device so that we don't have to create a new
// one with the kernel every time a command is issued.
mmth::Semaphore callback_semaphore_;
};

View File

@ -14,7 +14,7 @@ glcr::ErrorOr<glcr::UniquePtr<DenaliServer>> DenaliServer::Create(
glcr::Status DenaliServer::HandleRead(const ReadRequest& req,
ReadResponse& resp) {
ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id()));
ASSIGN_OR_RETURN(AhciPort * device, driver_.GetDevice(req.device_id()));
uint64_t paddr;
mmth::OwnedMemoryRegion region =
@ -33,7 +33,7 @@ glcr::Status DenaliServer::HandleRead(const ReadRequest& req,
glcr::Status DenaliServer::HandleReadMany(const ReadManyRequest& req,
ReadResponse& resp) {
ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id()));
ASSIGN_OR_RETURN(AhciPort * device, driver_.GetDevice(req.device_id()));
if (req.lba().size() != req.sector_cnt().size()) {
return glcr::InvalidArgument("LBA and Sector Cnt must be the same length.");