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
VirtioGetStatus(const struct VirtmmioDev * dev)19 static inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev)
20 {
21 return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS);
22 }
23
VirtioAddStatus(const struct VirtmmioDev * dev,uint32_t val)24 static inline void VirtioAddStatus(const struct VirtmmioDev *dev, uint32_t val)
25 {
26 WRITE_UINT32(VirtioGetStatus(dev) | val, dev->base + VIRTMMIO_REG_STATUS);
27 }
28
VirtioResetStatus(const struct VirtmmioDev * dev)29 static inline void VirtioResetStatus(const struct VirtmmioDev *dev)
30 {
31 WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS);
32 }
33
VirtmmioDiscover(uint32_t devId,struct VirtmmioDev * dev)34 bool 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
VirtqSize(uint16_t qsz)56 unsigned 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
VirtmmioInitBegin(const struct VirtmmioDev * dev)65 void 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
VritmmioInitEnd(const struct VirtmmioDev * dev)73 void VritmmioInitEnd(const struct VirtmmioDev *dev)
74 {
75 VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK);
76 }
77
VirtmmioInitFailed(const struct VirtmmioDev * dev)78 void VirtmmioInitFailed(const struct VirtmmioDev *dev)
79 {
80 VirtioAddStatus(dev, VIRTIO_STATUS_FAILED);
81 }
82
Negotiate(struct VirtmmioDev * baseDev,uint32_t nth,VirtioFeatureFn fn,void * dev)83 static 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
VirtmmioNegotiate(struct VirtmmioDev * baseDev,VirtioFeatureFn f0,VirtioFeatureFn f1,void * dev)106 bool 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
WriteQueueAddr(uint64_t addr,const struct VirtmmioDev * dev,uint32_t regLow)128 static 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
CompleteConfigQueue(uint32_t queue,const struct VirtmmioDev * dev)139 static 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
CalculateQueueAddr(VADDR_T base,uint16_t qsz,struct Virtq * q)163 static 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
VirtmmioConfigQueue(struct VirtmmioDev * dev,VADDR_T base,uint16_t qsz[],int num)176 VADDR_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
VirtmmioRegisterIRQ(struct VirtmmioDev * dev,HWI_PROC_FUNC handle,void * argDev,const char * devName)190 bool 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