pci.h

back


/*
 BSD 3-Clause License
 
 Copyright (c) 2025, k4m1
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 
 1. Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
 
 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
 
 3. Neither the name of the copyright holder nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef __PCI_H__
#define __PCI_H__

#include <sys/io.h>
#include <drivers/device.h>

#include <stdint.h>


enum pci_class_code {
    pci_class_unclassified = 0,
    pci_class_mass_storage_controller,
    pci_class_network_controller,
    pci_class_display_controller,
    pci_class_multimedia_controller,
    pci_class_memory_controller,
    pci_class_bridge,
    pci_class_simple_communication_controller,
    pci_class_base_system_peripheral,
    pci_class_input_device_controller,
    pci_class_docking_station,
    pci_class_processor,
    pci_class_serial_bus_controller,
    pci_class_wireless_controller,
    pci_class_intelligent_controller,
    pci_class_satellite_communication_controller,
    pci_class_encryption_controller,
    pci_class_signal_processing_controller,
    pci_class_processing_accelerator,
    pci_class_non_essential,
    pci_class_co_processor = 0x40,
    pci_class_unassigned = 0xFF
};

enum unclassified_devices {
    non_vga_unclassified,
    vga_compatible_unclassified
};

enum mass_storage_controllers {
    pci_mass_storage_controller_scsi_bus_controller,
    pci_mass_storage_controller_ide_controller,
    pci_mass_storage_controller_floppy_controller,
    pci_mass_storage_controller_ipi_bus_controller,
    pci_mass_storage_controller_raid_controller,
    pci_mass_storage_controller_ata_controller,
    pci_mass_storage_controller_sata_controller,
    pci_mass_storage_controller_serial_scsi_controller,
    pci_mass_storage_controller_nv_memory_controller,
    pci_mass_storage_controller_other_storage = 0x80
};

enum pci_bridge_type {
    pci_host_bridge,
    pci_isa_bridge,
    pci_eisa_bridge,
    pci_mca_bridge,
    pci_pci2pci_bridge,
    pci_pcmcia_bridge,
    pci_nubus_bridge,
    pci_cardbus_bridge,
    pci_raceway_bridge,
    pci_pci2pci_bridge2,
    pci_infiband_to_pci_host,
    pci_other_bridge = 0x80
};

typedef struct {
    uint32_t bar0;
    uint32_t bar1;
    uint32_t bar2;
    uint32_t bar3;
    uint32_t bar4;
    uint32_t bar5;
    uint32_t cardbus_cis_ptr;
    uint16_t subsystem_vendor_id;
    uint16_t subsystem_id;
    uint32_t expansion_rom_bar;
    uint8_t  capabilities;
    uint16_t reserved0;
    uint8_t  reserved2;
    uint32_t reserved3;
    uint8_t  interrupt_line;
    uint8_t  interrupt_pin;
    uint8_t  min_grant;
    uint8_t  max_latency;
} pci_general_device_data;

typedef struct {
    uint32_t bar0;
    uint32_t bar1;
    uint8_t  primary_bus_number;
    uint8_t  secondary_bus_number;
    uint8_t  subordinate_bus_number;
    uint8_t  secondary_latency_timer;
    uint8_t  io_base;
    uint8_t  io_limit;
    uint16_t secondary_status;
    uint16_t memory_base;
    uint16_t memory_limit;
    uint16_t prefetch_memory_base;
    uint16_t prefetch_memory_limit;
    uint32_t prefetch_base_hi;
    uint32_t prefetch_limit_hi;
    uint16_t io_base_hi;
    uint16_t io_limit_hi;
    uint8_t  capabilities;
    uint16_t reserved0;
    uint8_t  reserved1;
    uint32_t expansion_rom_bar;
    uint8_t  interrupt_line;
    uint8_t  interrupt_pin;
    uint16_t bridge_control;
} pci2pci_bridge_data;

typedef struct {
    uint16_t device_id;
    uint16_t vendor_id;
    uint16_t status;
    uint16_t command;
    uint8_t  class_code;
    uint8_t  subclass;
    uint8_t  prog_if;
    uint8_t  revision_id;
    uint8_t  bist;
    uint8_t  header_type;
    uint8_t  latency_timer;
    uint8_t  cache_line_size;
    uint32_t cb_socket_bar;
    uint16_t secondary_status;
    uint8_t  reserved;
    uint8_t  offset_of_capabilities_list;
    uint8_t  cardbus_latency_timer;
    uint8_t  subordinate_bus_number;
    uint8_t  cardbus_bus_number;
    uint8_t  pci_bus_number;
    uint32_t memory_base_0;
    uint32_t memory_limit_0;
    uint32_t memory_base_1;
    uint32_t memory_limit_1;
    uint32_t io_base_0;
    uint32_t io_limit_0;
    uint32_t io_base_1;
    uint32_t io_limit_1;
    uint16_t bridge_control;
    uint8_t  interrupt_pin;
    uint8_t  interrupt_line;
    uint16_t subsystem_vendor_id;
    uint16_t subsystem_device_id;
    uint32_t legacy_card_bar;
} pci2cbus_bridge_data;

typedef struct {
    uint16_t vendor_id;
    uint16_t device_id;
    uint16_t command;
    uint16_t status;
    uint8_t  revision_id;
    uint8_t  programming_interface_byte;
    uint8_t  subclass;
    uint8_t  class_code;
    uint8_t  cache_line_size;
    uint8_t  latency_timer;
    uint8_t  header_type;
    uint8_t  built_in_self_test;
} pci_header;

enum pci_header_type {
    pci_std_hdr,
    pci2pci_bridge_hdr,
    pci2cb_bridge_hdr,
    pci_mf_hdr = 0x80
};

/* PCI BIST register definition
 *
 */
