blob: b570e3b677965f1557fe478f1484c1c16af25bdc [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.
#include <hypervisor/pci.h>
#include <stdio.h>
#include <string.h>
#include <hw/pci.h>
#include <hypervisor/address.h>
#include <hypervisor/bits.h>
#include <hypervisor/decode.h>
#include <hypervisor/io_apic.h>
#include <hypervisor/vcpu.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/hypervisor.h>
#include <virtio/virtio_ids.h>
// PCI BAR register addresses.
#define PCI_REGISTER_BAR_0 0x10
#define PCI_REGISTER_BAR_1 0x14
#define PCI_REGISTER_BAR_2 0x18
#define PCI_REGISTER_BAR_3 0x1c
#define PCI_REGISTER_BAR_4 0x20
#define PCI_REGISTER_BAR_5 0x24
// PCI Capabilities registers.
#define PCI_REGISTER_CAP_BASE 0xa4
#define PCI_REGISTER_CAP_TOP UINT8_MAX
static const uint32_t kPioBase = 0x8000;
static const uint32_t kMaxBarSize = 1 << 8;
static const uint32_t kPioAddressMask = ~bit_mask<uint32_t>(2);
static const uint32_t kMmioAddressMask = ~bit_mask<uint32_t>(4);
// PCI capabilities register layout.
static const uint8_t kPciCapTypeOffset = 0;
static const uint8_t kPciCapNextOffset = 1;
/* Per-device IRQ assignments.
*
* These are provided to the guest via the _SB section in the DSDT ACPI table.
*/
static const uint32_t kPciGlobalIrqAssigments[PCI_MAX_DEVICES] = {32, 33, 34};
static zx_status_t pci_bar_read_unsupported(const pci_device_t* device, uint8_t bar, uint16_t port,
uint8_t access_size, zx_vcpu_io_t* vcpu_io) {
return ZX_ERR_NOT_SUPPORTED;
}
static zx_status_t pci_bar_write_unsupported(pci_device_t* device, uint8_t bar, uint16_t port,
const zx_vcpu_io_t* io) {
return ZX_ERR_NOT_SUPPORTED;
}
static inline bool pci_bar_implemented(pci_device_t* device, uint8_t bar) {
return bar < PCI_MAX_BARS && device->bar[bar].size > 0;
}
static pci_device_ops_t kRootComplexDeviceOps = {
.read_bar = &pci_bar_read_unsupported,
.write_bar = &pci_bar_write_unsupported,
};
static void pci_root_complex_init(pci_device_t* host_bridge) {
host_bridge->vendor_id = PCI_VENDOR_ID_INTEL;
host_bridge->device_id = PCI_DEVICE_ID_INTEL_Q35;
host_bridge->subsystem_vendor_id = 0;
host_bridge->subsystem_id = 0;
host_bridge->class_code = PCI_CLASS_BRIDGE_HOST;
host_bridge->bar[0].size = 0x10;
host_bridge->ops = &kRootComplexDeviceOps;
}
zx_status_t pci_bus_init(pci_bus_t* bus, const io_apic_t* io_apic) {
memset(bus, 0, sizeof(*bus));
bus->io_apic = io_apic;
bus->pio_base = kPioBase;
pci_root_complex_init(&bus->root_complex);
return pci_bus_connect(bus, &bus->root_complex, PCI_DEVICE_ROOT_COMPLEX);
}
zx_status_t pci_bus_connect(pci_bus_t* bus, pci_device_t* device, uint8_t slot) {
if (slot >= PCI_MAX_DEVICES)
return ZX_ERR_OUT_OF_RANGE;
if (bus->device[slot])
return ZX_ERR_ALREADY_EXISTS;
// Initialize BAR registers.
for (uint8_t bar_num = 0; bar_num < PCI_MAX_BARS; ++bar_num) {
// Skip unimplemented bars.
if (!pci_bar_implemented(device, bar_num))
continue;
// PCI LOCAL BUS SPECIFICATION, REV. 3.0 Section 6.2.5.1
//
// This design implies that all address spaces used are a power of two in
// size and are naturally aligned.
uint32_t bar_size = round_up_pow2(device->bar[bar_num].size);
if (bar_size > kMaxBarSize)
return ZX_ERR_NOT_SUPPORTED;
device->bus = bus;
device->bar[bar_num].size = static_cast<uint16_t>(bar_size);
device->bar[bar_num].addr = bus->pio_base;
device->bar[bar_num].io_type = PCI_BAR_IO_TYPE_PIO;
bus->pio_base += kMaxBarSize;
}
device->command = PCI_COMMAND_IO_EN;
device->global_irq = kPciGlobalIrqAssigments[slot];
bus->device[slot] = device;
return ZX_OK;
}
static bool pci_addr_valid(const pci_bus_t* b, uint8_t bus, uint8_t device, uint8_t function) {
return bus == 0 && device < PCI_MAX_DEVICES && function == 0 && b->device[device];
}
static void pci_addr_invalid_read(uint8_t len, uint32_t* value) {
// PCI LOCAL BUS SPECIFICATION, REV. 3.0 Section 6.1
//
// The host bus to PCI bridge must unambiguously report attempts to read the
// Vendor ID of non-existent devices. Since 0 FFFFh is an invalid Vendor ID,
// it is adequate for the host bus to PCI bridge to return a value of all
// 1's on read accesses to Configuration Space registers of non-existent
// devices.
*value = bit_mask<uint32_t>(len * 8);
}
zx_status_t pci_ecam_read(pci_bus_t* bus, zx_vaddr_t addr, uint8_t access_size, zx_vcpu_io_t* io) {
const uint8_t device = PCI_ECAM_DEVICE(addr);
const uint16_t reg = PCI_ECAM_REGISTER(addr);
const bool valid = pci_addr_valid(bus, PCI_ECAM_BUS(addr), device, PCI_ECAM_FUNCTION(addr));
if (!valid) {
pci_addr_invalid_read(access_size, &io->u32);
return ZX_OK;
}
return pci_device_read(bus->device[device], reg, access_size, &io->u32);
}
zx_status_t pci_ecam_write(pci_bus_t* bus, zx_vaddr_t addr, const zx_vcpu_io_t* io) {
const uint8_t device = PCI_ECAM_DEVICE(addr);
const uint16_t reg = PCI_ECAM_REGISTER(addr);
const bool valid = pci_addr_valid(bus, PCI_ECAM_BUS(addr), device, PCI_ECAM_FUNCTION(addr));
if (!valid) {
return ZX_ERR_OUT_OF_RANGE;
}
return pci_device_write(bus->device[device], reg, io->access_size, io->u32);
}
zx_status_t pci_bus_read(const pci_bus_t* bus, uint16_t port, uint8_t access_size,
zx_vcpu_io_t* vcpu_io) {
switch (port) {
case PCI_CONFIG_ADDRESS_PORT_BASE ... PCI_CONFIG_ADDRESS_PORT_TOP: {
uint32_t bit_offset = (port - PCI_CONFIG_ADDRESS_PORT_BASE) * 8;
uint32_t mask = bit_mask<uint32_t>(access_size * 8);
mtx_lock((mtx_t*)&bus->mutex);
uint32_t addr = bus->config_addr >> bit_offset;
mtx_unlock((mtx_t*)&bus->mutex);
vcpu_io->access_size = access_size;
vcpu_io->u32 = (vcpu_io->u32 & ~mask) | (addr & mask);
return ZX_OK;
}
case PCI_CONFIG_DATA_PORT_BASE ... PCI_CONFIG_DATA_PORT_TOP: {
mtx_lock((mtx_t*)&bus->mutex);
uint32_t addr = bus->config_addr;
mtx_unlock((mtx_t*)&bus->mutex);
vcpu_io->access_size = access_size;
if (!pci_addr_valid(bus, PCI_TYPE1_BUS(addr), PCI_TYPE1_DEVICE(addr),
PCI_TYPE1_FUNCTION(addr))) {
pci_addr_invalid_read(access_size, &vcpu_io->u32);
return ZX_OK;
}
const pci_device_t* device = bus->device[PCI_TYPE1_DEVICE(addr)];
uint32_t reg = PCI_TYPE1_REGISTER(addr) + port - PCI_CONFIG_DATA_PORT_BASE;
return pci_device_read(device, static_cast<uint16_t>(reg), access_size, &vcpu_io->u32);
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
zx_status_t pci_bus_write(pci_bus_t* bus, const zx_packet_guest_io_t* io) {
switch (io->port) {
case PCI_CONFIG_ADDRESS_PORT_BASE ... PCI_CONFIG_ADDRESS_PORT_TOP: {
// Software can (and Linux does) perform partial word accesses to the
// PCI address register. This means we need to take care to read/write
// portions of the 32bit register without trampling the other bits.
uint32_t bit_offset = (io->port - PCI_CONFIG_ADDRESS_PORT_BASE) * 8;
uint32_t bit_size = io->access_size * 8;
uint32_t mask = bit_mask<uint32_t>(bit_size);
mtx_lock(&bus->mutex);
// Clear out the bits we'll be modifying.
bus->config_addr = clear_bits(bus->config_addr, bit_size, bit_offset);
// Set the bits of the address.
bus->config_addr |= (io->u32 & mask) << bit_offset;
mtx_unlock(&bus->mutex);
return ZX_OK;
}
case PCI_CONFIG_DATA_PORT_BASE ... PCI_CONFIG_DATA_PORT_TOP: {
mtx_lock(&bus->mutex);
uint32_t addr = bus->config_addr;
mtx_unlock(&bus->mutex);
if (!pci_addr_valid(bus, PCI_TYPE1_BUS(addr), PCI_TYPE1_DEVICE(addr), PCI_TYPE1_FUNCTION(addr)))
return ZX_ERR_OUT_OF_RANGE;
pci_device_t* device = bus->device[PCI_TYPE1_DEVICE(addr)];
uint32_t reg = PCI_TYPE1_REGISTER(addr) + io->port - PCI_CONFIG_DATA_PORT_BASE;
return pci_device_write(device, static_cast<uint16_t>(reg), io->access_size, io->u32);
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
// PCI Local Bus Spec v3.0 Section 6.7: Each capability must be DWORD aligned.
static inline uint8_t pci_cap_len(const pci_cap_t* cap) {
return align(cap->len, 4);
}
static const pci_cap_t* pci_find_cap(const pci_device_t* device, uint8_t addr, uint8_t* cap_index,
uint32_t* cap_base) {
uint32_t base = PCI_REGISTER_CAP_BASE;
for (uint8_t i = 0; i < device->num_capabilities; ++i) {
const pci_cap_t* cap = &device->capabilities[i];
uint8_t cap_len = pci_cap_len(cap);
if (addr >= base + cap_len) {
base += cap_len;
continue;
}
*cap_index = i;
*cap_base = base;
return cap;
}
// Given address doesn't lie within the range of addresses occupied by
// capabilities.
return nullptr;
}
static zx_status_t pci_read_cap(const pci_device_t* device, uint8_t addr, uint32_t* out) {
uint8_t cap_index;
uint32_t cap_base;
const pci_cap_t* cap = pci_find_cap(device, addr, &cap_index, &cap_base);
if (cap == nullptr)
return ZX_ERR_NOT_FOUND;
uint32_t word = 0;
uint32_t cap_offset = addr - cap_base;
for (uint8_t byte = 0; byte < sizeof(word); ++byte, ++cap_offset) {
// In the case of padding bytes, return 0.
if (cap_offset >= cap->len)
break;
// PCI Local Bus Spec v3.0 Section 6.7:
// Each capability in the list consists of an 8-bit ID field assigned
// by the PCI SIG, an 8 bit pointer in configuration space to the next
// capability, and some number of additional registers immediately
// following the pointer to implement that capability.
uint32_t val = 0;
switch (cap_offset) {
case kPciCapTypeOffset:
val = cap->id;
break;
case kPciCapNextOffset:
// PCI Local Bus Spec v3.0 Section 6.7: A pointer value of 00h is
// used to indicate the last capability in the list.
if (cap_index + 1u < device->num_capabilities)
val = cap_base + pci_cap_len(cap);
break;
default:
val = cap->data[cap_offset];
break;
}
word |= val << (byte * 8);
}
*out = word;
return ZX_OK;
}
// PCI LOCAL BUS SPECIFICATION, REV. 3.0 Section 6.1
//
// Read accesses to reserved or unimplemented registers must be completed
// normally and a data value of 0 returned.
static zx_status_t pci_device_read_unimplemented(uint32_t* value) {
*value = 0;
return ZX_OK;
}
/* Read a 4 byte aligned value from PCI config space. */
static zx_status_t pci_device_read_word(const pci_device_t* device, uint8_t reg, uint32_t* value) {
switch (reg) {
// ---------------------------------
// | (31..16) | (15..0) |
// | device_id | vendor_id |
// ---------------------------------
case PCI_CONFIG_VENDOR_ID:
*value = device->vendor_id;
*value |= device->device_id << 16;
return ZX_OK;
// ----------------------------
// | (31..16) | (15..0) |
// | status | command |
// ----------------------------
case PCI_CONFIG_COMMAND: {
mtx_lock((mtx_t*)&device->mutex);
*value = device->command;
mtx_unlock((mtx_t*)&device->mutex);
uint16_t status = PCI_STATUS_INTERRUPT;
if (device->capabilities != nullptr)
status |= PCI_STATUS_NEW_CAPS;
*value |= status << 16;
return ZX_OK;
}
// -------------------------------------------------
// | (31..16) | (15..8) | (7..0) |
// | class_code | prog_if | revision_id |
// -------------------------------------------------
case PCI_CONFIG_REVISION_ID:
*value = device->class_code << 16 | device->revision_id;
return ZX_OK;
// ---------------------------------------------------------------
// | (31..24) | (23..16) | (15..8) | (7..0) |
// | BIST | header_type | latency_timer | cache_line_size |
// ---------------------------------------------------------------
case PCI_CONFIG_CACHE_LINE_SIZE:
*value = PCI_HEADER_TYPE_STANDARD << 16;
return ZX_OK;
case PCI_REGISTER_BAR_0:
case PCI_REGISTER_BAR_1:
case PCI_REGISTER_BAR_2:
case PCI_REGISTER_BAR_3:
case PCI_REGISTER_BAR_4:
case PCI_REGISTER_BAR_5: {
uint32_t bar_num = (reg - PCI_REGISTER_BAR_0) / 4;
if (bar_num >= PCI_MAX_BARS)
return pci_device_read_unimplemented(value);
mtx_lock((mtx_t*)&device->mutex);
const pci_bar_t* bar = &device->bar[bar_num];
*value = bar->addr | bar->io_type;
mtx_unlock((mtx_t*)&device->mutex);
return ZX_OK;
}
// -------------------------------------------------------------
// | (31..24) | (23..16) | (15..8) | (7..0) |
// | max_latency | min_grant | interrupt_pin | interrupt_line |
// -------------------------------------------------------------
case PCI_CONFIG_INTERRUPT_LINE: {
const uint8_t interrupt_pin = 1;
*value = interrupt_pin << 8;
return ZX_OK;
}
// -------------------------------------------
// | (31..16) | (15..0) |
// | subsystem_id | subsystem_vendor_id |
// -------------------------------------------
case PCI_CONFIG_SUBSYS_VENDOR_ID:
*value = device->subsystem_vendor_id;
*value |= device->subsystem_id << 16;
return ZX_OK;
// ------------------------------------------
// | (31..8) | (7..0) |
// | Reserved | capabilities_pointer |
// ------------------------------------------
case PCI_CONFIG_CAPABILITIES:
*value = 0;
if (device->capabilities != nullptr)
*value |= PCI_REGISTER_CAP_BASE;
return ZX_OK;
case PCI_REGISTER_CAP_BASE... PCI_REGISTER_CAP_TOP:
if (pci_read_cap(device, reg, value) != ZX_ERR_NOT_FOUND)
return ZX_OK;
// Fall-through if the capability is not-implemented.
// These are all 32-bit registers.
case PCI_CONFIG_CARDBUS_CIS_PTR:
case PCI_CONFIG_EXP_ROM_ADDRESS:
return pci_device_read_unimplemented(value);
}
fprintf(stderr, "Unhandled PCI device read %#x\n", reg);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t pci_device_read(const pci_device_t* device, uint16_t reg, uint8_t len, uint32_t* value) {
// Perform 4-byte aligned read and then shift + mask the result to get the
// expected value.
uint32_t word = 0;
const uint8_t reg_mask = bit_mask<uint8_t>(2);
uint8_t word_aligend_reg = static_cast<uint8_t>(reg & ~reg_mask);
uint8_t bit_offset = static_cast<uint8_t>((reg & reg_mask) * 8);
zx_status_t status = pci_device_read_word(device, word_aligend_reg, &word);
if (status != ZX_OK)
return status;
word >>= bit_offset;
word &= bit_mask<uint32_t>(len * 8);
*value = word;
return ZX_OK;
}
// PCI LOCAL BUS SPECIFICATION, REV. 3.0 Section 6.1
//
// All PCI devices must treat Configuration Space write operations to reserved
// registers as no-ops; that is, the access must be completed normally on the
// bus and the data discarded.
static inline zx_status_t pci_device_write_unimplemented() {
return ZX_OK;
}
zx_status_t pci_device_write(pci_device_t* device, uint16_t reg, uint8_t len, uint32_t value) {
switch (reg) {
case PCI_CONFIG_VENDOR_ID:
case PCI_CONFIG_DEVICE_ID:
case PCI_CONFIG_REVISION_ID:
case PCI_CONFIG_HEADER_TYPE:
case PCI_CONFIG_CLASS_CODE:
case PCI_CONFIG_CLASS_CODE_SUB:
case PCI_CONFIG_CLASS_CODE_BASE:
// Read-only registers.
return ZX_ERR_NOT_SUPPORTED;
case PCI_CONFIG_COMMAND:
if (len != 2)
return ZX_ERR_NOT_SUPPORTED;
mtx_lock(&device->mutex);
device->command = static_cast<uint16_t>(value);
mtx_unlock(&device->mutex);
return ZX_OK;
case PCI_REGISTER_BAR_0:
case PCI_REGISTER_BAR_1:
case PCI_REGISTER_BAR_2:
case PCI_REGISTER_BAR_3:
case PCI_REGISTER_BAR_4:
case PCI_REGISTER_BAR_5: {
if (len != 4)
return ZX_ERR_NOT_SUPPORTED;
uint32_t bar_num = (reg - PCI_REGISTER_BAR_0) / 4;
if (bar_num >= PCI_MAX_BARS)
return pci_device_write_unimplemented();
mtx_lock(&device->mutex);
pci_bar_t* bar = &device->bar[bar_num];
bar->addr = value;
// We zero bits in the BAR in order to set the size.
bar->addr &= ~(bar->size - 1);
mtx_unlock(&device->mutex);
return ZX_OK;
}
default:
return pci_device_write_unimplemented();
}
}
static bool pci_device_io_enabled(uint8_t io_type, uint16_t command) {
switch (io_type) {
case PCI_BAR_IO_TYPE_PIO:
return command & PCI_COMMAND_IO_EN;
case PCI_BAR_IO_TYPE_MMIO:
return command & PCI_COMMAND_MEM_EN;
default:
return false;
}
}
pci_device_t* pci_mapped_device(pci_bus_t* bus, uint8_t io_type, uint16_t addr, uint8_t* bar_out,
uint16_t* off) {
for (uint8_t i = 0; i < PCI_MAX_DEVICES; i++) {
pci_device_t* device = bus->device[i];
if (!device)
continue;
for (uint8_t bar_num = 0; bar_num < PCI_MAX_BARS; ++bar_num) {
mtx_lock(&device->mutex);
uint16_t command = device->command;
pci_bar_t* bar = &device->bar[bar_num];
uint32_t bar_base = pci_bar_base(bar);
uint16_t bar_size = pci_bar_size(bar);
mtx_unlock(&device->mutex);
// Ensure IO operations are enabled for this device.
if (!pci_device_io_enabled(io_type, command))
continue;
// Check if the BAR is implemented and configured for the requested
// IO type.
if (!bar_size || bar->io_type != io_type)
continue;
if (addr >= bar_base && addr < bar_base + bar_size) {
*bar_out = bar_num;
*off = static_cast<uint16_t>(addr - bar_base);
return bus->device[i];
}
}
}
return NULL;
}
uint32_t pci_bar_base(pci_bar_t* bar) {
switch (bar->io_type) {
case PCI_BAR_IO_TYPE_PIO:
return bar->addr & kPioAddressMask;
case PCI_BAR_IO_TYPE_MMIO:
return bar->addr & kMmioAddressMask;
default:
return 0;
}
}
uint16_t pci_bar_size(pci_bar_t* bar) {
return static_cast<uint16_t>(bar->size);
}
static zx_status_t pci_handler(zx_port_packet_t* packet, void* ctx) {
pci_device_t* pci_device = static_cast<pci_device_t*>(ctx);
// We provide the bar number as the trap key.
if (packet->key > UINT8_MAX)
return ZX_ERR_OUT_OF_RANGE;
uint8_t bar = static_cast<uint8_t>(packet->key);
zx_vcpu_io_t io;
io.access_size = packet->guest_io.access_size;
io.u32 = packet->guest_io.u32;
uint32_t bar_base = pci_bar_base(&pci_device->bar[bar]);
uint16_t device_port = static_cast<uint16_t>(packet->guest_io.port - bar_base);
return pci_device->ops->write_bar(pci_device, bar, device_port, &io);
}
zx_status_t pci_device_async(pci_device_t* device, zx_handle_t guest) {
size_t num_traps = 0;
trap_args_t traps[PCI_MAX_BARS];
for (uint8_t i = 0; i < PCI_MAX_BARS; ++i) {
pci_bar_t* bar = &device->bar[i];
if (!pci_bar_implemented(device, i))
continue;
trap_args_t* trap = &traps[num_traps++];
trap->key = i;
trap->addr = pci_bar_base(bar);
trap->len = pci_bar_size(bar);
trap->kind = bar->io_type == PCI_BAR_IO_TYPE_PIO ? ZX_GUEST_TRAP_IO : ZX_GUEST_TRAP_MEM;
}
return device_async(guest, traps, num_traps, pci_handler, device);
}
zx_status_t pci_interrupt(pci_device_t* pci_device) {
pci_bus_t* bus = pci_device->bus;
if (!bus)
return ZX_ERR_BAD_STATE;
return io_apic_interrupt(bus->io_apic, pci_device->global_irq);
}