karlos/include/pci.h

145 lines
4.3 KiB
C

#ifndef KARLOS_PCI_H
#define KARLOS_PCI_H
#include <stdint.h>
#include <stdbool.h>
/* Register offsets for common PCI header fields */
#define PCI_HEADER_VENDOR_ID 0x00
#define PCI_HEADER_DEVICE_ID 0x02
#define PCI_HEADER_COMMAND 0x04
#define PCI_HEADER_STATUS 0x06
#define PCI_HEADER_REVISION_ID 0x08
#define PCI_HEADER_PROG_IF 0x09
#define PCI_HEADER_SUBCLASS 0x10
#define PCI_HEADER_BASECLASS 0x11
#define PCI_HEADER_HEADER_TYPE 0x0E
#define PCI_HEADER_BIST 0x0F
#define PCI_HEADER_CAP_PTR 0x34
#define PCI_HEADER_BAR_START 0x10
#define PCI_VENDOR_ID_INVALID 0xFFFF
#define PCI_HEADER_TYPE_MULTI_FUNCTION_BIT 0x80
#define PCI_STATUS_CAPABILITIES_BIT 0x10
#define PCI_CAP_ID_MSI 0x05
// TODO if we are concerned about performance we can store the uint16_t in here.
struct bdf {
uint8_t bus;
uint8_t device;
uint8_t function;
};
#define BDF(bus, device, function) ((struct bdf){ bus, device, function })
uint16_t pci_bdf_pack(struct bdf bdf);
/* Low-level functions for working with the PCI configuration space */
uint32_t pci_config_read_u32(struct bdf bdf, uint8_t offset);
uint16_t pci_config_read_u16(struct bdf bdf, uint8_t offset);
uint8_t pci_config_read_u8(struct bdf bdf, uint8_t offset);
void pci_config_write_u32(struct bdf bdf, uint8_t offset, uint32_t value);
enum pci_bar_type {
PCI_BAR_TYPE_MEM = 0,
PCI_BAR_TYPE_IO = 1,
};
enum pci_bar_locatable {
PCI_BAR_LOCATABLE_32 = 0,
PCI_BAR_LOCATABLE_SMALL = 1,
PCI_BAR_LOCATABLE_64 = 2,
};
struct pci_bar {
enum pci_bar_type type;
// only for TYPE_MEM
enum pci_bar_locatable locatable;
bool prefetchable;
uint32_t base_address;
};
struct pci_bar pci_config_get_bar(struct bdf bdf, uint8_t bar_num);
/* Iterator over PCI devices (or more pedantically: PCI functions) */
struct pci_iter {
int next_bus;
int next_dev;
int next_fun;
struct bdf bdf;
uint16_t vendor;
uint8_t htype0; // header type of function 0
};
void pci_iter_reset(struct pci_iter *iter);
bool pci_iter_next(struct pci_iter *iter);
struct bdf pci_iter_get_bdf(const struct pci_iter *iter);
bool pci_matches_class(struct bdf bdf,
uint8_t base_class, uint8_t sub_class, uint8_t prog_if);
bool pci_matches_id(struct bdf bdf,
uint16_t vendor_id, uint16_t device_id, uint8_t revision_id);
/* Iterator over capability list of a single PCI header */
struct pci_cap_iter {
struct bdf bdf;
uint8_t cap_id;
uint8_t offset;
uint8_t next_offset;
};
void pci_cap_iter_begin(struct pci_cap_iter *iter, struct bdf bdf);
bool pci_cap_iter_next(struct pci_cap_iter *iter);
uint8_t pci_cap_iter_get_cap_id(const struct pci_cap_iter *iter);
uint8_t pci_cap_iter_get_offset(const struct pci_cap_iter *iter);
/**
* For a specific PCI device, find a capability header with the given ID.
* Returns 0 if no matching capability header exists.
*/
uint8_t pci_find_cap_by_id(struct bdf bdf, uint8_t cap_id);
void pci_print_listing();
/* Message Signalled Interrupts */
#define MSI_CONTROL_PER_VECTOR_MASKING 0x0100u
#define MSI_CONTROL_64_BIT 0x0080u
#define MSI_CONTROL_ENABLE 0x0001u
#define MSI_CONTROL_MULTIMSG_ENABLE_MASK 0x0070u
#define MSI_CONTROL_MULTIMSG_CAPABLE_MASK 0x000Eu
#define MSI_CONTROL_MULTIMSG_ENABLE_SHIFT 4
#define MSI_CONTROL_MULTIMSG_CAPABLE_SHIFT 1
/* These helper macros only work for x86-64 APIC interrupt controllers */
#define MSI_MAKE_ADDRESS(lapic_id) (0xFEE00000u | ((lapic_id) << 12))
#define MSI_MAKE_MESSAGE(vector) ((vector) & 0xFFu)
/**
* Read the control word of a MSI capability.
*
* bdf: PCI address of device
* cap_offset: Offset of MSI capability into the PCI config space of device
*/
uint16_t msi_cap_get_control(struct bdf bdf, uint8_t cap_offset);
/**
* Configure a PCI device to emit message signalled interrupts.
* Returns true on success.
*
* bdf: PCI address of device
* cap_offset: Offset of MSI capability into the PCI config space of device.
* An offset of 0 is allowed, but always results in failure.
* icount: Number of interrupts to reserve. Either power of two (up to 32), or 0 (MSI off)
* addr: MSI message address
* msg: MSI message data
*/
bool msi_cap_configure(struct bdf bdf, uint8_t cap_offset, unsigned icount, uint64_t addr, uint32_t msg);
#endif