xref: /device/qemu/drivers/virtio/virtmmio.c (revision d6aed566)
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, &param);
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