From 0f0e39d1e9ab9f3e11c846d6d5edef80c5263001 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Mon, 12 Jun 2023 19:20:51 -0700 Subject: [PATCH] Further parse AHCI information. Send an IDENTIFY command to each drive and set up a hook to handle interrupts. --- sys/denali/CMakeLists.txt | 1 + sys/denali/ahci/ahci.h | 157 ++++++++++++++++++++++++++++++++ sys/denali/ahci/ahci_device.cpp | 108 ++++++++++++++++++++++ sys/denali/ahci/ahci_device.h | 29 ++++++ sys/denali/ahci/ahci_driver.cpp | 116 +++++++++++++++++++---- sys/denali/ahci/ahci_driver.h | 70 +++----------- sys/denali/denali.cpp | 7 +- zion/CMakeLists.txt | 1 + zion/capability/capability.cpp | 8 ++ zion/capability/capability.h | 1 + zion/include/zcall.h | 18 ++++ zion/interrupt/apic.cpp | 24 +++-- zion/interrupt/interrupt.cpp | 37 +++++++- zion/interrupt/interrupt.h | 5 + zion/memory/physical_memory.cpp | 36 ++++++++ zion/memory/physical_memory.h | 1 + zion/object/memory_object.h | 1 + zion/object/port.cpp | 50 ++++++++++ zion/object/port.h | 22 +++++ zion/object/process.cpp | 7 ++ zion/object/process.h | 2 + zion/scheduler/scheduler.cpp | 2 + zion/syscall/syscall.cpp | 44 ++++++++- zion/usr/zcall.cpp | 46 +++++++++- zion/usr/zcall_internal.h | 18 ++++ 25 files changed, 721 insertions(+), 90 deletions(-) create mode 100644 sys/denali/ahci/ahci.h create mode 100644 sys/denali/ahci/ahci_device.cpp create mode 100644 sys/denali/ahci/ahci_device.h create mode 100644 zion/object/port.cpp create mode 100644 zion/object/port.h diff --git a/sys/denali/CMakeLists.txt b/sys/denali/CMakeLists.txt index f4be0d5..8c03131 100644 --- a/sys/denali/CMakeLists.txt +++ b/sys/denali/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(denali + ahci/ahci_device.cpp ahci/ahci_driver.cpp denali.cpp) diff --git a/sys/denali/ahci/ahci.h b/sys/denali/ahci/ahci.h new file mode 100644 index 0000000..002a938 --- /dev/null +++ b/sys/denali/ahci/ahci.h @@ -0,0 +1,157 @@ +#pragma once + +#include + +struct PciDeviceHeader { + uint16_t vendor_id; + uint16_t device_id; + uint16_t command_reg; + uint16_t status_reg; + uint8_t revision; + uint8_t prog_interface; + uint8_t subclass; + uint8_t class_code; + uint8_t cache_line_size; + uint8_t latency_timer; + uint8_t header_type; + uint8_t bist; + uint32_t bars[5]; + uint32_t abar; + uint32_t reserved0; + uint32_t subsystem_id; + uint32_t expansion_rom; + uint8_t cap_ptr; + uint8_t reserved1[7]; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_grant; + uint8_t max_latency; +} __attribute__((packed)); + +struct PciMsiCap { + uint8_t cap_id; + uint8_t next_offset; + uint8_t message_control; + uint8_t reserved; + uint64_t message_address; + uint16_t message_data; +}; + +struct AhciHba { + uint32_t capabilities; + uint32_t global_host_control; + uint32_t interrupt_status; + uint32_t port_implemented; + uint32_t version; + uint32_t ccc_ctl; // 0x14, Command completion coalescing control + uint32_t ccc_pts; // 0x18, Command completion coalescing ports + uint32_t em_loc; // 0x1C, Enclosure management location + uint32_t em_ctl; // 0x20, Enclosure management control + uint32_t capabilities_ext; + uint32_t bohc; // 0x28, BIOS/OS handoff control and status +} __attribute__((packed)); + +struct AhciPort { + uint64_t command_list_base; + uint64_t fis_base; + uint32_t interrupt_status; + uint32_t interrupt_enable; + uint32_t command; + uint32_t reserved; + uint32_t task_file_data; + uint32_t signature; + uint32_t sata_status; + uint32_t sata_control; + uint32_t sata_error; + uint32_t sata_active; + uint32_t command_issue; + uint32_t sata_notification; + uint32_t fis_based_switching_ctl; + uint32_t device_sleep; +} __attribute__((packed)); + +struct CommandHeader { + uint16_t command; + uint16_t prd_table_length; + uint32_t prd_byte_count; + uint64_t command_table_base_addr; + uint64_t reserved1; + uint64_t reserved2; +} __attribute__((packed)); + +struct CommandList { + CommandHeader command_headers[32]; +} __attribute__((packed)); + +struct PhysicalRegionDescriptor { + uint64_t region_address; + uint32_t reserved; + // bit 0 must be one. + // 21:0 is byte count + // 31 is Interrupt on Completion + uint32_t byte_count; +}; + +struct CommandTable { + uint8_t command_fis[64]; + uint8_t atapi_command[16]; + uint8_t reserved[48]; + PhysicalRegionDescriptor prds[65535]; +} __attribute__((packed)); + +typedef enum { + FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device + FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host + FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host + FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional + FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional + FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional + FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host + FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host +} FIS_TYPE; + +struct DmaFis {}; + +struct PioSetupFis {}; +struct HostToDeviceRegisterFis { + uint8_t fis_type; // FIS_TYPE_REG_H2D + uint8_t pmp_and_c; + uint8_t command; // Command register + uint8_t featurel; // Feature register, 7:0 + + // DWORD 1 + uint8_t lba0; // LBA low register, 7:0 + uint8_t lba1; // LBA mid register, 15:8 + uint8_t lba2; // LBA high register, 23:16 + uint8_t device; // Device register + + // DWORD 2 + uint8_t lba3; // LBA register, 31:24 + uint8_t lba4; // LBA register, 39:32 + uint8_t lba5; // LBA register, 47:40 + uint8_t featureh; // Feature register, 15:8 + + // DWORD 3 + uint16_t count; + uint8_t icc; // Isochronous command completion + uint8_t control; // Control register + + // DWORD 4 + uint32_t reserved; // Reserved +}; +struct DeviceToHostRegisterFis {}; +struct SetDeviceBitsFis {}; + +struct ReceivedFis { + DmaFis dma_fis; + uint32_t reserved0; + + PioSetupFis pio_set_fis; + uint32_t reserved1[3]; + + DeviceToHostRegisterFis device_to_host_register_fis; + uint32_t reserved2; + + SetDeviceBitsFis set_device_bits_fis; + uint8_t unknown_fis[64]; +} __attribute__((packed)); diff --git a/sys/denali/ahci/ahci_device.cpp b/sys/denali/ahci/ahci_device.cpp new file mode 100644 index 0000000..d39e695 --- /dev/null +++ b/sys/denali/ahci/ahci_device.cpp @@ -0,0 +1,108 @@ +#include "ahci/ahci_device.h" + +#include +#include +#include + +AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) { + if ((port_struct_->sata_status & 0x103) != 0x103) { + return; + } + uint64_t cl_page = port_struct_->command_list_base & (~0xFFF); + uint64_t fis_page = port_struct_->fis_base & (~0xFFF); + + if (cl_page != fis_page) { + crash("Non adjacent cl & fis", Z_ERR_UNIMPLEMENTED); + } + + check(ZMemoryObjectCreatePhysical(cl_page, 0x1000, &vmmo_cap_)); + + uint64_t vaddr; + check(ZAddressSpaceMap(Z_INIT_VMAS_SELF, 0, vmmo_cap_, &vaddr)); + + uint64_t cl_off = port_struct_->command_list_base & 0xFFF; + command_list_ = reinterpret_cast(vaddr + cl_off); + + uint64_t fis_off = port_struct_->fis_base & 0xFFF; + received_fis_ = reinterpret_cast(vaddr + fis_off); + + // FIXME: Hacky + uint64_t ct_off = + command_list_->command_headers[0].command_table_base_addr & 0xFFF; + command_table_ = reinterpret_cast(vaddr + ct_off); + + port_struct_->interrupt_enable = 0xFFFFFFFF; +} + +z_err_t AhciDevice::SendIdentify(uint16_t** result) { + HostToDeviceRegisterFis fis{ + .fis_type = FIS_TYPE_REG_H2D, + .pmp_and_c = 0x80, + .command = 0xEC, + .featurel = 0, + + .lba0 = 0, + .lba1 = 0, + .lba2 = 0, + .device = 0, + + .lba3 = 0, + .lba4 = 0, + .lba5 = 0, + .featureh = 0, + + .count = 0, + .icc = 0, + .control = 0, + + .reserved = 0, + }; + + command_list_->command_headers[0].command = (sizeof(fis) / 2) & 0x1F; + command_list_->command_headers[0].prd_table_length = 1; + + memcpy(command_table_->command_fis, &fis, sizeof(fis)); + + port_struct_->command_issue |= 1; + + uint64_t vmmo_cap, paddr; + RET_ERR(ZMemoryObjectCreateContiguous(512, &vmmo_cap, &paddr)); + + command_table_->prds[0].region_address = paddr; + command_table_->prds[0].byte_count = 512; + + uint64_t vaddr; + RET_ERR(ZAddressSpaceMap(Z_INIT_VMAS_SELF, 0, vmmo_cap, &vaddr)); + + *result = reinterpret_cast(vaddr); + + return Z_OK; +} + +void AhciDevice::DumpInfo() { + dbgln("Comlist: %lx", port_struct_->command_list_base); + dbgln("FIS: %lx", port_struct_->fis_base); + dbgln("Command: %x", port_struct_->command); + dbgln("Signature: %x", port_struct_->signature); + dbgln("SATA status: %x", port_struct_->sata_status); + dbgln("Int status: %x", port_struct_->interrupt_status); + dbgln("Int enable: %x", port_struct_->interrupt_enable); + dbgln("Int enable: %x", port_struct_->interrupt_enable); + + // Just dump one command info for now. + for (uint64_t i = 0; i < 1; i++) { + dbgln("Command Header: %u", i); + dbgln("Command %x", command_list_->command_headers[i].command); + dbgln("PRD Len: %x", command_list_->command_headers[i].prd_table_length); + dbgln("Command Table %lx", + command_list_->command_headers[i].command_table_base_addr); + } +} + +void AhciDevice::HandleIrq() { + uint64_t int_status = port_struct_->interrupt_status; + // FIXME: Probably only clear the interrupts we know how to handle. + port_struct_->interrupt_status = int_status; + + dbgln("int receieved: %x", int_status); +} diff --git a/sys/denali/ahci/ahci_device.h b/sys/denali/ahci/ahci_device.h new file mode 100644 index 0000000..686818d --- /dev/null +++ b/sys/denali/ahci/ahci_device.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "ahci/ahci.h" + +class AhciDevice { + public: + AhciDevice() {} + // Caller retains ownership of the pointer. + AhciDevice(AhciPort* port_struct); + + void DumpInfo(); + + bool IsInit() { return port_struct_ != nullptr && vmmo_cap_ != 0; } + + // Result will point to a 512 byte (256 word array). + z_err_t SendIdentify(uint16_t** result); + + void HandleIrq(); + + private: + AhciPort* port_struct_ = nullptr; + uint64_t vmmo_cap_ = 0; + + CommandList* command_list_ = nullptr; + ReceivedFis* received_fis_ = nullptr; + CommandTable* command_table_ = nullptr; +}; diff --git a/sys/denali/ahci/ahci_driver.cpp b/sys/denali/ahci/ahci_driver.cpp index e6e671e..1cebb0e 100644 --- a/sys/denali/ahci/ahci_driver.cpp +++ b/sys/denali/ahci/ahci_driver.cpp @@ -9,13 +9,30 @@ namespace { const uint64_t kSataPciPhys = 0xB00FA000; const uint64_t kPciSize = 0x1000; +const uint64_t kGhc_InteruptEnable = 0x2; + +void interrupt_thread(void* void_driver) { + AhciDriver* driver = static_cast(void_driver); + dbgln("this %lx", driver); + + driver->InterruptLoop(); + + crash("Driver returned from interrupt loop", Z_ERR_UNIMPLEMENTED); +} + } // namespace z_err_t AhciDriver::Init() { RET_ERR(LoadPciDeviceHeader()); + RET_ERR(LoadCapabilities()); dbgln("ABAR: %x", pci_device_header_->abar); + dbgln("Interrupt line: %x", pci_device_header_->interrupt_line); + dbgln("Interrupt pin: %x", pci_device_header_->interrupt_pin); + RET_ERR(RegisterIrq()); RET_ERR(LoadHbaRegisters()); dbgln("Version: %x", ahci_hba_->version); + ahci_hba_->global_host_control |= kGhc_InteruptEnable; + RET_ERR(LoadDevices()); DumpCapabilities(); DumpPorts(); return Z_OK; @@ -26,7 +43,7 @@ void AhciDriver::DumpCapabilities() { uint32_t caps = ahci_hba_->capabilities; dbgln("Num Ports: %u", (caps & 0x1F) + 1); - dbgln("Num Command Slots: %u", (caps & 0x1F00) >> 8); + dbgln("Num Command Slots: %u", ((caps & 0x1F00) >> 8) + 1); if (caps & 0x20) { dbgln("External SATA"); } @@ -97,33 +114,33 @@ void AhciDriver::DumpCapabilities() { if (caps & 0x10) { dbgln("Aggressive device sleep management"); } + + dbgln("Control %x", ahci_hba_->global_host_control); } void AhciDriver::DumpPorts() { - dbgln("Ports implemented %x", ahci_hba_->port_implemented); - - uint64_t port_index = 0; - uint32_t ports_implemented = ahci_hba_->port_implemented; - while (ports_implemented) { - if (!(ports_implemented & 0x1)) { - ports_implemented >>= 1; - port_index++; + for (uint64_t i = 0; i < 6; i++) { + AhciDevice& dev = devices_[i]; + if (!dev.IsInit()) { continue; } - uint64_t port_addr = - reinterpret_cast(ahci_hba_) + 0x100 + (0x80 * port_index); - AhciPort* port = reinterpret_cast(port_addr); dbgln(""); - dbgln("Port %u:", port_index); - dbgln("Comlist: %lx", port->command_list_base); - dbgln("FIS: %lx", port->fis_base); - dbgln("Command: %x", port->command); - dbgln("Signature: %x", port->signature); - dbgln("SATA status: %x", port->sata_status); + dbgln("Port %u:", i); + dev.DumpInfo(); + } +} - ports_implemented >>= 1; - port_index++; +void AhciDriver::InterruptLoop() { + dbgln("this %lx", this); + while (true) { + uint64_t type, bytes, caps; + check(ZPortRecv(irq_port_cap_, 0, 0, 0, 0, &type, &bytes, &caps)); + for (uint64_t i = 0; i < 6; i++) { + if (devices_[i].IsInit()) { + devices_[i].HandleIrq(); + } + } } } @@ -137,6 +154,46 @@ z_err_t AhciDriver::LoadPciDeviceHeader() { return Z_OK; } +z_err_t AhciDriver::LoadCapabilities() { + if (!(pci_device_header_->status_reg & 0x10)) { + dbgln("No caps!"); + return Z_ERR_INVALID; + } + uint8_t* base = reinterpret_cast(pci_device_header_); + uint16_t offset = pci_device_header_->cap_ptr; + do { + uint16_t* cap = reinterpret_cast(base + offset); + switch (*cap & 0xFF) { + case 0x01: + dbgln("Power Management"); + break; + case 0x05: + dbgln("MSI"); + break; + case 0x12: + dbgln("SATA"); + break; + default: + dbgln("Unrecognized cap"); + break; + } + + offset = (*cap & 0xFF00) >> 8; + } while (offset); + return Z_OK; +} + +z_err_t AhciDriver::RegisterIrq() { + if (pci_device_header_->interrupt_pin == 0) { + crash("Can't register IRQ without a pin num", Z_INVALID); + } + uint64_t irq_num = Z_IRQ_PCI_BASE + pci_device_header_->interrupt_pin - 1; + RET_ERR(ZIrqRegister(irq_num, &irq_port_cap_)); + dbgln("this %lx", this); + irq_thread_ = Thread(interrupt_thread, this); + return Z_OK; +} + z_err_t AhciDriver::LoadHbaRegisters() { uint64_t vmmo_cap; RET_ERR( @@ -147,3 +204,22 @@ z_err_t AhciDriver::LoadHbaRegisters() { ahci_hba_ = reinterpret_cast(vaddr); return Z_OK; } + +z_err_t AhciDriver::LoadDevices() { + // FIXME: Don't set this up so we hardcode 6 devices. + for (uint8_t i = 0; i < 6; i++) { + if (!(ahci_hba_->port_implemented & (1 << i))) { + continue; + } + uint64_t port_addr = + reinterpret_cast(ahci_hba_) + 0x100 + (0x80 * i); + devices_[i] = AhciDevice(reinterpret_cast(port_addr)); + if (!devices_[i].IsInit()) { + continue; + } + dbgln("Identify %u", i); + uint16_t* identify; + devices_[i].SendIdentify(&identify); + } + return Z_OK; +} diff --git a/sys/denali/ahci/ahci_driver.h b/sys/denali/ahci/ahci_driver.h index a15f90b..0cebe37 100644 --- a/sys/denali/ahci/ahci_driver.h +++ b/sys/denali/ahci/ahci_driver.h @@ -1,69 +1,17 @@ #pragma once +#include #include -struct PciDeviceHeader { - uint16_t vendor_id; - uint16_t device_id; - uint16_t command_reg; - uint16_t status_reg; - uint8_t revision; - uint8_t prog_interface; - uint8_t subclass; - uint8_t class_code; - uint8_t cache_line_size; - uint8_t latency_timer; - uint8_t header_type; - uint8_t bist; - uint32_t bars[5]; - uint32_t abar; - uint32_t subsystem_id; - uint32_t expansion_rom; - uint8_t cap_ptr; - uint8_t reserved[7]; - uint8_t interrupt_line; - uint8_t interrupt_pin; - uint8_t min_grant; - uint8_t max_latency; -} __attribute__((packed)); - -struct AhciHba { - uint32_t capabilities; - uint32_t global_host_control; - uint32_t interrupt_status; - uint32_t port_implemented; - uint32_t version; - uint32_t ccc_ctl; // 0x14, Command completion coalescing control - uint32_t ccc_pts; // 0x18, Command completion coalescing ports - uint32_t em_loc; // 0x1C, Enclosure management location - uint32_t em_ctl; // 0x20, Enclosure management control - uint32_t capabilities_ext; - uint32_t bohc; // 0x28, BIOS/OS handoff control and status -}; - -struct AhciPort { - uint64_t command_list_base; - uint64_t fis_base; - uint32_t interrupt_status; - uint32_t interrupt_enable; - uint32_t command; - uint32_t reserved; - uint32_t task_file_data; - uint32_t signature; - uint32_t sata_status; - uint32_t sata_control; - uint32_t sata_error; - uint32_t sata_active; - uint32_t command_issue; - uint32_t sata_notification; - uint32_t fis_based_switching_ctl; - uint32_t device_sleep; -}; +#include "ahci/ahci.h" +#include "ahci/ahci_device.h" class AhciDriver { public: z_err_t Init(); + void InterruptLoop(); + void DumpCapabilities(); void DumpPorts(); @@ -71,6 +19,14 @@ class AhciDriver { PciDeviceHeader* pci_device_header_ = nullptr; AhciHba* ahci_hba_ = nullptr; + AhciDevice devices_[6]; + + Thread irq_thread_; + uint64_t irq_port_cap_ = 0; + z_err_t LoadPciDeviceHeader(); + z_err_t LoadCapabilities(); + z_err_t RegisterIrq(); z_err_t LoadHbaRegisters(); + z_err_t LoadDevices(); }; diff --git a/sys/denali/denali.cpp b/sys/denali/denali.cpp index 2247b5a..3a43fc2 100644 --- a/sys/denali/denali.cpp +++ b/sys/denali/denali.cpp @@ -1,8 +1,13 @@ +#include #include #include "ahci/ahci_driver.h" int main(uint64_t bootstrap_cap) { AhciDriver driver; - return driver.Init(); + RET_ERR(driver.Init()); + + while (1) { + }; + return 0; } diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index bb3f52e..f81685d 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(zion object/address_space.cpp object/channel.cpp object/memory_object.cpp + object/port.cpp object/process.cpp object/thread.cpp scheduler/context_switch.s diff --git a/zion/capability/capability.cpp b/zion/capability/capability.cpp index 67d3443..5e8c657 100644 --- a/zion/capability/capability.cpp +++ b/zion/capability/capability.cpp @@ -42,3 +42,11 @@ RefPtr Capability::obj() { } return StaticCastRefPtr(obj_); } + +template <> +RefPtr Capability::obj() { + if (type_ != PORT) { + panic("Accessing %u cap as object.", type_); + } + return StaticCastRefPtr(obj_); +} diff --git a/zion/capability/capability.h b/zion/capability/capability.h index 5bc4332..6616ec2 100644 --- a/zion/capability/capability.h +++ b/zion/capability/capability.h @@ -17,6 +17,7 @@ class Capability : public RefCounted { ADDRESS_SPACE, MEMORY_OBJECT, CHANNEL, + PORT, }; Capability(const RefPtr& obj, Type type, uint64_t id, uint64_t permissions) diff --git a/zion/include/zcall.h b/zion/include/zcall.h index 211f5fe..a690a24 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -43,6 +43,14 @@ #define Z_CHANNEL_RECV 0x42 #define Z_CHANNEL_SENDRECV 0x43 +#define Z_PORT_CREATE 0x50 +#define Z_PORT_SEND 0x51 +#define Z_PORT_RECV 0x52 + +#define Z_IRQ_REGISTER 0x58 + +#define Z_IRQ_PCI_BASE 0x30 + // Debugging Calls. #define Z_DEBUG_PRINT 0x10000000 @@ -70,6 +78,10 @@ void ZThreadExit(); [[nodiscard]] z_err_t ZMemoryObjectCreate(uint64_t size, uint64_t* vmmo_cap); [[nodiscard]] z_err_t ZMemoryObjectCreatePhysical(uint64_t paddr, uint64_t size, uint64_t* vmmo_cap); +[[nodiscard]] z_err_t ZMemoryObjectCreateContiguous(uint64_t size, + uint64_t* vmmo_cap, + uint64_t* paddr); + [[nodiscard]] z_err_t ZTempPcieConfigObjectCreate(uint64_t* vmmo_cap, uint64_t* vmmo_size); @@ -83,4 +95,10 @@ void ZThreadExit(); uint64_t* actual_bytes, uint64_t* actual_caps); +[[nodiscard]] z_err_t ZPortRecv(uint64_t port_cap, uint64_t num_bytes, + uint8_t* bytes, uint64_t num_caps, + uint64_t* caps, uint64_t* type, + uint64_t* actual_bytes, uint64_t* actual_caps); +[[nodiscard]] z_err_t ZIrqRegister(uint64_t irq_num, uint64_t* port_cap); + [[nodiscard]] z_err_t ZDebug(const char* message); diff --git a/zion/interrupt/apic.cpp b/zion/interrupt/apic.cpp index 9f6a248..ff546e4 100644 --- a/zion/interrupt/apic.cpp +++ b/zion/interrupt/apic.cpp @@ -7,7 +7,7 @@ #include "common/port.h" #include "debug/debug.h" -#define APIC_DEBUG 0 +#define APIC_DEBUG 1 namespace { @@ -17,6 +17,7 @@ namespace { const uint64_t kEoiOffset = 0xB0; +// FIXME: parse these from madt. constexpr uint64_t kLApicBase = 0xFEE0'0000; constexpr uint64_t kIoApicAddr = 0xFEC0'0000; constexpr uint64_t kIoApicData = 0xFEC0'0010; @@ -88,7 +89,6 @@ void InspectApic() { dbgln("TPR: %x", GetLocalReg(0x80)); dbgln("APR: %x", GetLocalReg(0x90)); dbgln("PPR: %x", GetLocalReg(0xA0)); - dbgln("RRD: %x", GetLocalReg(0xC0)); dbgln("LDR: %x", GetLocalReg(0xD0)); dbgln("DFR: %x", GetLocalReg(0xE0)); dbgln("SIV: %x", GetLocalReg(0xF0)); @@ -119,10 +119,22 @@ void EnableApic() { SetIoEntry(0x14, 0x20); // Skip Keyboard for now. // SetIoEntry(0x12, 0x21); + + // TODO: This also works with the interrupt numbers provided by the MADT + // I need to do further investigation on the difference in this case and + // also how to find a declarative spec for where the PCI Lines are mapped. + + // PCI Line 1-4 + // FIXME: These should be level triggered according to spec I believe + // but because we handle the interrupt outside of the kernel it is tricky + // to wait to send the end of interrupt message. + // Because of this leave them as edge triggered and send EOI immediately. + SetIoEntry(0x30, 0x30); + SetIoEntry(0x32, 0x31); + SetIoEntry(0x34, 0x32); + SetIoEntry(0x36, 0x33); + InspectApic(); } -void SignalEOI() { - // Value doesn't matter. - WriteLocalReg(kEoiOffset, 0x1); -} +void SignalEOI() { WriteLocalReg(kEoiOffset, 0x0); } diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index ab2d8c9..d6ff5c2 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -138,11 +138,44 @@ extern "C" void interrupt_timer(InterruptFrame*) { gScheduler->Preempt(); } +RefPtr pci1_port; +extern "C" void isr_pci1(); +extern "C" void interrupt_pci1(InterruptFrame*) { + dbgln("Interrupt PCI line 1"); + pci1_port->Write({}); + SignalEOI(); +} + +extern "C" void isr_pci2(); +extern "C" void interrupt_pci2(InterruptFrame*) { + dbgln("Interrupt PCI line 2"); + SignalEOI(); +} + +extern "C" void isr_pci3(); +extern "C" void interrupt_pci3(InterruptFrame*) { + dbgln("Interrupt PCI line 3"); + SignalEOI(); +} + +extern "C" void isr_pci4(); +extern "C" void interrupt_pci4(InterruptFrame*) { + dbgln("Interrupt PCI line 4"); + SignalEOI(); +} + void InitIdt() { gIdt[0] = CreateDescriptor(isr_divide_by_zero); gIdt[13] = CreateDescriptor(isr_protection_fault); gIdt[14] = CreateDescriptor(isr_page_fault); - gIdt[32] = CreateDescriptor(isr_timer); + + gIdt[0x20] = CreateDescriptor(isr_timer); + + gIdt[0x30] = CreateDescriptor(isr_pci1); + gIdt[0x31] = CreateDescriptor(isr_pci2); + gIdt[0x32] = CreateDescriptor(isr_pci3); + gIdt[0x33] = CreateDescriptor(isr_pci4); + InterruptDescriptorTablePointer idtp{ .size = sizeof(gIdt), .base = reinterpret_cast(gIdt), @@ -151,3 +184,5 @@ void InitIdt() { EnableApic(); } + +void RegisterPciPort(const RefPtr& port) { pci1_port = port; } diff --git a/zion/interrupt/interrupt.h b/zion/interrupt/interrupt.h index d6225ab..85bde5d 100644 --- a/zion/interrupt/interrupt.h +++ b/zion/interrupt/interrupt.h @@ -1,3 +1,8 @@ #pragma once +#include "lib/ref_ptr.h" +#include "object/port.h" + void InitIdt(); + +void RegisterPciPort(const RefPtr& port); diff --git a/zion/memory/physical_memory.cpp b/zion/memory/physical_memory.cpp index 2f8e3a6..c9de6fd 100644 --- a/zion/memory/physical_memory.cpp +++ b/zion/memory/physical_memory.cpp @@ -63,6 +63,34 @@ class PhysicalMemoryManager { } return page; } + uint64_t AllocateContinuous(uint64_t num_pages) { + if (front_ == nullptr) { + panic("No available memory regions."); + } + + if (front_->num_pages == 0) { + panic("Bad state, empty memory block."); + } + + MemBlock* block = front_; + while (block != nullptr && block->num_pages < num_pages) { + block = block->next; + } + + if (block == nullptr) { + panic("No memory regions to allocate"); + } + + uint64_t page = front_->base; + front_->base += num_pages * 0x1000; + front_->num_pages -= num_pages; + if (front_->num_pages == 0) { + MemBlock* temp = front_; + front_ = front_->next; + delete temp; + } + return page; + } void FreePage(uint64_t page) { AddMemoryRegion(page, 0x1000); } private: @@ -129,4 +157,12 @@ uint64_t AllocatePage() { return page; } +uint64_t AllocateContinuous(uint64_t num_continuous) { + if (gPmm == nullptr) { + panic("No physical memory manager!"); + } + + return gPmm->AllocateContinuous(num_continuous); +} + } // namespace phys_mem diff --git a/zion/memory/physical_memory.h b/zion/memory/physical_memory.h index 40bca04..d1cde93 100644 --- a/zion/memory/physical_memory.h +++ b/zion/memory/physical_memory.h @@ -13,6 +13,7 @@ void InitBootstrapPageAllocation(); void InitPhysicalMemoryManager(); uint64_t AllocatePage(); +uint64_t AllocateContinuous(uint64_t num_pages); void FreePage(uint64_t page); } // namespace phys_mem diff --git a/zion/object/memory_object.h b/zion/object/memory_object.h index 63de6bb..09e0cca 100644 --- a/zion/object/memory_object.h +++ b/zion/object/memory_object.h @@ -35,6 +35,7 @@ class MemoryObject : public KernelObject { class FixedMemoryObject : public MemoryObject { public: + // FIXME: Validate that this is 4k aligned. FixedMemoryObject(uint64_t physical_addr, uint64_t size) : MemoryObject(size, true), physical_addr_(physical_addr) {} diff --git a/zion/object/port.cpp b/zion/object/port.cpp new file mode 100644 index 0000000..a733c9d --- /dev/null +++ b/zion/object/port.cpp @@ -0,0 +1,50 @@ +#include "object/port.h" + +Port::Port() {} + +z_err_t Port::Write(const ZMessage& msg) { + if (msg.num_caps > 0) { + dbgln("Unimplemented passing caps on port"); + return Z_ERR_UNIMPLEMENTED; + } + + if (msg.num_bytes > 0x1000) { + dbgln("Large message size unimplemented: %x", msg.num_bytes); + return Z_ERR_INVALID; + } + + Message message{ + .type = msg.type, + .num_bytes = msg.num_bytes, + .bytes = new uint8_t[msg.num_bytes], + }; + for (uint64_t i = 0; i < msg.num_bytes; i++) { + message.bytes[i] = msg.bytes[i]; + } + pending_messages_.PushBack(message); + return Z_OK; +} + +z_err_t Port::Read(ZMessage& msg) { + if (pending_messages_.size() < 1) { + dbgln("Implement blocking"); + return Z_ERR_UNIMPLEMENTED; + } + + Message next_msg = pending_messages_.PeekFront(); + if (next_msg.num_bytes > msg.num_bytes) { + return Z_ERR_BUFF_SIZE; + } + + msg.type = next_msg.type; + msg.num_bytes = next_msg.num_bytes; + msg.num_caps = 0; + + for (uint64_t i = 0; i < msg.num_bytes; i++) { + msg.bytes[i] = next_msg.bytes[i]; + } + + pending_messages_.PopFront(); + + return Z_OK; +} diff --git a/zion/object/port.h b/zion/object/port.h new file mode 100644 index 0000000..ed0d9c7 --- /dev/null +++ b/zion/object/port.h @@ -0,0 +1,22 @@ +#pragma once + +#include "lib/linked_list.h" +#include "object/kernel_object.h" +#include "usr/zcall_internal.h" + +class Port : public KernelObject { + public: + Port(); + + z_err_t Write(const ZMessage& msg); + z_err_t Read(ZMessage& msg); + + private: + struct Message { + uint64_t type; + uint64_t num_bytes; + uint8_t* bytes; + }; + + LinkedList pending_messages_; +}; diff --git a/zion/object/process.cpp b/zion/object/process.cpp index 932a5f9..751f546 100644 --- a/zion/object/process.cpp +++ b/zion/object/process.cpp @@ -127,6 +127,13 @@ uint64_t Process::AddCapability(const RefPtr& chan) { return cap_id; } +uint64_t Process::AddCapability(const RefPtr& port) { + uint64_t cap_id = next_cap_id_++; + caps_.PushBack(MakeRefCounted(port, Capability::PORT, cap_id, + ZC_WRITE | ZC_READ)); + return cap_id; +} + void Process::AddCapability(uint64_t cap_id, const RefPtr& vmmo) { caps_.PushBack(MakeRefCounted(vmmo, Capability::MEMORY_OBJECT, cap_id, ZC_WRITE)); diff --git a/zion/object/process.h b/zion/object/process.h index 75e3988..301680b 100644 --- a/zion/object/process.h +++ b/zion/object/process.h @@ -7,6 +7,7 @@ #include "lib/ref_ptr.h" #include "object/address_space.h" #include "object/channel.h" +#include "object/port.h" // Forward decl due to cyclic dependency. class Thread; @@ -37,6 +38,7 @@ class Process : public KernelObject { uint64_t AddCapability(const RefPtr& vmas); uint64_t AddCapability(const RefPtr& vmmo); uint64_t AddCapability(const RefPtr& chan); + uint64_t AddCapability(const RefPtr& chan); void AddCapability(uint64_t cap_id, const RefPtr& vmmo); // Checks the state of all child threads and transitions to diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 39496f1..ee3cd5e 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -40,11 +40,13 @@ void Scheduler::Preempt() { if (current_thread_ == sleep_thread_) { // Sleep should never be preempted. (We should yield it if another thread // becomes scheduleable). + asm volatile("sti"); return; } if (runnable_threads_.size() == 0) { // Continue. + asm volatile("sti"); return; } diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index 0a15662..8d83d11 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -7,7 +7,10 @@ #include "debug/debug.h" #include "include/zcall.h" #include "include/zerrors.h" +#include "interrupt/interrupt.h" +#include "memory/physical_memory.h" #include "object/channel.h" +#include "object/port.h" #include "object/process.h" #include "scheduler/process_manager.h" #include "scheduler/scheduler.h" @@ -131,11 +134,16 @@ z_err_t MemoryObjectCreate(ZMemoryObjectCreateReq* req, } z_err_t MemoryObjectCreatePhysical(ZMemoryObjectCreatePhysicalReq* req, - ZMemoryObjectCreateResp* resp) { + ZMemoryObjectCreatePhysicalResp* resp) { auto& curr_proc = gScheduler->CurrentProcess(); - auto vmmo_ref = MakeRefCounted(req->paddr, req->size); + uint64_t paddr = req->paddr; + if (paddr == 0) { + paddr = phys_mem::AllocateContinuous((req->size - 1 / 0x1000) + 1); + } + auto vmmo_ref = MakeRefCounted(paddr, req->size); resp->vmmo_cap = curr_proc.AddCapability(StaticCastRefPtr(vmmo_ref)); + resp->paddr = paddr; return Z_OK; } @@ -174,7 +182,30 @@ z_err_t ChannelRecv(ZChannelRecvReq* req) { RET_ERR(ValidateCap(chan_cap, Capability::CHANNEL, ZC_READ)); auto chan = chan_cap->obj(); - chan->Read(req->message); + return chan->Read(req->message); +} + +z_err_t PortRecv(ZPortRecvReq* req) { + auto& proc = gScheduler->CurrentProcess(); + dbgln("Port cap %u", req->port_cap); + auto port_cap = proc.GetCapability(req->port_cap); + RET_ERR(ValidateCap(port_cap, Capability::PORT, ZC_READ)); + + auto port = port_cap->obj(); + return port->Read(req->message); +} + +z_err_t IrqRegister(ZIrqRegisterReq* req, ZIrqRegisterResp* resp) { + auto& proc = gScheduler->CurrentProcess(); + if (req->irq_num != Z_IRQ_PCI_BASE) { + // FIXME: Don't hardcode this nonsense. + dbgln("Irq %x", req->irq_num); + return Z_ERR_UNIMPLEMENTED; + } + RefPtr port = MakeRefCounted(); + resp->port_cap = proc.AddCapability(port); + dbgln("Port cap %u", resp->port_cap); + RegisterPciPort(port); return Z_OK; } @@ -210,7 +241,7 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req, void* resp) { case Z_MEMORY_OBJECT_CREATE_PHYSICAL: return MemoryObjectCreatePhysical( reinterpret_cast(req), - reinterpret_cast(resp)); + reinterpret_cast(resp)); case Z_TEMP_PCIE_CONFIG_OBJECT_CREATE: return TempPcieConfigObjectCreate( reinterpret_cast(resp)); @@ -220,6 +251,11 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req, void* resp) { return ChannelSend(reinterpret_cast(req)); case Z_CHANNEL_RECV: return ChannelRecv(reinterpret_cast(req)); + case Z_PORT_RECV: + return PortRecv(reinterpret_cast(req)); + case Z_IRQ_REGISTER: + return IrqRegister(reinterpret_cast(req), + reinterpret_cast(resp)); case Z_DEBUG_PRINT: dbgln("[Debug] %s", req); return Z_OK; diff --git a/zion/usr/zcall.cpp b/zion/usr/zcall.cpp index d048160..7ee1c5e 100644 --- a/zion/usr/zcall.cpp +++ b/zion/usr/zcall.cpp @@ -90,12 +90,25 @@ z_err_t ZMemoryObjectCreatePhysical(uint64_t paddr, uint64_t size, .paddr = paddr, .size = size, }; - ZMemoryObjectCreateResp resp; + ZMemoryObjectCreatePhysicalResp resp; z_err_t ret = SysCall2(Z_MEMORY_OBJECT_CREATE_PHYSICAL, &req, &resp); *vmmo_cap = resp.vmmo_cap; return ret; } +z_err_t ZMemoryObjectCreateContiguous(uint64_t size, uint64_t* vmmo_cap, + uint64_t* paddr) { + ZMemoryObjectCreatePhysicalReq req{ + .paddr = 0, + .size = size, + }; + ZMemoryObjectCreatePhysicalResp resp; + z_err_t ret = SysCall2(Z_MEMORY_OBJECT_CREATE_PHYSICAL, &req, &resp); + *vmmo_cap = resp.vmmo_cap; + *paddr = resp.paddr; + return ret; +} + z_err_t ZTempPcieConfigObjectCreate(uint64_t* vmmo_cap, uint64_t* vmmo_size) { ZTempPcieConfigObjectCreateResp resp; z_err_t ret = SysCall2(Z_TEMP_PCIE_CONFIG_OBJECT_CREATE, 0, &resp); @@ -150,4 +163,35 @@ z_err_t ZChannelRecv(uint64_t chan_cap, uint64_t num_bytes, uint8_t* bytes, return ret; } +z_err_t ZPortRecv(uint64_t port_cap, uint64_t num_bytes, uint8_t* bytes, + uint64_t num_caps, uint64_t* caps, uint64_t* type, + uint64_t* actual_bytes, uint64_t* actual_caps) { + ZPortRecvReq req{ + .port_cap = port_cap, + .message = + { + .type = 0, + .num_bytes = num_bytes, + .bytes = bytes, + .num_caps = num_caps, + .caps = caps, + }, + }; + z_err_t ret = SysCall1(Z_PORT_RECV, &req); + *type = req.message.type; + *actual_bytes = req.message.num_bytes; + *actual_caps = req.message.num_caps; + return ret; +} + +z_err_t ZIrqRegister(uint64_t irq_num, uint64_t* port_cap) { + ZIrqRegisterReq req{ + .irq_num = irq_num, + }; + ZIrqRegisterResp resp; + z_err_t ret = SysCall2(Z_IRQ_REGISTER, &req, &resp); + *port_cap = resp.port_cap; + return ret; +} + z_err_t ZDebug(const char* message) { return SysCall1(Z_DEBUG_PRINT, message); } diff --git a/zion/usr/zcall_internal.h b/zion/usr/zcall_internal.h index bfdda5c..f55a361 100644 --- a/zion/usr/zcall_internal.h +++ b/zion/usr/zcall_internal.h @@ -51,6 +51,11 @@ struct ZMemoryObjectCreatePhysicalReq { uint64_t size; }; +struct ZMemoryObjectCreatePhysicalResp { + uint64_t vmmo_cap; + uint64_t paddr; +}; + struct ZTempPcieConfigObjectCreateResp { uint64_t vmmo_cap; uint64_t vmmo_size; @@ -80,3 +85,16 @@ struct ZChannelRecvReq { uint64_t chan_cap; ZMessage message; }; + +struct ZPortRecvReq { + uint64_t port_cap; + ZMessage message; +}; + +struct ZIrqRegisterReq { + uint64_t irq_num; +}; + +struct ZIrqRegisterResp { + uint64_t port_cap; +};