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_vm_zone.h" 17#include "virtmmio.h" 18 19static inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev) 20{ 21 return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS); 22} 23 24static inline void VirtioAddStatus(const struct VirtmmioDev *dev, uint32_t val) 25{ 26 WRITE_UINT32(VirtioGetStatus(dev) | val, dev->base + VIRTMMIO_REG_STATUS); 27} 28 29static inline void VirtioResetStatus(const struct VirtmmioDev *dev) 30{ 31 WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS); 32} 33 34bool VirtmmioDiscover(uint32_t devId, struct VirtmmioDev *dev) 35{ 36 VADDR_T base; 37 int i; 38 39 base = IO_DEVICE_ADDR(VIRTMMIO_BASE_ADDR) + VIRTMMIO_BASE_SIZE * (NUM_VIRTIO_TRANSPORTS - 1); 40 for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { 41 if ((GET_UINT32(base + VIRTMMIO_REG_MAGICVALUE) == VIRTMMIO_MAGIC) && 42 (GET_UINT32(base + VIRTMMIO_REG_VERSION) == VIRTMMIO_VERSION) && 43 (GET_UINT32(base + VIRTMMIO_REG_DEVICEID) == devId)) { 44 dev->base = base; 45 dev->irq = IRQ_SPI_BASE + VIRTMMIO_BASE_IRQ + i; 46 return true; 47 } 48 49 base -= VIRTMMIO_BASE_SIZE; 50 } 51 52 PRINT_ERR("virtio-mmio ID=%u device not found\n", devId); 53 return false; 54} 55 56unsigned VirtqSize(uint16_t qsz) 57{ 58 /* pretend we do not have an aligned start address */ 59 return VIRTQ_ALIGN_DESC - 1 + 60 ALIGN(sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL) + 61 ALIGN(sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED) + 62 sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz; 63} 64 65void VirtmmioInitBegin(const struct VirtmmioDev *dev) 66{ 67 VirtioResetStatus(dev); 68 VirtioAddStatus(dev, VIRTIO_STATUS_ACK); 69 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER); 70 while ((VirtioGetStatus(dev) & VIRTIO_STATUS_DRIVER) == 0) { } 71} 72 73void VritmmioInitEnd(const struct VirtmmioDev *dev) 74{ 75 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK); 76} 77 78void VirtmmioInitFailed(const struct VirtmmioDev *dev) 79{ 80 VirtioAddStatus(dev, VIRTIO_STATUS_FAILED); 81} 82 83static bool Negotiate(struct VirtmmioDev *baseDev, uint32_t nth, VirtioFeatureFn fn, void *dev) 84{ 85 uint32_t features, supported, before, after; 86 87 WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DEVFEATURESEL); 88 features = GET_UINT32(baseDev->base + VIRTMMIO_REG_DEVFEATURE); 89 90 do { 91 before = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION); 92 93 supported = 0; 94 if (!fn(features, &supported, dev)) { 95 return false; 96 } 97 98 after = GET_UINT32(baseDev->base + VIRTMMIO_REG_CONFIGGENERATION); 99 } while (before != after); 100 101 WRITE_UINT32(nth, baseDev->base + VIRTMMIO_REG_DRVFEATURESEL); 102 WRITE_UINT32(supported, baseDev->base + VIRTMMIO_REG_DRVFEATURE); 103 return true; 104} 105 106bool VirtmmioNegotiate(struct VirtmmioDev *baseDev, VirtioFeatureFn f0, VirtioFeatureFn f1, void *dev) 107{ 108 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD0, f0, dev)) { 109 return false; 110 } 111 112 if(!Negotiate(baseDev, VIRTIO_FEATURE_WORD1, f1, dev)) { 113 return false; 114 } 115 116 VirtioAddStatus(baseDev, VIRTIO_STATUS_FEATURES_OK); 117 if ((VirtioGetStatus(baseDev) & VIRTIO_STATUS_FEATURES_OK) == 0) { 118 PRINT_ERR("negotiate features failed\n"); 119 return false; 120 } 121 122 return true; 123} 124 125#define U32_MASK 0xFFFFFFFF 126#define U32_BYTES 4 127#define U64_32_SHIFT 32 128static void WriteQueueAddr(uint64_t addr, const struct VirtmmioDev *dev, uint32_t regLow) 129{ 130 uint32_t paddr; 131 132 addr = VMM_TO_DMA_ADDR(addr); 133 paddr = addr & U32_MASK; 134 WRITE_UINT32(paddr, dev->base + regLow); 135 paddr = addr >> U64_32_SHIFT; 136 WRITE_UINT32(paddr, dev->base + regLow + U32_BYTES); 137} 138 139static bool CompleteConfigQueue(uint32_t queue, const struct VirtmmioDev *dev) 140{ 141 const struct Virtq *q = &dev->vq[queue]; 142 uint32_t num; 143 144 WRITE_UINT32(queue, dev->base + VIRTMMIO_REG_QUEUESEL); 145 146 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUEREADY); 147 LOS_ASSERT(num == 0); 148 num = GET_UINT32(dev->base + VIRTMMIO_REG_QUEUENUMMAX); 149 if (num < q->qsz) { 150 PRINT_ERR("queue %u not available: max qsz=%u, requested=%u\n", queue, num, q->qsz); 151 return false; 152 } 153 154 WRITE_UINT32(q->qsz, dev->base + VIRTMMIO_REG_QUEUENUM); 155 WriteQueueAddr((uint64_t)q->desc, dev, VIRTMMIO_REG_QUEUEDESCLOW); 156 WriteQueueAddr((uint64_t)q->avail, dev, VIRTMMIO_REG_QUEUEDRIVERLOW); 157 WriteQueueAddr((uint64_t)q->used, dev, VIRTMMIO_REG_QUEUEDEVICELOW); 158 159 WRITE_UINT32(1, dev->base + VIRTMMIO_REG_QUEUEREADY); 160 return true; 161} 162 163static VADDR_T CalculateQueueAddr(VADDR_T base, uint16_t qsz, struct Virtq *q) 164{ 165 base = ALIGN(base, VIRTQ_ALIGN_DESC); 166 q->desc = (struct VirtqDesc *)base; 167 q->qsz = qsz; 168 base = ALIGN(base + sizeof(struct VirtqDesc) * qsz, VIRTQ_ALIGN_AVAIL); 169 q->avail = (struct VirtqAvail *)base; 170 base = ALIGN(base + sizeof(struct VirtqAvail) + sizeof(uint16_t) * qsz, VIRTQ_ALIGN_USED); 171 q->used = (struct VirtqUsed *)base; 172 173 return base + sizeof(struct VirtqUsed) + sizeof(struct VirtqUsedElem) * qsz; 174} 175 176VADDR_T VirtmmioConfigQueue(struct VirtmmioDev *dev, VADDR_T base, uint16_t qsz[], int num) 177{ 178 uint32_t i; 179 180 for (i = 0; i < num; i++) { 181 base = CalculateQueueAddr(base, qsz[i], &dev->vq[i]); 182 if (!CompleteConfigQueue(i, dev)) { 183 return 0; 184 } 185 } 186 187 return base; 188} 189 190bool VirtmmioRegisterIRQ(struct VirtmmioDev *dev, HWI_PROC_FUNC handle, void *argDev, const char *devName) 191{ 192 uint32_t ret; 193 HwiIrqParam param = {0, argDev, devName}; 194 195 ret = LOS_HwiCreate(dev->irq, OS_HWI_PRIO_HIGHEST, IRQF_SHARED, handle, ¶m); 196 if (ret != 0) { 197 PRINT_ERR("virtio-mmio %s IRQ register failed: %u\n", devName, ret); 198 return false; 199 } 200 201 HalIrqUnmask(dev->irq); 202 dev->irq |= ~_IRQ_MASK; 203 return true; 204} 205