1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <autoconf.h> 14#include <rumprun/gen_config.h> 15#include <bmk-core/types.h> 16#include <sel4/kernel.h> 17#include <assert.h> 18 19#include <bmk-core/pgalloc.h> 20#include <bmk-core/printf.h> 21#include <bmk-pcpu/pcpu.h> 22 23#include <vka/object_capops.h> 24 25#include <platsupport/io.h> 26#include <sel4/helpers.h> 27#include "pci_user.h" 28 29#define PCI_CONF_ADDR 0xcf8 30#define PCI_CONF_DATA 0xcfc 31 32 33// #define TRACK_PCI_MAPPINGS 34 35#ifdef TRACK_PCI_MAPPINGS 36/* Currently an arbitrary number */ 37#define NUM_PCI_MAPPINGS 50 38static void *addresses[NUM_PCI_MAPPINGS]; 39static uint32_t sizes[NUM_PCI_MAPPINGS]; 40#endif /* TRACK_PCI_MAPPINGS */ 41 42static struct { 43 int intrs; 44 int bus; 45 int dev; 46 int function; 47} pci_data[BMK_MAXINTR]; 48 49/* Wrappers to pass through to sel4 */ 50int rumpcomp_pci_port_out(uint32_t port, int io_size, uint32_t val) 51{ 52 if (env.custom_simple.camkes) { 53 ZF_LOGF("ERROR\n"); 54 } else { 55 return ps_io_port_out(&env.io_ops.io_port_ops, port, io_size, val); 56 } 57 return 0; 58} 59 60int rumpcomp_pci_port_in(uint32_t port, int io_size, uint32_t *result) 61{ 62 if (env.custom_simple.camkes) { 63 ZF_LOGF("ERROR\n"); 64 65 } else { 66 return ps_io_port_in(&env.io_ops.io_port_ops, port, io_size, result); 67 } 68 return 0; 69} 70 71int rumpcomp_pci_intr_type(void) 72{ 73#ifdef CONFIG_USE_MSI_ETH 74 return 1; //PCI_INTR_TYPE_MSI; 75#else 76 return 0; //PCI_INTR_TYPE_INTX; 77#endif 78} 79 80/* Don't support iospace yet */ 81int rumpcomp_pci_iospace_init(void) 82{ 83 return 0; 84} 85 86static uint32_t makeaddr(unsigned bus, unsigned dev, unsigned fun, int reg) 87{ 88 89 return (BIT(31)) | (bus << 16u) | (dev << 11u) | (fun << 8u) | (reg & 0xfc); 90} 91 92int rumpcomp_pci_confread(unsigned bus, unsigned dev, unsigned fun, int reg, 93 unsigned int *value) 94{ 95 uint32_t addr; 96 unsigned int data; 97 int res; 98 addr = makeaddr(bus, dev, fun, reg); 99 if (!is_hw_pci_config(&env.custom_simple)) { 100 *value = env.custom_simple.pci_config_config.pci_config_read32(bus, dev, fun, reg); 101 return 0; 102 } 103 res = rumpcomp_pci_port_out(PCI_CONF_ADDR, 4, addr); 104 105 if (res) { 106 return res; 107 } 108 res = rumpcomp_pci_port_in(PCI_CONF_DATA, 4, &data); 109 if (res) { 110 return res; 111 } 112 *value = data; 113 return res; 114} 115 116int rumpcomp_pci_confwrite(unsigned bus, unsigned dev, unsigned fun, int reg, 117 unsigned int value) 118{ 119 uint32_t addr; 120 int res; 121 if (!is_hw_pci_config(&env.custom_simple)) { 122 env.custom_simple.pci_config_config.pci_config_write32(bus, dev, fun, reg, value); 123 return 0; 124 } 125 126 addr = makeaddr(bus, dev, fun, reg); 127 res = rumpcomp_pci_port_out(PCI_CONF_ADDR, 4, addr); 128 129 if (res) { 130 return res; 131 } 132 res = rumpcomp_pci_port_out(PCI_CONF_DATA, 4, value); 133 134 if (res) { 135 return res; 136 } 137 return 0; 138} 139 140/* map interrupts */ 141/* TODO Refactor this section to better support different underlying platforms. 142 Implement the arch_simple interface */ 143int rumpcomp_pci_irq_map(unsigned bus, unsigned device, unsigned fun, 144 int intrline, unsigned cookie) 145{ 146 if (cookie > BMK_MAXINTR) { 147 return BMK_EGENERIC; 148 } 149 150 pci_data[cookie].intrs = intrline; 151 pci_data[cookie].bus = bus; 152 pci_data[cookie].dev = device; 153 pci_data[cookie].function = fun; 154 return 0; 155} 156 157int rumpcomp_pci_get_bdf(unsigned cookie, unsigned *bus, unsigned *dev, unsigned *function) 158{ 159 if (cookie > BMK_MAXINTR) { 160 return 1; 161 } 162 163 *bus = pci_data[cookie].bus; 164 *dev = pci_data[cookie].dev; 165 *function = pci_data[cookie].function; 166 return 0; 167} 168 169/* Create interrupt and notification objects */ 170void *rumpcomp_pci_irq_establish(unsigned cookie, int (*handler)(void *), void *data) 171{ 172 if (env.caps[pci_data[cookie].intrs] == 0 && !env.custom_simple.camkes) { 173 int error = vka_cspace_alloc(&env.vka, &env.caps[pci_data[cookie].intrs]); 174 ZF_LOGF_IF(error != 0, "Failed to allocate cslot, error %d", error); 175 cspacepath_t path; 176 vka_cspace_make_path(&env.vka, env.caps[pci_data[cookie].intrs], &path); 177 178#if defined CONFIG_IRQ_IOAPIC && CONFIG_USE_MSI_ETH 179 int irq = pci_data[cookie].intrs; 180 irq = 6; 181 error = seL4_IRQControl_GetMSI(simple_get_irq_ctrl(&env.simple), path.root, path.capPtr, path.capDepth, 182 pci_data[cookie].bus, pci_data[cookie].dev, pci_data[cookie].function, 0, irq); 183 184 if (error != 0) { 185 bmk_printf("Failed IRQControl Get MSI\n"); 186 } 187 /* 188 * XXX: We use a pretty static way of picking IRQ numbers here. 189 * The rumpkernel pci driver is giving us interrupt numbers for the PIC 190 * even when we are running in IOAPIC mode. For now, we override the IRQ 191 * numbers based on what the root task gives us. 192 */ 193#elif defined CONFIG_IRQ_IOAPIC 194 ps_irq_t irq = {0}; 195 error = custom_irq_from_pci_device(&env.custom_simple, pci_data[cookie].bus, pci_data[cookie].dev, 196 pci_data[cookie].function, &irq); 197 ZF_LOGF_IF(error == -1, "Failed to find IRQ number\n"); 198 error = seL4_IRQControl_GetIOAPIC(simple_get_irq_ctrl(&env.simple), path.root, path.capPtr, 199 path.capDepth, irq.ioapic.ioapic, irq.ioapic.pin, irq.ioapic.level, irq.ioapic.polarity, irq.ioapic.vector); 200 if (error != 0) { 201 bmk_printf("Failed to get IOAPIC, error = %d\n", error); 202 } 203 error = seL4_IRQHandler_Ack(env.caps[pci_data[cookie].intrs]); 204 if (error != 0) { 205 bmk_printf("Failed to Ack IOAPIC\n"); 206 } 207 208#else /* using PIC */ 209 210 error = seL4_IRQControl_Get(simple_get_irq_ctrl(&env.simple), pci_data[cookie].intrs, path.root, path.capPtr, 211 path.capDepth); 212 ZF_LOGF_IF(error != 0, "Failed to get IRQControl\n"); 213 error = seL4_IRQHandler_Ack(env.caps[pci_data[cookie].intrs]); 214 ZF_LOGF_IF(error != 0, "Failed to ack IRQ handler\n"); 215#endif /* CONFIG_IRQ_IOAPIC */ 216 217 cspacepath_t path2; 218 error = vka_mint_object(&env.vka, &env.pci_notification, &path2, 219 seL4_AllRights, 1 << (pci_data[cookie].intrs)); 220 ZF_LOGF_IF(error != 0, "Failed to mint notification object\n"); 221 error = seL4_IRQHandler_SetNotification(env.caps[pci_data[cookie].intrs], path2.capPtr); 222 ZF_LOGF_IF(error != 0, "Failed to bind IRQ to notification\n"); 223 } 224 225 bmk_isr_rumpkernel(handler, data, pci_data[cookie].intrs, HARDWARE_INT); 226 return &pci_data[cookie].intrs; 227} 228 229 230 231void *rumpcomp_pci_map(unsigned long addr, unsigned long len) 232{ 233 ZF_LOGE("MAP: %lx, %ld", addr, len); 234 void *vaddr = ps_io_map(&env.io_ops.io_mapper, addr, len, 0, PS_MEM_NORMAL); 235#ifdef TRACK_PCI_MAPPINGS 236 int i; 237 for (i = 0; i < NUM_PCI_MAPPINGS; i++) { 238 if (addresses[i] == NULL) { 239 addresses[i] = vaddr; 240 sizes[i] = len; 241 break; 242 } 243 } 244 ZF_LOGF_IF(i == NUM_PCI_MAPPINGS, "ERROR: Overwrote memory\n"); 245#endif /* TRACK_PCI_MAPPINGS */ 246 return vaddr; 247} 248 249void rumpcomp_pci_unmap(void *addr) 250{ 251 uint32_t len = 0; 252#ifdef TRACK_PCI_MAPPINGS 253 int i; 254 for (i = 0; i < NUM_PCI_MAPPINGS; i++) { 255 if (addresses[i] == addr) { 256 addresses[i] = NULL; 257 len = sizes[i]; 258 break; 259 } 260 } 261 ZF_LOGF_IF(i == NUM_PCI_MAPPINGS, "ERROR: Cannot find entry\n"); 262#endif /* TRACK_PCI_MAPPINGS */ 263 ZF_LOGD("\trumpcomp_pci_unmap: addr 0x%p len %d\n", addr, len); 264 return ps_io_unmap(&env.io_ops.io_mapper, addr, len); 265 266} 267