[Zion][Yellowstone] First pass at adding PCI ioport access.

This commit is contained in:
Drew Galbraith 2025-05-07 01:44:09 -07:00
parent f2c2cff98a
commit b677633248
16 changed files with 337 additions and 199 deletions

View file

@ -30,6 +30,7 @@ add_executable(zion
object/ipc_object.cpp
object/memory_object.cpp
object/mutex.cpp
object/pci_port.cpp
object/port.cpp
object/process.cpp
object/reply_port.cpp
@ -45,6 +46,7 @@ add_executable(zion
syscall/debug.cpp
syscall/ipc.cpp
syscall/memory_object.cpp
syscall/pci.cpp
syscall/process.cpp
syscall/synchronization.cpp
syscall/syscall.cpp

View file

@ -11,3 +11,13 @@ static inline uint8_t inb(uint16_t port) {
static inline void outb(uint16_t port, uint8_t value) {
asm volatile("outb %0, %1" ::"a"(value), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t result;
asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outl(uint16_t port, uint32_t value) {
asm volatile("outl %0, %1" ::"a"(value), "Nd"(port));
}

View file

@ -71,3 +71,14 @@ SYS1(SemaphoreWait, z_cap_t, semaphore_cap);
SYS1(SemaphoreSignal, z_cap_t, semaphore_cap);
SYS2(Debug, const char*, message, uint64_t, size);
// TODO: These should be handled with a more generic user-space interface.
// To be honest we could just have an inl and outl interface that is provided to
// yellowstone and the extra ipc load would be a good stress test of our
// performance
SYS6(PciRead, z_cap_t, pci_cap, uint8_t, bus, uint8_t, slot, uint8_t, func,
uint8_t, offset, uint32_t*, output);
SYS5(PciCreateBound, z_cap_t, pci_cap, uint8_t, bus, uint8_t, slot, uint8_t,
func, z_cap_t*, new_cap);
SYS3(PciReadBound, z_cap_t, pci_cap, uint8_t, offset, uint32_t*, data);
SYS3(PciWriteBound, z_cap_t, pci_cap, uint8_t, offset, uint32_t, data);

View file

@ -67,6 +67,11 @@ const uint64_t kZionSemaphoreSignal = 0x85;
// Debugging Calls.
const uint64_t kZionDebug = 0x1'0000;
const uint64_t kZionPciRead = 0x1'1000;
const uint64_t kZionPciCreateBound = 0x1'1001;
const uint64_t kZionPciReadBound = 0x1'1002;
const uint64_t kZionPciWriteBound = 0x1'1003;
// Irq Types
const uint64_t kZIrqKbd = 0x22;
const uint64_t kZIrqPci1 = 0x30;

View file

@ -8,6 +8,7 @@
#include "debug/debug.h"
#include "include/zcall.h"
#include "memory/paging_util.h"
#include "object/pci_port.h"
#include "object/process.h"
#include "object/thread.h"
#include "scheduler/process_manager.h"
@ -187,7 +188,9 @@ void LoadInitProgram() {
WriteFramebufferVmmo(port);
if (WritePciVmmo(port, Z_BOOT_PCI_VMMO) != glcr::OK) {
panic("Failed to provide PCI info to init.");
dbgln("Failed to find PCIe space, creating PCI IO Port Cap");
auto pci_port = PciPort::Create();
port->WriteKernel(Z_BOOT_PCI_VMMO, MakeRefCounted<Capability>(pci_port));
}
// Start process.

View file

@ -16,6 +16,10 @@ class KernelObject : public glcr::RefCounted<KernelObject> {
REPLY_PORT = 0x8,
MUTEX = 0x9,
SEMAPHORE = 0x10,
// Temporary.
PCI_CAP = 0x100,
PCI_BOUND_CAP = 0x101,
};
virtual uint64_t TypeTag() = 0;

46
zion/object/pci_port.cpp Normal file
View file

@ -0,0 +1,46 @@
#include "object/pci_port.h"
#include "common/port.h"
namespace {
const uint16_t PCI_ADDR_PORT = 0xCF8;
const uint16_t PCI_DATA_PORT = 0xCFC;
uint32_t AddressOf(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t lbus = (uint32_t)bus;
uint32_t lslot = (uint32_t)slot;
uint32_t lfunc = (uint32_t)func;
return (uint32_t)((lbus << 16) | (lslot << 11) | (lfunc << 8) |
(offset & 0xFC) | ((uint32_t)0x80000000));
}
uint32_t PciReadAtOffset(uint8_t bus, uint8_t slot, uint8_t func,
uint8_t offset) {
uint32_t address = AddressOf(bus, slot, func, offset);
outl(PCI_ADDR_PORT, address);
return inl(PCI_DATA_PORT);
}
void PciWriteAtOffset(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset,
uint32_t word) {
uint32_t address = AddressOf(bus, slot, func, offset);
outl(PCI_ADDR_PORT, address);
outl(PCI_DATA_PORT, word);
}
} // namespace
uint32_t PciPort::ReadAtOffset(uint8_t bus, uint8_t slot, uint8_t func,
uint8_t offset) {
return PciReadAtOffset(bus, slot, func, offset);
}
uint32_t PciPortBound::Read(uint8_t offset) {
return PciReadAtOffset(bus_, slot_, func_, offset);
}
void PciPortBound::Write(uint8_t offset, uint32_t data) {
PciWriteAtOffset(bus_, slot_, func_, offset, data);
}

60
zion/object/pci_port.h Normal file
View file

@ -0,0 +1,60 @@
#include <glacier/memory/ref_ptr.h>
#include "include/ztypes.h"
#include "object/kernel_object.h"
class PciPort;
class PciPortBound;
template <>
struct KernelObjectTag<PciPort> {
static const uint64_t type = KernelObject::PCI_CAP;
};
class PciPort : public KernelObject {
public:
static uint64_t DefaultPermissions() {
return kZionPerm_Write | kZionPerm_Read | kZionPerm_Duplicate |
kZionPerm_Transmit;
}
uint64_t TypeTag() override { return KernelObject::PCI_CAP; }
static glcr::RefPtr<PciPort> Create() { return glcr::AdoptPtr(new PciPort); }
uint32_t ReadAtOffset(uint8_t bus, uint8_t slot, uint8_t func,
uint8_t offset);
private:
PciPort() {}
};
template <>
struct KernelObjectTag<PciPortBound> {
static const uint64_t type = KernelObject::PCI_BOUND_CAP;
};
class PciPortBound : public KernelObject {
public:
static uint64_t DefaultPermissions() {
return kZionPerm_Write | kZionPerm_Read | kZionPerm_Duplicate;
}
uint64_t TypeTag() override { return KernelObject::PCI_BOUND_CAP; }
static glcr::RefPtr<PciPortBound> Create(uint8_t bus, uint8_t slot,
uint8_t func) {
return glcr::AdoptPtr(new PciPortBound(bus, slot, func));
}
uint32_t Read(uint8_t offset);
void Write(uint8_t offset, uint32_t data);
private:
PciPortBound(uint8_t bus, uint8_t slot, uint8_t func)
: bus_(bus), slot_(slot), func_(func) {}
uint8_t bus_;
uint8_t slot_;
uint8_t func_;
};

44
zion/syscall/pci.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "syscall/pci.h"
#include "object/pci_port.h"
#include "scheduler/scheduler.h"
z_err_t PciRead(ZPciReadReq* req) {
auto& curr_proc = gScheduler->CurrentProcess();
auto pci_cap = curr_proc.GetCapability(req->pci_cap);
RET_ERR(ValidateCapability<PciPort>(pci_cap, kZionPerm_Read));
*req->output = pci_cap->obj<PciPort>()->ReadAtOffset(req->bus, req->slot,
req->func, req->offset);
return glcr::OK;
}
z_err_t PciCreateBound(ZPciCreateBoundReq* req) {
auto& curr_proc = gScheduler->CurrentProcess();
auto pci_cap = curr_proc.GetCapability(req->pci_cap);
RET_ERR(ValidateCapability<PciPort>(pci_cap, kZionPerm_Duplicate));
*req->new_cap = curr_proc.AddNewCapability(
PciPortBound::Create(req->bus, req->slot, req->func));
return glcr::OK;
}
z_err_t PciReadBound(ZPciReadBoundReq* req) {
auto& curr_proc = gScheduler->CurrentProcess();
auto pci_cap = curr_proc.GetCapability(req->pci_cap);
RET_ERR(ValidateCapability<PciPortBound>(pci_cap, kZionPerm_Read));
*req->data = pci_cap->obj<PciPortBound>()->Read(req->offset);
return glcr::OK;
}
z_err_t PciWriteBound(ZPciWriteBoundReq* req) {
auto& curr_proc = gScheduler->CurrentProcess();
auto pci_cap = curr_proc.GetCapability(req->pci_cap);
RET_ERR(ValidateCapability<PciPortBound>(pci_cap, kZionPerm_Write));
pci_cap->obj<PciPortBound>()->Write(req->offset, req->data);
return glcr::OK;
}

6
zion/syscall/pci.h Normal file
View file

@ -0,0 +1,6 @@
#include "include/zcall.h"
z_err_t PciRead(ZPciReadReq* req);
z_err_t PciCreateBound(ZPciCreateBoundReq* req);
z_err_t PciReadBound(ZPciReadBoundReq* req);
z_err_t PciWriteBound(ZPciWriteBoundReq* req);