typedef union {
    uint8_t register_raw;
    struct __attribute__((packed)) {
        unsigned completion_code : 4;
        unsigned reserved : 2;
        unsigned start_bist : 1;
        unsigned bist_capable : 1;
    } fields;
} pci_bist_register;

/* PIO addresses for PCI configuration and data.
 */
static const uint16_t pci_config_port = 0x0CF8;
static const uint16_t pci_data_port = 0x0CFC;

/* PCI Configuration address
 *
 * @member enable   -- Set to one to enable
 * @member reserved -- Set to 0
 * @member bus      -- Which PCI bus we're working with
 * @member device   -- Which PCI device in that bus we're working with
 * @member function -- Which device function do we want
 * @member offset   -- Which dword out of the 256-byte configuration space
 *                     are we interested in
 */
typedef struct __attribute__((packed)) {
    unsigned offset     : 8;
    unsigned function   : 3;
    unsigned device     : 5;
    unsigned bus        : 8;
    unsigned reserved   : 7;
    unsigned enable     : 1;
} pci_config_address;


/* Structure for holding information about our pci subsystem
 *
 */
typedef struct {
    pci_config_address address;
    bool bist_executed;
    pci_header generic_header_fields;
    union {
        pci_general_device_data device_hdr;
        pci2pci_bridge_data pci2pci_bridge_hdr;
        pci2cbus_bridge_data pci2cbus_bridge_hdr;
    };
} pci_device_data;

typedef struct __attribute__((packed)) {
    bool mode         : 1;
    unsigned type     : 2;
    bool prefetchable : 1;
    unsigned addr     : 28;
} pci_memory_bar_fields;

typedef struct __attribute__((packed)) {
    bool mode     : 1;
    bool reserved : 1;
    unsigned addr : 30;
} pci_io_bar_fields;

typedef union {
    uint32_t raw_bar;
    pci_memory_bar_fields memory_bar;
    pci_io_bar_fields io_bar;
} pci_bar;

/* Fetch headers for all pci devices we have plugged in
 *
 * @param device *pci_device_array -- Pointer to pci device data
 * @return uint8_t amount of devices found or -1 on error
 */
uint8_t enumerate_pci_buses(device **pci_device_array);

/* Print pci device tree information
 *
 * @param device **pci_device_array -- Device array after enumerate_pci_buses() is done
 * @param uint8_t device_count -- Amount of devices we have
 */
void pci_print_devtree(device **pci_device_array, uint8_t device_count);

static const char *pci_unknown_str = "other";

static const char *pci_class_code_str[] = {
    "unclassified",
    "mass storage",
    "network",
    "display",
    "multimedia",
    "memory",
    "bridge",
    "simple comms",
    "base system peripheral",
    "input device",
    "docking station",
    "processor",
    "serial bus",
    "wireless",
    "intelligent",
    "satcom",
    "encryption",
    "signal processing",
    "processing accelerator",
    "non essential",
};

static const char *pci_subclass_str[] = {

    // Mass storage
    "SCSI",
    "IDE",
    "Floppy",
    "IPI",
    "RAID",
    "ATA",
    "Serial ATA",
    "Serial SCSI",
    "NV memory",

    // Net - 9
    "ethernet",
    "token ring",
    "FDDI",
    "ATM",
    "ISDN",
    "WorldFip",
    "PICMG",
    "infiniband",
    "fabric",

    // Display - 18
    "VGA",
    "XGA",
    "3D",
    
    // Memory - 21
    "RAM",
    "flash",

    // Simple comms - 24
    "serial",
    "parallel",
    "multiport serial",
    "modem",
    "iee 488",
    "smart card",

    // base system - 31
    "PIC",
    "DMA",
    "timer",
    "RTC",
    "PCI",
    "SD",
    "IOMMU",

    // input - 37
    "keyboard",
    "digitizer pen",
    "mouse",
    "scanner",
    "gameport",

    // Serial bus - 41
    "firewire",
    "access bus",
    "SSA",
    "USB",
    "Fibre channel",
    "SMbus",
    "InfiniBand",
    "IPMI",
    "SERCOS",
    "CANbus",

    // Wireless - 51
    "iRDA",
    "IR",
    "RF",
    "bluetooth",
    "broadband",
    "802.1a",
    "802.1b",

    // bridge - 58
    "Host",
    "ISA",
    "EISA",
    "MCA",
    "PCI",
    "PCMCIA",
    "NuBus",
    "CardBus",
    "RACEway",
    "PCI",
    "InfiniBand to PCI",
};


#endif // __PCI_H__