1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <stdlib.h> 8#include <sel4/sel4.h> 9#include <vka/capops.h> 10 11#include <sel4vm/guest_vm.h> 12#include <sel4vm/guest_memory.h> 13#include <sel4vm/guest_memory_helpers.h> 14#include <sel4vm/guest_irq_controller.h> 15#include <sel4vm/guest_vcpu_fault.h> 16#include <sel4vm/boot.h> 17 18#include <sel4vmmplatsupport/device.h> 19#include <sel4vmmplatsupport/guest_memory_util.h> 20#include <sel4vmmplatsupport/drivers/virtio.h> 21#include <sel4vmmplatsupport/drivers/pci_helper.h> 22#include <sel4vmmplatsupport/drivers/cross_vm_connection.h> 23 24#include <pci/helper.h> 25 26#define MAX_NUM_CONNECTIONS 32 27 28#define EVENT_BAR_EMIT_REGISTER 0x0 29#define EVENT_BAR_EMIT_REGISTER_INDEX 0 30#define EVENT_BAR_CONSUME_EVENT_REGISTER 0x4 31#define EVENT_BAR_CONSUME_EVENT_REGISTER_INDEX 1 32#define EVENT_BAR_DEVICE_NAME_REGISTER 0x8 33#define EVENT_BAR_DEVICE_NAME_MAX_LEN 50 34 35struct connection_info { 36 uintptr_t event_address; 37 void *event_registers; 38 uintptr_t dataport_address; 39 size_t dataport_size_bits; 40 crossvm_handle_t connection; 41 int connection_irq; 42}; 43 44struct dataport_iterator_cookie { 45 seL4_CPtr *dataport_frames; 46 uintptr_t dataport_start; 47 size_t dataport_size; 48 vm_t *vm; 49}; 50 51struct connection_info info[MAX_NUM_CONNECTIONS]; 52int total_connections; 53 54static int construct_connection_bar(vm_t *vm, struct connection_info *info, int num_connections, vmm_pci_space_t *pci) 55{ 56 for (int conn_idx = 0; conn_idx < num_connections; conn_idx++) { 57 vmm_pci_device_def_t *pci_config; 58 pci_config = calloc(1, sizeof(*pci_config)); 59 ZF_LOGF_IF(pci_config == NULL, "Failed to allocate pci config"); 60 *pci_config = (vmm_pci_device_def_t) { 61 .vendor_id = 0x1af4, 62 .device_id = 0xa111, 63 .revision_id = 0x1, 64 .subsystem_vendor_id = 0x0, 65 .subsystem_id = 0x0, 66 .interrupt_pin = 1, 67 .interrupt_line = info[conn_idx].connection_irq, 68 .bar0 = info[conn_idx].event_address | PCI_BASE_ADDRESS_SPACE_MEMORY, 69 .bar1 = info[conn_idx].dataport_address | PCI_BASE_ADDRESS_SPACE_MEMORY, 70 .command = PCI_COMMAND_MEMORY, 71 .header_type = PCI_HEADER_TYPE_NORMAL, 72 .subclass = (PCI_CLASS_MEMORY_RAM >> 8) & 0xff, 73 .class_code = (PCI_CLASS_MEMORY_RAM >> 16) & 0xff, 74 }; 75 76 vmm_pci_entry_t entry = (vmm_pci_entry_t) { 77 .cookie = pci_config, 78 .ioread = vmm_pci_mem_device_read, 79 .iowrite = vmm_pci_mem_device_write 80 }; 81 82 vmm_pci_bar_t bars[2] = { 83 { 84 .mem_type = PREFETCH_MEM, 85 .address = info[conn_idx].event_address, 86 /* if size_bits isn't the same for all resources then Linux tries 87 to remap the resources which the vmm driver doesn't support. 88 */ 89 .size_bits = info[conn_idx].dataport_size_bits 90 }, 91 { 92 .mem_type = PREFETCH_MEM, 93 .address = info[conn_idx].dataport_address, 94 .size_bits = info[conn_idx].dataport_size_bits 95 } 96 }; 97 vmm_pci_entry_t connection_pci_bar; 98 /* TODO: Make both architecture go through the same interface */ 99 if (config_set(CONFIG_ARCH_X86)) { 100 connection_pci_bar = vmm_pci_create_bar_emulation(entry, 2, bars); 101 } else if (config_set(CONFIG_ARCH_ARM)) { 102 connection_pci_bar = vmm_pci_create_passthrough_bar_emulation(entry, 2, bars); 103 } 104 vmm_pci_add_entry(pci, connection_pci_bar, NULL); 105 } 106 return 0; 107} 108 109static memory_fault_result_t handle_event_bar_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr, 110 size_t fault_length, void *cookie) 111{ 112 struct device *d = (struct device *)cookie; 113 struct connection_info *info = (struct connection_info *)d->priv; 114 uint8_t reg = (fault_addr - d->pstart) & 0xff; 115 116 if (is_vcpu_read_fault(vcpu)) { 117 /* This shouldn't happen - the guest should have read writes to the event bar */ 118 ZF_LOGE("Error: Event Bar Memory is misconfigured"); 119 } else if (reg == EVENT_BAR_EMIT_REGISTER) { 120 /* Emit operation: Write to register 0 */ 121 if (!info->connection.emit_fn) { 122 ZF_LOGE("Connection is not configured with an emit function\n"); 123 } else { 124 info->connection.emit_fn(); 125 } 126 } else if (reg == EVENT_BAR_CONSUME_EVENT_REGISTER) { 127 uint32_t mask = get_vcpu_fault_data_mask(vcpu); 128 uint32_t value = get_vcpu_fault_data(vcpu) & mask; 129 uint32_t *event_registers = (uint32_t *)info->event_registers; 130 event_registers[EVENT_BAR_CONSUME_EVENT_REGISTER_INDEX] = value; 131 } else { 132 ZF_LOGE("Event Bar register unsupported"); 133 } 134 advance_vcpu_fault(vcpu); 135 return FAULT_HANDLED; 136} 137 138static int reserve_event_bar(vm_t *vm, uintptr_t event_bar_address, struct connection_info *info) 139{ 140 struct device *event_bar = calloc(1, sizeof(struct device)); 141 if (!event_bar) { 142 ZF_LOGE("Failed to create event bar device"); 143 return -1; 144 } 145 event_bar->pstart = event_bar_address; 146 event_bar->priv = (void *)info; 147 148 info->event_registers = create_allocated_reservation_frame(vm, event_bar_address, seL4_CanRead, 149 handle_event_bar_fault, event_bar); 150 if (info->event_registers == NULL) { 151 ZF_LOGE("Failed to map emulated event bar space"); 152 return -1; 153 } 154 /* Zero out memory */ 155 memset(info->event_registers, 0, PAGE_SIZE); 156 info->event_address = event_bar_address; 157 return 0; 158} 159 160static vm_frame_t dataport_memory_iterator(uintptr_t addr, void *cookie) 161{ 162 int error; 163 cspacepath_t return_frame; 164 vm_frame_t frame_result = { seL4_CapNull, seL4_NoRights, 0, 0 }; 165 struct dataport_iterator_cookie *dataport_cookie = (struct dataport_iterator_cookie *)cookie; 166 seL4_CPtr *dataport_frames = dataport_cookie->dataport_frames; 167 vm_t *vm = dataport_cookie->vm; 168 uintptr_t dataport_start = dataport_cookie->dataport_start; 169 size_t dataport_size = dataport_cookie->dataport_size; 170 int page_size = seL4_PageBits; 171 172 uintptr_t frame_start = ROUND_DOWN(addr, BIT(page_size)); 173 if (frame_start < dataport_start || 174 frame_start > dataport_start + dataport_size) { 175 ZF_LOGE("Error: Not Dataport region"); 176 return frame_result; 177 } 178 int page_idx = (frame_start - dataport_start) / BIT(page_size); 179 frame_result.cptr = dataport_frames[page_idx]; 180 frame_result.rights = seL4_AllRights; 181 frame_result.vaddr = frame_start; 182 frame_result.size_bits = page_size; 183 return frame_result; 184} 185 186static int reserve_dataport_memory(vm_t *vm, crossvm_dataport_handle_t *dataport, uintptr_t dataport_address, 187 struct connection_info *info) 188{ 189 int err; 190 size_t size = dataport->size; 191 unsigned int num_frames = dataport->num_frames; 192 seL4_CPtr *frames = dataport->frames; 193 194 vm_memory_reservation_t *dataport_reservation = vm_reserve_memory_at(vm, dataport_address, size, 195 default_error_fault_callback, 196 NULL); 197 struct dataport_iterator_cookie *dataport_cookie = malloc(sizeof(struct dataport_iterator_cookie)); 198 if (!dataport_cookie) { 199 ZF_LOGE("Failed to allocate dataport iterator cookie"); 200 return -1; 201 } 202 dataport_cookie->vm = vm; 203 dataport_cookie->dataport_frames = frames; 204 dataport_cookie->dataport_start = dataport_address; 205 dataport_cookie->dataport_size = size; 206 err = vm_map_reservation(vm, dataport_reservation, dataport_memory_iterator, (void *)dataport_cookie); 207 if (err) { 208 ZF_LOGE("Failed to map dataport memory"); 209 return -1; 210 } 211 info->dataport_address = dataport_address; 212 info->dataport_size_bits = BYTES_TO_SIZE_BITS(size); 213 return 0; 214} 215 216static void connection_consume_ack(vm_vcpu_t *vcpu, int irq, void *cookie) {} 217 218void consume_connection_event(vm_t *vm, seL4_Word event_id, bool inject_irq) 219{ 220 struct connection_info *conn_info = NULL; 221 /* Search for a connection with a matching badge */ 222 for (int i = 0; i < total_connections; i++) { 223 if (info[i].connection.consume_id == event_id) { 224 conn_info = &info[i]; 225 break; 226 } 227 } 228 if (conn_info == NULL) { 229 /* No match */ 230 return; 231 } 232 /* We have an event - update the value in the event status register */ 233 uint32_t *event_registers = (uint32_t *)conn_info->event_registers; 234 /* Increment the register to indicate a signal event occured - 235 * we assume our kernel module will clear it as it consumes interrupt */ 236 event_registers[EVENT_BAR_CONSUME_EVENT_REGISTER_INDEX]++; 237 if (inject_irq) { 238 /* Inject our event interrupt */ 239 int err = vm_inject_irq(vm->vcpus[BOOT_VCPU], conn_info->connection_irq); 240 if (err) { 241 ZF_LOGE("Failed to inject connection irq"); 242 } 243 } 244 return; 245} 246 247static int register_consume_event(vm_t *vm, crossvm_handle_t *connection, struct connection_info *conn_info) 248{ 249 if (connection->consume_id != -1 && conn_info->connection_irq > 0) { 250 /* Register an irq for the crossvm connection */ 251 int err = vm_register_irq(vm->vcpus[BOOT_VCPU], conn_info->connection_irq, connection_consume_ack, NULL); 252 if (err) { 253 ZF_LOGE("Failed to register IRQ for event consume"); 254 return -1; 255 } 256 } 257 return 0; 258} 259 260static int initialise_connections(vm_t *vm, uintptr_t connection_base_addr, crossvm_handle_t *connections, 261 int num_connections, struct connection_info *info, int connection_irq) 262{ 263 int err; 264 uintptr_t connection_curr_addr = connection_base_addr; 265 for (int i = 0; i < num_connections; i++) { 266 /* We need to round everything up to the largest sized resource to prevent 267 * Linux from remapping the devices, which the vpci device can't emulate. 268 */ 269 crossvm_dataport_handle_t *dataport = connections[i].dataport; 270 uintptr_t dataport_size = dataport->size; 271 err = reserve_event_bar(vm, connection_curr_addr, &info[i]); 272 if (err) { 273 ZF_LOGE("Failed to create event bar (id:%d)", i); 274 return -1; 275 } 276 connection_curr_addr += dataport_size; 277 err = reserve_dataport_memory(vm, dataport, connection_curr_addr, &info[i]); 278 if (err) { 279 ZF_LOGE("Failed to create dataport bar (id %d)", i); 280 return -1; 281 } 282 /* Register a callback event consuming (if we have one) */ 283 info[i].connection_irq = connection_irq; 284 err = register_consume_event(vm, &connections[i], &info[i]); 285 if (err) { 286 ZF_LOGE("Failed to register connections consume event"); 287 return -1; 288 } 289 info[i].connection = connections[i]; 290 if (connections[i].connection_name == NULL) { 291 connections[i].connection_name = "connector"; 292 } 293 strncpy(info[i].event_registers + EVENT_BAR_DEVICE_NAME_REGISTER, connections[i].connection_name, 294 EVENT_BAR_DEVICE_NAME_MAX_LEN); 295 296 connection_curr_addr += dataport_size; 297 } 298 return 0; 299} 300 301int cross_vm_connections_init_common(vm_t *vm, uintptr_t connection_base_addr, crossvm_handle_t *connections, 302 int num_connections, vmm_pci_space_t *pci, alloc_free_interrupt_fn alloc_irq) 303{ 304 uintptr_t guest_paddr = 0; 305 size_t guest_size = 0; 306 if (num_connections > MAX_NUM_CONNECTIONS) { 307 ZF_LOGE("Unable to register more than %d dataports", MAX_NUM_CONNECTIONS); 308 return -1; 309 } 310 int connection_irq = alloc_irq(); 311 int err = initialise_connections(vm, connection_base_addr, connections, num_connections, info, connection_irq); 312 if (err) { 313 ZF_LOGE("Failed to reserve memory for dataports"); 314 return -1; 315 } 316 err = construct_connection_bar(vm, info, num_connections, pci); 317 if (err) { 318 ZF_LOGE("Failed to construct pci device for dataports"); 319 return -1; 320 } 321 total_connections = num_connections; 322 return 0; 323} 324