In preparation for the new mailbox IPC model, blocking threads needed an overhaul. The `wait_on_*` and `wake_on_*` methods are gone, and the `block()` and `wake()` calls on threads now pass a value between the waker and the blocked thread. As part of this change, the concept of signals on the base kobject class was removed, along with the queue of blocked threads waiting on any given object. Signals are now exclusively the domain of the event object type, and the new wait_queue utility class helps manage waiting threads when an object does actually need this functionality. In some cases (eg, logger) an event object is used instead of the lower-level wait_queue. Since this change has a lot of ramifications, this large commit includes the following additional changes: - The j6_object_wait, j6_object_wait_many, and j6_thread_pause syscalls have been removed. - The j6_event_clear syscall has been removed - events are "cleared" by reading them now. A new j6_event_wait syscall has been added to read events. - The generic close() method on kobject has been removed. - The on_no_handles() method on kobject now deletes the object by default, and needs to be overridden by classes that should not be. - The j6_system_bind_irq syscall now takes an event handle, as well as a signal that the IRQ should set on the event. IRQs will cause a waiting thread to be woken with the appropriate bit set. - Threads waking due to timeout is simplified to just having a wake_timeout() accessor that returns a timestamp. - The new wait_queue uses util::deque, which caused the disovery of two bugs in the deque implementation: empty deques could still have a single array allocated and thus return true for empty(), and new arrays getting allocated were not being zeroed first. - Exposed a new erase() method on util::map that takes a node pointer instead of a key, skipping lookup.
182 lines
5.5 KiB
C++
182 lines
5.5 KiB
C++
#pragma once
|
|
/// \file device_manager.h
|
|
/// The device manager definition
|
|
|
|
#include <util/vector.h>
|
|
|
|
#include "apic.h"
|
|
#include "hpet.h"
|
|
#include "pci.h"
|
|
|
|
struct acpi_table_header;
|
|
class block_device;
|
|
|
|
namespace obj {
|
|
class event;
|
|
}
|
|
|
|
using irq_callback = void (*)(void *);
|
|
|
|
|
|
/// Manager for all system hardware devices
|
|
class device_manager
|
|
{
|
|
public:
|
|
/// Constructor.
|
|
device_manager();
|
|
|
|
/// Get the system global device manager.
|
|
/// \returns A reference to the system device manager
|
|
static device_manager & get() { return s_instance; }
|
|
|
|
/// Get an IOAPIC
|
|
/// \arg i Index of the requested IOAPIC
|
|
/// \returns An object representing the given IOAPIC if it exists,
|
|
/// otherwise nullptr.
|
|
ioapic * get_ioapic(int i);
|
|
|
|
/// Parse ACPI tables.
|
|
/// \arg root_table Pointer to the ACPI RSDP
|
|
void parse_acpi(const void *root_table);
|
|
|
|
/// Intialize drivers for the current device list.
|
|
void init_drivers();
|
|
|
|
/// Bind an IRQ to an endpoint
|
|
/// \arg irq The IRQ number to bind
|
|
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
|
/// \arg signal The event's signal number to bind to
|
|
/// \returns True on success
|
|
bool bind_irq(unsigned irq, obj::event *target, unsigned signal);
|
|
|
|
/// Remove IRQ bindings for an endpoint
|
|
/// \arg target The endpoint to remove
|
|
void unbind_irqs(obj::event *target);
|
|
|
|
/// 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
|
|
bool dispatch_irq(unsigned irq);
|
|
|
|
struct apic_nmi
|
|
{
|
|
uint8_t cpu;
|
|
uint8_t lint;
|
|
uint16_t flags;
|
|
};
|
|
|
|
struct irq_override
|
|
{
|
|
uint8_t source;
|
|
uint16_t flags;
|
|
uint32_t gsi;
|
|
};
|
|
|
|
/// Get the list of APIC ids for other CPUs
|
|
inline const util::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
|
|
|
|
/// Get the LAPIC base address
|
|
/// \returns The physical base address of the local apic registers
|
|
uintptr_t get_lapic_base() const { return m_lapic_base; }
|
|
|
|
/// Get the NMI mapping for the given local APIC
|
|
/// \arg id ID of the local APIC
|
|
/// \returns apic_nmi structure describing the NMI configuration,
|
|
/// or null if no configuration was provided
|
|
const apic_nmi * get_lapic_nmi(uint8_t id) const;
|
|
|
|
/// Get the IRQ source override for the given IRQ
|
|
/// \arg irq IRQ number (not isr vector)
|
|
/// \returns irq_override structure describing that IRQ's
|
|
/// configuration, or null if no configuration was provided
|
|
const irq_override * get_irq_override(uint8_t irq) const;
|
|
|
|
/// Register the existance of a block device.
|
|
/// \arg blockdev Pointer to the block device
|
|
void register_block_device(block_device *blockdev);
|
|
|
|
/// Get the number of block devices in the system
|
|
/// \returns A count of devices
|
|
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
|
|
|
|
/// Get a block device
|
|
/// \arg i Index of the device to get
|
|
/// \returns A pointer to the requested device, or nullptr
|
|
inline block_device * get_block_device(unsigned i)
|
|
{
|
|
return i < m_blockdevs.count() ?
|
|
m_blockdevs[i] : nullptr;
|
|
}
|
|
|
|
/// Get an HPET device
|
|
/// \arg i Index of the device to get
|
|
/// \returns A pointer to the requested device, or nullptr
|
|
inline hpet * get_hpet(unsigned i)
|
|
{
|
|
return i < m_hpets.count() ?
|
|
&m_hpets[i] : nullptr;
|
|
}
|
|
|
|
private:
|
|
/// Parse the ACPI XSDT and load relevant sub-tables.
|
|
/// \arg xsdt Pointer to the XSDT from the firmware
|
|
void load_xsdt(const acpi_table_header *xsdt);
|
|
|
|
/// Parse the ACPI MADT and initialize APICs from it.
|
|
/// \arg apic Pointer to the MADT from the XSDT
|
|
void load_apic(const acpi_table_header *apic);
|
|
|
|
/// Parse the ACPI MCFG and initialize PCIe from it.
|
|
/// \arg mcfg Pointer to the MCFG from the XSDT
|
|
void load_mcfg(const acpi_table_header *mcfg);
|
|
|
|
/// Parse the ACPI HPET and initialize an HPET from it.
|
|
/// \arg hpet Pointer to the HPET from the XSDT
|
|
void load_hpet(const acpi_table_header *hpet);
|
|
|
|
/// Probe the PCIe busses and add found devices to our
|
|
/// device list. The device list is destroyed and rebuilt.
|
|
void probe_pci();
|
|
|
|
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
|
/// that has no callback.
|
|
void bad_irq(uint8_t irq);
|
|
|
|
uintptr_t m_lapic_base;
|
|
|
|
util::vector<ioapic> m_ioapics;
|
|
util::vector<hpet> m_hpets;
|
|
util::vector<uint8_t> m_apic_ids;
|
|
util::vector<apic_nmi> m_nmis;
|
|
util::vector<irq_override> m_overrides;
|
|
|
|
util::vector<pci_group> m_pci;
|
|
util::vector<pci_device> m_devices;
|
|
|
|
struct irq_binding
|
|
{
|
|
obj::event *target = nullptr;
|
|
unsigned signal = 0;
|
|
};
|
|
util::vector<irq_binding> m_irqs;
|
|
|
|
util::vector<block_device *> m_blockdevs;
|
|
|
|
static device_manager s_instance;
|
|
|
|
device_manager(const device_manager &) = delete;
|
|
device_manager operator=(const device_manager &) = delete;
|
|
};
|