145 lines
4.3 KiB
C
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
|