1/* 2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "los_debug.h" 17#include "los_arch_interrupt.h" 18#include "los_interrupt.h" 19#include "lwip/mem.h" 20#include "virtmmio.h" 21 22#define IRQF_SHARED 0 23 24static inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev) 25{ 26 return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS); 27} 28 29static inline void VirtioAddStatus(const struct VirtmmioDev *dev, uint32_t val) 30{ 31 FENCE_WRITE_UINT32(VirtioGetStatus(dev) | val, dev->base + VIRTMMIO_REG_STATUS); 32} 33 34static inline void VirtioResetStatus(const struct VirtmmioDev *dev) 35{ 36 FENCE_WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS); 37} 38 39bool VirtmmioDiscover(uint32_t devId, struct VirtmmioDev *dev) 40{ 41 VADDR_T base; 42 int i; 43 44 base = IO_DEVICE_ADDR(VIRTMMIO_BASE_ADDR) + VIRTMMIO_BASE_SIZE * (NUM_VIRTIO_TRANSPORTS - 1); 45 for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { 46 if ((GET_UINT32(base + VIRTMMIO_REG_MAGICVALUE) == VIRTMMIO_MAGIC) && 47 (GET_UINT32(base + VIRTMMIO_REG_VERSION) == VIRTMMIO_VERSION) && 48 (GET_UINT32(base + VIRTMMIO_REG_DEVICEID) == devId)) { 49 dev->base = base; 50 dev->irq = IRQ_SPI_BASE + VIRTMMIO_BASE_IRQ + i; 51 return true; 52 } 53 54 base -= VIRTMMIO_BASE_SIZE; 55 } 56 57 PRINT_ERR("virtio-mmio ID=%u device not found\n", devId); 58 return false; 59} 60 61unsigned VirtqSize(uint16_t qsz) 62{ 63 /* pretend we do not have an aligned start address */ 64 return VIRTQ_ALIGN_DESC - 1 + 65 ALIGN(sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL) + 66 ALIGN(sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED) + 67 sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz; 68} 69 70void VirtmmioInitBegin(const struct VirtmmioDev *dev) 71{ 72 VirtioResetStatus(dev); 73 VirtioAddStatus(dev, VIRTIO_STATUS_ACK); 74 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER); 75 while ((VirtioGetStatus(dev) & VIRTIO_STATUS_DRIVER) == 0) { } 76} 77 78void VritmmioInitEnd(const struct VirtmmioDev *dev) 79{ 80 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK); 81} 82 83void VirtmmioInitFailed(const struct VirtmmioDev *dev) 84{ 85 VirtioAddStatus(dev, VIRTIO_STATUS_FAILED); 86} 87 88static bool Negotiate(struct VirtmmioDev *baseDev, uint32_t nth, VirtioFeatureFn fn, void *dev) 89{ 90 uint32_t features, supported, before, after; 91 92 FENCE_WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DEVFEATURESEL); 93 features = GET_UINT32(baseDev->base + VIRTMMIO_REG_DEVFEATURE); 94 95 do { 96 before = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION); 97 98 supported = 0; 99 if (!fn(features, &supported, dev)) { 100 return false; 101 } 102 103 after = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION); 104 } while (before != after); 105 106 FENCE_WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DRVFEATURESEL); 107 FENCE_WRITE_UINT32(supported, baseDev->base + VIRTMMIO_REG_DRVFEATURE); 108 return true; 109} 110 111bool VirtmmioNegotiate(struct VirtmmioDev *baseDev, VirtioFeatureFn f0, VirtioFeatureFn f1, void *dev) 112{ 113 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD0, f0, dev)) { 114 return false; 115 } 116 117 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD1, f1, dev)) { 118 return false; 119 } 120 121 VirtioAddStatus(baseDev, VIRTIO_STATUS_FEATURES_OK); 122 if ((VirtioGetStatus(baseDev) & VIRTIO_STATUS_FEATURES_OK) == 0) { 123 PRINT_ERR("negotiate features failed\n"); 124 return false; 125 } 126 127 return true; 128} 129 130#define U32_MASK 0xFFFFFFFF 131#define U32_BYTES 4 132#define U64_32_SHIFT 32 133uint64_t u32_to_u64(uint32_t addr) { 134 uint64_t paddr = (uint64_t)addr & U32_MASK; 135 return paddr; 136} 137static void WriteQueueAddr(uint64_t addr, const struct VirtmmioDev *dev, uint32_t regLow) 138{ 139 uint32_t paddr; 140 141 paddr = addr & U32_MASK; 142 FENCE_WRITE_UINT32(paddr, dev->base + regLow); 143 paddr = addr >> U64_32_SHIFT; 144 FENCE_WRITE_UINT32(paddr, dev->base + regLow + U32_BYTES); 145} 146 147static bool CompleteConfigQueue(uint32_t queue, const struct VirtmmioDev *dev) 148{ 149 const struct Virtq *q = &dev->vq[queue]; 150 uint32_t num; 151 152 FENCE_WRITE_UINT32(queue, dev->base + VIRTMMIO_REG_QUEUESEL); 153 154 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUEREADY); 155 LOS_ASSERT(num == 0); 156 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUENUMMAX); 157 if (num < q->qsz) { 158 PRINT_ERR("queue %u not available: max qsz=%u, requested=%u\n", queue, num, q->qsz); 159 return false; 160 } 161 162 FENCE_WRITE_UINT32(q->qsz, dev->base + VIRTMMIO_REG_QUEUENUM); 163 WriteQueueAddr(u32_to_u64(q->desc), dev, VIRTMMIO_REG_QUEUEDESCLOW); 164 WriteQueueAddr(u32_to_u64(q->avail), dev, VIRTMMIO_REG_QUEUEDRIVERLOW); 165 WriteQueueAddr(u32_to_u64(q->used), dev, VIRTMMIO_REG_QUEUEDEVICELOW); 166 167 FENCE_WRITE_UINT32(1, dev->base + VIRTMMIO_REG_QUEUEREADY); 168 return true; 169} 170 171static VADDR_T CalculateQueueAddr(VADDR_T base, uint16_t qsz, struct Virtq *q) 172{ 173 base = ALIGN(base, VIRTQ_ALIGN_DESC); 174 q->desc = (struct VirtqDesc *)base; 175 q->qsz = qsz; 176 base = ALIGN(base + sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL); 177 q->avail = (struct VirtqAvail *)base; 178 base = ALIGN(base + sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED); 179 q->used = (struct VirtqUsed *)base; 180 181 return base + sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz; 182} 183 184VADDR_T VirtmmioConfigQueue(struct VirtmmioDev *dev, VADDR_T base, uint16_t qsz[], int num) 185{ 186 uint32_t i; 187 188 for (i = 0; i < num; i++) { 189 base = CalculateQueueAddr(base, qsz[i], &dev->vq[i]); 190 if (!CompleteConfigQueue(i, dev)) { 191 return 0; 192 } 193 } 194 195 return base; 196} 197 198bool VirtmmioRegisterIRQ(struct VirtmmioDev *dev, HWI_PROC_FUNC handle, void *argDev, const char *devName) 199{ 200 uint32_t ret; 201 HwiIrqParam *param = mem_calloc(1, sizeof(HwiIrqParam)); 202 param->swIrq = dev->irq; 203 param->pDevId = argDev; 204 param->pName = devName; 205 206 ret = LOS_HwiCreate(dev->irq, OS_HWI_PRIO_HIGHEST, IRQF_SHARED, handle, param); 207 if (ret != 0) { 208 PRINT_ERR("virtio-mmio %s IRQ register failed: %u\n", devName, ret); 209 return false; 210 } 211 212 HalIrqEnable(dev->irq); 213 return true; 214} 215