• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
VirtioGetStatus(const struct VirtmmioDev * dev)24 static inline uint32_t VirtioGetStatus(const struct VirtmmioDev *dev)
25 {
26     return GET_UINT32(dev->base + VIRTMMIO_REG_STATUS);
27 }
28 
VirtioAddStatus(const struct VirtmmioDev * dev,uint32_t val)29 static 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 
VirtioResetStatus(const struct VirtmmioDev * dev)34 static inline void VirtioResetStatus(const struct VirtmmioDev *dev)
35 {
36     FENCE_WRITE_UINT32(VIRTIO_STATUS_RESET, dev->base + VIRTMMIO_REG_STATUS);
37 }
38 
VirtmmioDiscover(uint32_t devId,struct VirtmmioDev * dev)39 bool 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 
VirtqSize(uint16_t qsz)61 unsigned 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 
VirtmmioInitBegin(const struct VirtmmioDev * dev)70 void 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 
VritmmioInitEnd(const struct VirtmmioDev * dev)78 void VritmmioInitEnd(const struct VirtmmioDev *dev)
79 {
80     VirtioAddStatus(dev, VIRTIO_STATUS_DRIVER_OK);
81 }
82 
VirtmmioInitFailed(const struct VirtmmioDev * dev)83 void VirtmmioInitFailed(const struct VirtmmioDev *dev)
84 {
85     VirtioAddStatus(dev, VIRTIO_STATUS_FAILED);
86 }
87 
Negotiate(struct VirtmmioDev * baseDev,uint32_t nth,VirtioFeatureFn fn,void * dev)88 static 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 
VirtmmioNegotiate(struct VirtmmioDev * baseDev,VirtioFeatureFn f0,VirtioFeatureFn f1,void * dev)111 bool 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
u32_to_u64(uint32_t addr)133 uint64_t u32_to_u64(uint32_t addr) {
134     uint64_t paddr = (uint64_t)addr & U32_MASK;
135     return paddr;
136 }
WriteQueueAddr(uint64_t addr,const struct VirtmmioDev * dev,uint32_t regLow)137 static 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 
CompleteConfigQueue(uint32_t queue,const struct VirtmmioDev * dev)147 static 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 
CalculateQueueAddr(VADDR_T base,uint16_t qsz,struct Virtq * q)171 static 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 
VirtmmioConfigQueue(struct VirtmmioDev * dev,VADDR_T base,uint16_t qsz[],int num)184 VADDR_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 
VirtmmioRegisterIRQ(struct VirtmmioDev * dev,HWI_PROC_FUNC handle,void * argDev,const char * devName)198 bool 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