blob: 8c6be009864e4f11bb90ffee09d399fb1fae35df [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <threads.h>
#include <zircon/types.h>
#include <sys/types.h>
// clang-format off
#define PCI_DEVICE_ROOT_COMPLEX 0u
#define PCI_DEVICE_VIRTIO_BALLOON 1u
#define PCI_DEVICE_VIRTIO_BLOCK 2u
#define PCI_DEVICE_INVALID UINT16_MAX
#define PCI_MAX_DEVICES 3u
#define PCI_MAX_BARS 2u
// PCI configuration constants.
#define PCI_BAR_IO_TYPE_MASK 0x0001u
#define PCI_BAR_IO_TYPE_PIO 0x0001u
#define PCI_BAR_IO_TYPE_MMIO 0x0000u
#define PCI_VENDOR_ID_INTEL 0x8086u
#define PCI_DEVICE_ID_INTEL_Q35 0x29c0u
#define PCI_CLASS_BRIDGE_HOST 0x0600u
#define PCI_CLASS_MASS_STORAGE 0x0100u
// PCI type 1 address manipulation.
#define PCI_TYPE1_BUS(addr) (((addr) >> 16) & 0xff)
#define PCI_TYPE1_DEVICE(addr) (((addr) >> 11) & 0x1f)
#define PCI_TYPE1_FUNCTION(addr) (((addr) >> 8) & 0x7)
#define PCI_TYPE1_REGISTER_MASK 0xfc
#define PCI_TYPE1_REGISTER(addr) ((addr)&PCI_TYPE1_REGISTER_MASK)
// PCI ECAM address manipulation.
#define PCI_ECAM_BUS(addr) (((addr) >> 20) & 0xff)
#define PCI_ECAM_DEVICE(addr) (((addr) >> 15) & 0x1f)
#define PCI_ECAM_FUNCTION(addr) (((addr) >> 12) & 0x7)
#define PCI_ECAM_REGISTER(addr) ((addr)&0xfff)
// clang-format on
__BEGIN_CDECLS
typedef struct instruction instruction_t;
typedef struct io_apic io_apic_t;
typedef struct zx_packet_guest_io zx_packet_guest_io_t;
typedef struct zx_packet_guest_mem zx_packet_guest_mem_t;
typedef struct zx_vcpu_io zx_vcpu_io_t;
typedef struct pci_bus pci_bus_t;
typedef struct pci_device pci_device_t;
/* Device specific callbacks. */
typedef struct pci_device_ops {
// Read from a region mapped by a BAR register.
zx_status_t (*read_bar)(const pci_device_t* device, uint8_t bar, uint16_t port,
uint8_t access_size, zx_vcpu_io_t* vcpu_io);
// Write to a region mapped by a BAR register.
zx_status_t (*write_bar)(pci_device_t* device, uint8_t bar, uint16_t port,
const zx_vcpu_io_t* io);
} pci_device_ops_t;
/* PCI capability structure.
*
* The 1-byte next pointer will be computed dynamically while traversing the
* capabilities list.
*/
typedef struct pci_cap {
// PCI capability ID as defined in PCI LOCAL BUS SPECIFICATION, REV. 3.0
// Appendix H.
uint8_t id;
// Data for this capability. Must be at least |len| bytes. The first
// two bytes will be ignored (id and next) as these will be populated
// dynamically. They're skipped over in the data pointer to allow common
// structures to be used for read/write where the id/next pointers are
// embedded in the structure.
uint8_t* data;
// Size of |data|.
uint8_t len;
} pci_cap_t;
typedef struct pci_bar {
// Register value.
uint32_t addr;
// Size of this BAR.
uint32_t size;
// IO type for this bar (memory or IO ports).
uint32_t io_type;
} pci_bar_t;
/* Stores the state of PCI devices. */
typedef struct pci_device {
mtx_t mutex;
// Device attributes.
uint16_t device_id;
uint16_t vendor_id;
uint16_t subsystem_id;
uint16_t subsystem_vendor_id;
// Both class & subclass fields combined.
uint16_t class_code;
// Command register.
uint16_t command;
// Revision ID register.
uint8_t revision_id;
// Base address registers.
pci_bar_t bar[PCI_MAX_BARS];
// Array of capabilities for this device.
const pci_cap_t* capabilities;
// Size of |capabilities|.
size_t num_capabilities;
// Private pointer for use by the device implementation.
void* impl;
// Device specific operations.
const pci_device_ops_t* ops;
// PCI bus this device is connected to.
pci_bus_t* bus;
// IRQ vector assigned by the bus.
uint32_t global_irq;
} pci_device_t;
typedef struct pci_bus {
mtx_t mutex;
// Selected address in PCI config space.
uint32_t config_addr;
// Devices on the virtual PCI bus.
pci_device_t* device[PCI_MAX_DEVICES];
// IO APIC for use with interrupt redirects.
const io_apic_t* io_apic;
// Embedded root complex device.
pci_device_t root_complex;
// Next pio window to be allocated to connected devices.
uint32_t pio_base;
} pci_bus_t;
zx_status_t pci_bus_init(pci_bus_t* bus, const io_apic_t* io_apic);
/* Connect a PCI device to the bus.
*
* slot must be between 1 and PCI_MAX_DEVICES (slot 0 is reserved for
* the root complex).
*/
zx_status_t pci_bus_connect(pci_bus_t* bus, pci_device_t* device, uint8_t slot);
/* Handle reads from the PCI ECAM region. */
zx_status_t pci_ecam_read(pci_bus_t* bus, zx_vaddr_t addr, uint8_t access_size, zx_vcpu_io_t* io);
/* Handle writes to the PCI ECAM region. */
zx_status_t pci_ecam_write(pci_bus_t* bus, zx_vaddr_t addr, const zx_vcpu_io_t* io);
/* Handle PIO reads to the PCI config space. */
zx_status_t pci_bus_read(const pci_bus_t* bus, uint16_t port, uint8_t access_size,
zx_vcpu_io_t* vcpu_io);
/* Handle PIO writes to the PCI config space. */
zx_status_t pci_bus_write(pci_bus_t* bus, const zx_packet_guest_io_t* io);
zx_status_t pci_device_read(const pci_device_t* device, uint16_t reg, uint8_t len, uint32_t* value);
zx_status_t pci_device_write(pci_device_t* device, uint16_t reg, uint8_t len, uint32_t value);
/* Return the device that has a BAR mapped to the given address with the
* specified IO type. Returns NULL if no mapping exists or IO is disabled for
* the mapping.
*
* If a mapping is found, the bar number is written to |bar| and the offset
* into that bar is written to |off|.
*/
pci_device_t* pci_mapped_device(pci_bus_t* bus, uint8_t io_type, uint16_t addr, uint8_t* bar,
uint16_t* off);
/* Returns the base address for the BAR. */
uint32_t pci_bar_base(pci_bar_t* bar);
/* Returns the size of the BAR. */
uint16_t pci_bar_size(pci_bar_t* bar);
/* Start asynchronous handling of writes to the pci device. */
zx_status_t pci_device_async(pci_device_t* device, zx_handle_t guest);
/* Raise the configured interrupt for the given PCI device. */
zx_status_t pci_interrupt(pci_device_t* device);
__END_CDECLS