1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7/* VMM PCI Driver, which manages the host's PCI devices, and handles guest OS PCI config space
8 * read & writes.
9 */
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include <sel4/sel4.h>
16#include <pci/pci.h>
17#include <pci/helper.h>
18
19#include <sel4vmmplatsupport/drivers/pci.h>
20#include <sel4vmmplatsupport/drivers/pci_helper.h>
21
22#define NUM_DEVICES 32
23#define NUM_FUNCTIONS 8
24
25int vmm_pci_init(vmm_pci_space_t **space)
26{
27    vmm_pci_space_t *pci_space = (vmm_pci_space_t *)calloc(1, sizeof(vmm_pci_space_t));
28    if (!pci_space) {
29        ZF_LOGE("Failed to calloc memory for pci space");
30        return -1;
31    }
32
33    for (int i = 0; i < NUM_DEVICES; i++) {
34        for (int j = 0; j < NUM_FUNCTIONS; j++) {
35            pci_space->bus0[i][j] = NULL;
36        }
37    }
38    pci_space->conf_port_addr = 0;
39    /* Define the initial PCI bridge */
40    vmm_pci_device_def_t *bridge = calloc(1, sizeof(*bridge));
41    if (!bridge) {
42        ZF_LOGE("Failed to calloc memory for pci bridge");
43        return -1;
44    }
45    define_pci_host_bridge(bridge);
46    *space = pci_space;
47    return vmm_pci_add_entry(pci_space, (vmm_pci_entry_t) {
48        .cookie = bridge, .ioread = vmm_pci_mem_device_read, .iowrite = vmm_pci_entry_ignore_write
49    }, NULL);
50}
51
52int vmm_pci_add_entry(vmm_pci_space_t *space, vmm_pci_entry_t entry, vmm_pci_address_t *addr)
53{
54    /* Find empty dev */
55    for (int i = 0; i < 32; i++) {
56        if (!space->bus0[i][0]) {
57            /* Allocate an entry */
58            space->bus0[i][0] = calloc(1, sizeof(entry));
59
60            *space->bus0[i][0] = entry;
61            /* Report addr if reqeusted */
62            if (addr) {
63                *addr = (vmm_pci_address_t) {
64                    .bus = 0, .dev = i, .fun = 0
65                };
66            }
67            ZF_LOGI("Adding virtual PCI device at %02x:%02x.%d", 0, i, 0);
68            return 0;
69        }
70    }
71    ZF_LOGE("No free device slot on bus 0 to add virtual pci device");
72    return -1;
73}
74
75void make_addr_reg_from_config(uint32_t conf, vmm_pci_address_t *addr, uint8_t *reg)
76{
77    addr->bus = (conf >> 16) & MASK(8);
78    addr->dev = (conf >> 11) & MASK(5);
79    addr->fun = (conf >> 8) & MASK(3);
80    *reg = conf & MASK(8);
81}
82
83vmm_pci_entry_t *find_device(vmm_pci_space_t *self, vmm_pci_address_t addr)
84{
85    if (addr.bus != 0 || addr.dev >= 32 || addr.fun >= 8) {
86        return NULL;
87    }
88    return self->bus0[addr.dev][addr.fun];
89}
90