1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6/* 7 * DesignWare eMMC 8 */ 9 10#include <stdlib.h> 11#include <string.h> 12 13#include <sel4vm/guest_vm.h> 14#include <sel4vm/guest_vcpu_fault.h> 15 16#include <sel4vmmplatsupport/guest_memory_util.h> 17#include <sel4vmmplatsupport/plat/vsdhc.h> 18#include <sel4vmmplatsupport/device.h> 19#include <sel4vmmplatsupport/plat/devices.h> 20 21#define DWEMMC_DBADDR_OFFSET 0x088 22#define DWEMMC_DSCADDR_OFFSET 0x094 23#define DWEMMC_BUFADDR_OFFSET 0x098 24 25struct sdhc_priv { 26 /* The VM associated with this device */ 27 vm_t *vm; 28 /* Physical registers of the SDHC */ 29 void *regs; 30 /* Residual for 64 bit atomic access to FIFO */ 31 uint32_t a64; 32}; 33 34static memory_fault_result_t handle_sdhc_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr, size_t fault_length, 35 void *cookie) 36{ 37 struct device *d = (struct device *)cookie; 38 struct sdhc_priv *sdhc_data = (struct sdhc_priv *)d->priv; 39 volatile uint32_t *reg; 40 int offset; 41 42 /* Gather fault information */ 43 offset = fault_addr - d->pstart; 44 reg = (uint32_t *)(sdhc_data->regs + offset); 45 /* Handle the fault */ 46 reg = (volatile uint32_t *)(sdhc_data->regs + offset); 47 if (is_vcpu_read_fault(vcpu)) { 48 if (fault_length == sizeof(uint64_t)) { 49 if (offset & 0x4) { 50 /* Unaligned access: report residual */ 51 set_vcpu_fault_data(vcpu, sdhc_data->a64); 52 } else { 53 /* Aligned access: Read in and store residual */ 54 uint64_t v; 55 v = *(volatile uint64_t *)reg; 56 set_vcpu_fault_data(vcpu, v); 57 sdhc_data->a64 = v >> 32; 58 } 59 } else { 60 assert(fault_length == sizeof(seL4_Word)); 61 set_vcpu_fault_data(vcpu, *reg); 62 } 63 ZF_LOGD("[%s] pc0x%x| r0x%x:0x%x\n", d->name, get_vcpu_fault_ip(vcpu), 64 fault_addr, get_vcpu_fault_data(vcpu)); 65 } else { 66 switch (offset & ~0x3) { 67 case DWEMMC_DBADDR_OFFSET: 68 case DWEMMC_DSCADDR_OFFSET: 69 case DWEMMC_BUFADDR_OFFSET: 70 printf("[%s] Restricting DMA access offset 0x%x\n", d->name, offset); 71 break; 72 default: 73 if (fault_length == sizeof(uint64_t)) { 74 if (offset & 0x4) { 75 /* Unaligned acces: store data and residual */ 76 uint64_t v; 77 v = ((uint64_t)get_vcpu_fault_data(vcpu) << 32) | sdhc_data->a64; 78 *(volatile uint64_t *)reg = v; 79 } else { 80 /* Aligned access: record residual */ 81 sdhc_data->a64 = get_vcpu_fault_data(vcpu); 82 } 83 } else { 84 assert(fault_length == sizeof(seL4_Word)); 85 *reg = get_vcpu_fault_data(vcpu); 86 } 87 } 88 89 ZF_LOGD("[%s] pc0x%x| w0x%x:0x%x\n", d->name, get_vcpu_fault_ip(vcpu), 90 fault_addr, get_vcpu_fault_data(vcpu)); 91 } 92 advance_vcpu_fault(vcpu); 93 return FAULT_HANDLED; 94} 95 96 97const struct device dev_msh0 = { 98 .name = "MSH0", 99 .pstart = MSH0_PADDR, 100 .size = 0x1000, 101 .priv = NULL 102}; 103 104const struct device dev_msh2 = { 105 .name = "MSH2", 106 .pstart = MSH2_PADDR, 107 .size = 0x1000, 108 .priv = NULL 109}; 110 111static int vm_install_nodma_sdhc(vm_t *vm, int idx) 112{ 113 struct sdhc_priv *sdhc_data; 114 struct device *d; 115 int err; 116 d = calloc(1, sizeof(struct device)); 117 if (!d) { 118 return -1; 119 } 120 switch (idx) { 121 case 0: 122 *d = dev_msh0; 123 break; 124 case 2: 125 *d = dev_msh2; 126 break; 127 default: 128 assert(0); 129 return -1; 130 } 131 132 /* Initialise the virtual device */ 133 sdhc_data = calloc(1, sizeof(struct sdhc_priv)); 134 if (sdhc_data == NULL) { 135 assert(sdhc_data); 136 return -1; 137 } 138 sdhc_data->vm = vm; 139 sdhc_data->regs = create_device_reservation_frame(vm, d->pstart, seL4_CanRead, 140 handle_sdhc_fault, (void *)d); 141 if (sdhc_data->regs == NULL) { 142 assert(sdhc_data->regs); 143 return -1; 144 } 145 d->priv = sdhc_data; 146 return 0; 147} 148 149int vm_install_nodma_sdhc0(vm_t *vm) 150{ 151 return vm_install_nodma_sdhc(vm, 0); 152} 153 154int vm_install_nodma_sdhc2(vm_t *vm) 155{ 156 return vm_install_nodma_sdhc(vm, 2); 157} 158