Enable AHCI interrupts.
* Implement MSI style interrupts * Move interrupt handling to device_manager for IRQs * Give device_manager the ability to allocate IRQs * Move achi::port to an interrupt-based scheme
This commit is contained in:
@@ -14,6 +14,15 @@ ahci_driver::register_device(pci_device *device)
|
|||||||
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
|
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
|
||||||
device->bus(), device->device(), device->function());
|
device->bus(), device->device(), device->function());
|
||||||
|
|
||||||
m_devices.emplace(device);
|
ahci::hba &hba = m_devices.emplace(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ahci::port *
|
||||||
|
ahci_driver::find_disk()
|
||||||
|
{
|
||||||
|
for (auto &hba : m_devices) {
|
||||||
|
ahci::port *d = hba.find_disk();
|
||||||
|
if (d) return d;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public:
|
|||||||
/// \arg device The PCI device to remove
|
/// \arg device The PCI device to remove
|
||||||
void unregister_device(pci_device *device);
|
void unregister_device(pci_device *device);
|
||||||
|
|
||||||
|
/// Debug: find the first disk
|
||||||
|
ahci::port * find_disk();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kutil::vector<ahci::hba> m_devices;
|
kutil::vector<ahci::hba> m_devices;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "ahci/ata.h"
|
||||||
#include "ahci/hba.h"
|
#include "ahci/hba.h"
|
||||||
#include "console.h"
|
#include "device_manager.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
@@ -46,6 +47,13 @@ struct hba_data
|
|||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
|
void irq_cb(void *data)
|
||||||
|
{
|
||||||
|
hba *h = reinterpret_cast<hba *>(data);
|
||||||
|
h->handle_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
hba::hba(pci_device *device)
|
hba::hba(pci_device *device)
|
||||||
{
|
{
|
||||||
page_manager *pm = page_manager::get();
|
page_manager *pm = page_manager::get();
|
||||||
@@ -54,6 +62,9 @@ hba::hba(pci_device *device)
|
|||||||
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
|
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
|
||||||
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
|
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
|
||||||
|
|
||||||
|
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
|
||||||
|
m_data->host_control |= 0x80000000; // Enable AHCI mode
|
||||||
|
|
||||||
uint32_t icap = static_cast<uint32_t>(m_data->cap);
|
uint32_t icap = static_cast<uint32_t>(m_data->cap);
|
||||||
|
|
||||||
unsigned ports = (icap & 0xf) + 1;
|
unsigned ports = (icap & 0xf) + 1;
|
||||||
@@ -65,24 +76,42 @@ hba::hba(pci_device *device)
|
|||||||
port_data *pd = reinterpret_cast<port_data *>(
|
port_data *pd = reinterpret_cast<port_data *>(
|
||||||
kutil::offset_pointer(m_data, 0x100));
|
kutil::offset_pointer(m_data, 0x100));
|
||||||
|
|
||||||
|
bool needs_interrupt = false;
|
||||||
m_ports.ensure_capacity(ports);
|
m_ports.ensure_capacity(ports);
|
||||||
for (unsigned i = 0; i < ports; ++i) {
|
for (unsigned i = 0; i < ports; ++i) {
|
||||||
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
||||||
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||||
if (p.get_state() == port::state::active) {
|
if (p.get_state() == port::state::active)
|
||||||
uint8_t buf[512];
|
needs_interrupt = true;
|
||||||
p.read(1, sizeof(buf), buf);
|
}
|
||||||
|
|
||||||
console *cons = console::get();
|
if (needs_interrupt) {
|
||||||
uint8_t *p = &buf[0];
|
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
|
||||||
for (int i = 0; i < 8; ++i) {
|
m_data->host_control |= 0x02; // enable interrupts
|
||||||
for (int j = 0; j < 16; ++j) {
|
|
||||||
cons->printf(" %02x", *p++);
|
|
||||||
}
|
|
||||||
cons->putc('\n');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
port *
|
||||||
|
hba::find_disk()
|
||||||
|
{
|
||||||
|
for (auto &port : m_ports) {
|
||||||
|
if (port.get_state() == port::state::active &&
|
||||||
|
port.get_type() == sata_signature::sata_drive)
|
||||||
|
return &port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hba::handle_interrupt()
|
||||||
|
{
|
||||||
|
for (auto &port : m_ports) {
|
||||||
|
if (m_data->int_status & (1 << port.index())) {
|
||||||
|
port.handle_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_data->int_status = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ahci
|
} // namespace ahci
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ public:
|
|||||||
/// \arg device The PCI device for this HBA
|
/// \arg device The PCI device for this HBA
|
||||||
hba(pci_device *device);
|
hba(pci_device *device);
|
||||||
|
|
||||||
|
/// Interrupt handler.
|
||||||
|
void handle_interrupt();
|
||||||
|
|
||||||
|
/// Debug: find the first disk
|
||||||
|
port * find_disk();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pci_device *m_device;
|
pci_device *m_device;
|
||||||
hba_data *m_data;
|
hba_data *m_data;
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ port::update()
|
|||||||
|
|
||||||
rebase();
|
rebase();
|
||||||
m_pending.set_size(32);
|
m_pending.set_size(32);
|
||||||
|
m_data->interrupt_enable = 1;
|
||||||
} else {
|
} else {
|
||||||
m_state = state::inactive;
|
m_state = state::inactive;
|
||||||
}
|
}
|
||||||
@@ -290,25 +291,20 @@ port::issue_command(int slot)
|
|||||||
// Set bit in CI. Note that only new bits should be written, not
|
// Set bit in CI. Note that only new bits should be written, not
|
||||||
// previous state.
|
// previous state.
|
||||||
m_data->cmd_issue = (1 << slot);
|
m_data->cmd_issue = (1 << slot);
|
||||||
|
return true;
|
||||||
// TODO: interrupt-based
|
|
||||||
while (true) {
|
|
||||||
if ((m_data->cmd_issue & (1 << slot)) == 0) break;
|
|
||||||
if (m_data->interrupt_status & 0x40000000) {
|
|
||||||
log::error(logs::driver, "AHCI task file error");
|
|
||||||
// TODO: clean up!
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
io_wait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where interrupt handler would begin
|
void
|
||||||
|
port::handle_interrupt()
|
||||||
|
{
|
||||||
|
log::debug(logs::driver, "AHCI port %d got an interrupt");
|
||||||
|
|
||||||
// TODO: handle other states in interrupt_status
|
// TODO: handle other states in interrupt_status
|
||||||
|
|
||||||
if (m_data->interrupt_status & 0x40000000) {
|
if (m_data->interrupt_status & 0x40000000) {
|
||||||
log::error(logs::driver, "AHCI task file error");
|
log::error(logs::driver, "AHCI task file error");
|
||||||
// TODO: clean up!
|
// TODO: clean up!
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
|
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
|
||||||
@@ -329,8 +325,7 @@ port::issue_command(int slot)
|
|||||||
p.type = command_type::none;
|
p.type = command_type::none;
|
||||||
p.data = nullptr;
|
p.data = nullptr;
|
||||||
}
|
}
|
||||||
|
m_data->interrupt_status = m_data->interrupt_status;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -27,15 +27,19 @@ public:
|
|||||||
/// Destructor
|
/// Destructor
|
||||||
~port();
|
~port();
|
||||||
|
|
||||||
|
/// Get the index of this port on the HBA
|
||||||
|
/// \returns The port index
|
||||||
|
inline uint8_t index() const { return m_index; }
|
||||||
|
|
||||||
enum class state : uint8_t { unimpl, inactive, active };
|
enum class state : uint8_t { unimpl, inactive, active };
|
||||||
|
|
||||||
/// Get the current state of this device
|
/// Get the current state of this device
|
||||||
/// \returns An enum representing the state
|
/// \returns An enum representing the state
|
||||||
state get_state() const { return m_state; }
|
inline state get_state() const { return m_state; }
|
||||||
|
|
||||||
/// Get the type signature of this device
|
/// Get the type signature of this device
|
||||||
/// \returns An enum representing the type of device
|
/// \returns An enum representing the type of device
|
||||||
sata_signature get_type() const { return m_type; }
|
inline sata_signature get_type() const { return m_type; }
|
||||||
|
|
||||||
/// Update the state of this object from the register data
|
/// Update the state of this object from the register data
|
||||||
void update();
|
void update();
|
||||||
@@ -56,6 +60,9 @@ public:
|
|||||||
/// \returns True if the command succeeded
|
/// \returns True if the command succeeded
|
||||||
bool read(uint64_t sector, size_t length, void *dest);
|
bool read(uint64_t sector, size_t length, void *dest);
|
||||||
|
|
||||||
|
/// Handle an incoming interrupt
|
||||||
|
void handle_interrupt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Rebase the port command structures to a new location in system
|
/// Rebase the port command structures to a new location in system
|
||||||
/// memory, to be allocated from the page manager.
|
/// memory, to be allocated from the page manager.
|
||||||
|
|||||||
@@ -6,14 +6,17 @@
|
|||||||
#include "acpi_tables.h"
|
#include "acpi_tables.h"
|
||||||
#include "ahci/driver.h"
|
#include "ahci/driver.h"
|
||||||
#include "apic.h"
|
#include "apic.h"
|
||||||
|
#include "console.h"
|
||||||
#include "device_manager.h"
|
#include "device_manager.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
|
|
||||||
|
|
||||||
static const char expected_signature[] = "RSD PTR ";
|
static const char expected_signature[] = "RSD PTR ";
|
||||||
|
|
||||||
|
device_manager device_manager::s_instance(nullptr);
|
||||||
ahci_driver ahcid;
|
ahci_driver ahcid;
|
||||||
|
|
||||||
struct acpi1_rsdp
|
struct acpi1_rsdp
|
||||||
@@ -56,6 +59,21 @@ acpi_table_header::validate(uint32_t expected_type) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void irq2_callback(void *)
|
||||||
|
{
|
||||||
|
console *cons = console::get();
|
||||||
|
cons->set_color(11);
|
||||||
|
cons->puts(".");
|
||||||
|
cons->set_color();
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq4_callback(void *)
|
||||||
|
{
|
||||||
|
// TODO: move this to a real serial driver
|
||||||
|
console *cons = console::get();
|
||||||
|
cons->echo();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
device_manager::device_manager(const void *root_table) :
|
device_manager::device_manager(const void *root_table) :
|
||||||
m_lapic(nullptr)
|
m_lapic(nullptr)
|
||||||
@@ -81,6 +99,11 @@ device_manager::device_manager(const void *root_table) :
|
|||||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||||
|
|
||||||
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
|
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
|
||||||
|
|
||||||
|
m_irqs.ensure_capacity(32);
|
||||||
|
m_irqs.set_size(16);
|
||||||
|
m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr};
|
||||||
|
m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
ioapic *
|
ioapic *
|
||||||
@@ -281,3 +304,19 @@ device_manager::init_drivers()
|
|||||||
ahcid.register_device(&device);
|
ahcid.register_device(&device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||||
|
{
|
||||||
|
// TODO: find gaps to fill
|
||||||
|
uint8_t irq = m_irqs.count();
|
||||||
|
isr vector = isr::irq00 + irq;
|
||||||
|
m_irqs.append({name, cb, data});
|
||||||
|
|
||||||
|
log::debug(logs::device, "Allocating IRQ %02x to %s.", irq, name);
|
||||||
|
|
||||||
|
device.write_msi_regs(
|
||||||
|
0xFEE00000,
|
||||||
|
static_cast<uint16_t>(vector));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ struct acpi_mcfg;
|
|||||||
class lapic;
|
class lapic;
|
||||||
class ioapic;
|
class ioapic;
|
||||||
|
|
||||||
|
using irq_callback = void (*)(void *);
|
||||||
|
|
||||||
|
|
||||||
/// Manager for all system hardware devices
|
/// Manager for all system hardware devices
|
||||||
class device_manager
|
class device_manager
|
||||||
@@ -19,6 +21,10 @@ public:
|
|||||||
/// \arg root_table Pointer to the ACPI RSDP
|
/// \arg root_table Pointer to the ACPI RSDP
|
||||||
device_manager(const void *root_table);
|
device_manager(const void *root_table);
|
||||||
|
|
||||||
|
/// Get the system global device manager.
|
||||||
|
/// \returns A reference to the system device manager
|
||||||
|
static device_manager & get() { return s_instance; }
|
||||||
|
|
||||||
/// Get the LAPIC
|
/// Get the LAPIC
|
||||||
/// \returns An object representing the local APIC
|
/// \returns An object representing the local APIC
|
||||||
lapic * get_lapic() { return m_lapic; }
|
lapic * get_lapic() { return m_lapic; }
|
||||||
@@ -32,6 +38,33 @@ public:
|
|||||||
/// Intialize drivers for the current device list.
|
/// Intialize drivers for the current device list.
|
||||||
void init_drivers();
|
void init_drivers();
|
||||||
|
|
||||||
|
/// Allocate an MSI IRQ for a device
|
||||||
|
/// \arg name Name of the interrupt, for display to user
|
||||||
|
/// \arg device Device this MSI is being allocated for
|
||||||
|
/// \arg cb Callback to call when the interrupt is received
|
||||||
|
/// \arg data Data to pass to the callback
|
||||||
|
/// \returns True if an interrupt was allocated successfully
|
||||||
|
bool allocate_msi(
|
||||||
|
const char *name,
|
||||||
|
pci_device &device,
|
||||||
|
irq_callback cb,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
/// Dispatch an IRQ interrupt
|
||||||
|
/// \arg irq The irq number of the interrupt
|
||||||
|
/// \returns True if the interrupt was handled
|
||||||
|
inline bool dispatch_irq(uint8_t irq)
|
||||||
|
{
|
||||||
|
if (irq < m_irqs.count()) {
|
||||||
|
irq_allocation &cba = m_irqs[irq];
|
||||||
|
if (cba.callback) {
|
||||||
|
cba.callback(cba.data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||||
@@ -49,12 +82,27 @@ private:
|
|||||||
/// device list. The device list is destroyed and rebuilt.
|
/// device list. The device list is destroyed and rebuilt.
|
||||||
void probe_pci();
|
void probe_pci();
|
||||||
|
|
||||||
|
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
||||||
|
/// that has no callback.
|
||||||
|
void bad_irq(uint8_t irq);
|
||||||
|
|
||||||
lapic *m_lapic;
|
lapic *m_lapic;
|
||||||
kutil::vector<ioapic *> m_ioapics;
|
kutil::vector<ioapic *> m_ioapics;
|
||||||
|
|
||||||
kutil::vector<pci_group> m_pci;
|
kutil::vector<pci_group> m_pci;
|
||||||
kutil::vector<pci_device> m_devices;
|
kutil::vector<pci_device> m_devices;
|
||||||
|
|
||||||
|
struct irq_allocation
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
irq_callback callback;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
kutil::vector<irq_allocation> m_irqs;
|
||||||
|
|
||||||
|
static device_manager s_instance;
|
||||||
|
|
||||||
device_manager() = delete;
|
device_manager() = delete;
|
||||||
device_manager(const device_manager &) = delete;
|
device_manager(const device_manager &) = delete;
|
||||||
|
device_manager operator=(const device_manager &) = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
#include "kutil/enum_bitfields.h"
|
#include "kutil/enum_bitfields.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
#include "device_manager.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
|
||||||
|
|
||||||
enum class gdt_flags : uint8_t
|
enum class gdt_flags : uint8_t
|
||||||
{
|
{
|
||||||
@@ -319,22 +319,9 @@ irq_handler(registers regs)
|
|||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
uint8_t irq = get_irq(regs.interrupt);
|
uint8_t irq = get_irq(regs.interrupt);
|
||||||
|
if (! device_manager::get().dispatch_irq(irq)) {
|
||||||
switch (irq) {
|
|
||||||
case 2:
|
|
||||||
cons->set_color(11);
|
cons->set_color(11);
|
||||||
cons->puts(".");
|
cons->printf("\nReceived unknown IRQ: %d (vec %d)\n",
|
||||||
cons->set_color();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
// TODO: move this to a real serial driver
|
|
||||||
cons->echo();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
cons->set_color(11);
|
|
||||||
cons->printf("\nReceived IRQ interrupt: %d (vec %d)\n",
|
|
||||||
irq, regs.interrupt);
|
irq, regs.interrupt);
|
||||||
cons->set_color();
|
cons->set_color();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
|
|
||||||
|
#include "ahci/driver.h"
|
||||||
|
#include "ahci/port.h"
|
||||||
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "device_manager.h"
|
#include "device_manager.h"
|
||||||
@@ -23,6 +27,8 @@ extern "C" {
|
|||||||
void *__bss_start, *__bss_end;
|
void *__bss_start, *__bss_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern ahci_driver ahcid;
|
||||||
|
|
||||||
extern void __kernel_assert(const char *, unsigned, const char *);
|
extern void __kernel_assert(const char *, unsigned, const char *);
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -79,7 +85,10 @@ kernel_main(popcorn_data *header)
|
|||||||
// pager->dump_blocks();
|
// pager->dump_blocks();
|
||||||
|
|
||||||
interrupts_init();
|
interrupts_init();
|
||||||
device_manager devices(header->acpi_table);
|
|
||||||
|
device_manager *devices =
|
||||||
|
new (&device_manager::get()) device_manager(header->acpi_table);
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
|
|
||||||
cpu_id cpu;
|
cpu_id cpu;
|
||||||
@@ -87,7 +96,25 @@ kernel_main(popcorn_data *header)
|
|||||||
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
|
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
|
||||||
cpu.family(), cpu.model(), cpu.stepping());
|
cpu.family(), cpu.model(), cpu.stepping());
|
||||||
|
|
||||||
devices.init_drivers();
|
devices->init_drivers();
|
||||||
|
|
||||||
|
ahci::port *disk = ahcid.find_disk();
|
||||||
|
if (disk) {
|
||||||
|
uint8_t buf[512];
|
||||||
|
kutil::memset(buf, 0, 512);
|
||||||
|
|
||||||
|
disk->read(1, sizeof(buf), buf);
|
||||||
|
while (buf[0] == 0) io_wait();
|
||||||
|
|
||||||
|
console *cons = console::get();
|
||||||
|
uint8_t *p = &buf[0];
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
for (int j = 0; j < 16; ++j) {
|
||||||
|
cons->printf(" %02x", *p++);
|
||||||
|
}
|
||||||
|
cons->putc('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// do_error_1();
|
// do_error_1();
|
||||||
// __asm__ __volatile__("int $15");
|
// __asm__ __volatile__("int $15");
|
||||||
|
|||||||
@@ -3,6 +3,28 @@
|
|||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
|
struct pci_cap_msi :
|
||||||
|
public pci_cap
|
||||||
|
{
|
||||||
|
uint16_t control;
|
||||||
|
uint64_t address;
|
||||||
|
uint16_t data;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t pending;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct pci_cap_msix :
|
||||||
|
public pci_cap
|
||||||
|
{
|
||||||
|
uint16_t control;
|
||||||
|
uint64_t address;
|
||||||
|
uint16_t data;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t pending;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
pci_device::pci_device() :
|
pci_device::pci_device() :
|
||||||
m_base(nullptr),
|
m_base(nullptr),
|
||||||
@@ -20,6 +42,7 @@ pci_device::pci_device() :
|
|||||||
|
|
||||||
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
||||||
m_base(group.base_for(bus, device, func)),
|
m_base(group.base_for(bus, device, func)),
|
||||||
|
m_msi(nullptr),
|
||||||
m_bus_addr(bus_addr(bus, device, func)),
|
m_bus_addr(bus_addr(bus, device, func)),
|
||||||
m_irq(isr::isrIgnoreF)
|
m_irq(isr::isrIgnoreF)
|
||||||
{
|
{
|
||||||
@@ -34,8 +57,24 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
|
|||||||
m_header_type = (m_base[3] >> 16) & 0x7f;
|
m_header_type = (m_base[3] >> 16) & 0x7f;
|
||||||
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
||||||
|
|
||||||
|
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||||
|
*command |= 0x400; // Mask old INTx style interrupts
|
||||||
|
|
||||||
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %d.%d id %04x:%04x",
|
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %d.%d id %04x:%04x",
|
||||||
bus, device, func, m_class, m_subclass, m_vendor, m_device);
|
bus, device, func, m_class, m_subclass, m_vendor, m_device);
|
||||||
|
|
||||||
|
// Walk the extended capabilities list
|
||||||
|
uint8_t next = m_base[13] & 0xff;
|
||||||
|
while (next) {
|
||||||
|
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
|
||||||
|
next = cap->next;
|
||||||
|
|
||||||
|
if (cap->id == pci_cap::type::msi) {
|
||||||
|
m_msi = cap;
|
||||||
|
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||||
|
mcap->control |= ~0x1; // Mask interrupts
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
@@ -66,6 +105,19 @@ pci_device::set_bar(unsigned i, uint32_t val)
|
|||||||
m_base[4+i] = val;
|
m_base[4+i] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pci_device::write_msi_regs(addr_t address, uint16_t data)
|
||||||
|
{
|
||||||
|
kassert(m_msi, "Tried to write MSI for a device without that cap");
|
||||||
|
if (m_msi->id == pci_cap::type::msi) {
|
||||||
|
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
||||||
|
mcap->address = address;
|
||||||
|
mcap->data = data;
|
||||||
|
mcap->control |= 1;
|
||||||
|
} else {
|
||||||
|
kassert(0, "MIS-X is NYI");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
||||||
|
|||||||
@@ -7,6 +7,18 @@
|
|||||||
struct pci_group;
|
struct pci_group;
|
||||||
enum class isr : uint8_t;
|
enum class isr : uint8_t;
|
||||||
|
|
||||||
|
struct pci_cap
|
||||||
|
{
|
||||||
|
enum class type : uint8_t
|
||||||
|
{
|
||||||
|
msi = 0x05,
|
||||||
|
msix = 0x11
|
||||||
|
};
|
||||||
|
|
||||||
|
type id;
|
||||||
|
uint8_t next;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
/// Information about a discovered PCIe device
|
/// Information about a discovered PCIe device
|
||||||
class pci_device
|
class pci_device
|
||||||
@@ -60,6 +72,11 @@ public:
|
|||||||
/// \arg val The value to write
|
/// \arg val The value to write
|
||||||
void set_bar(unsigned i, uint32_t val);
|
void set_bar(unsigned i, uint32_t val);
|
||||||
|
|
||||||
|
/// Write to the MSI registers
|
||||||
|
/// \arg addr The address to write to the MSI address registers
|
||||||
|
/// \arg data The value to write to the MSI data register
|
||||||
|
void write_msi_regs(addr_t addr, uint16_t data);
|
||||||
|
|
||||||
/// Get a bus address, given the bus/device/function numbers.
|
/// Get a bus address, given the bus/device/function numbers.
|
||||||
/// \arg bus Number of the bus
|
/// \arg bus Number of the bus
|
||||||
/// \arg device Index of the device on the bus
|
/// \arg device Index of the device on the bus
|
||||||
@@ -72,6 +89,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t *m_base;
|
uint32_t *m_base;
|
||||||
|
pci_cap *m_msi;
|
||||||
|
|
||||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||||
uint16_t m_bus_addr;
|
uint16_t m_bus_addr;
|
||||||
|
|||||||
Reference in New Issue
Block a user