xref: /device/qemu/drivers/virtio/virtrng.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 * Simple virtio-rng driver.
17 * Any time, only one task can use it to get randoms.
18 */
19
20#include "osal.h"
21#include "osal_io.h"
22#include "dmac_core.h"
23#include "los_vm_iomap.h"
24#include "los_random.h"
25#include "virtmmio.h"
26
27#define VIRTQ_REQUEST_QSZ   1
28#define VIRTMMIO_RNG_NAME   "virtrng"
29
30struct Virtrng {
31    struct VirtmmioDev      dev;
32
33    OSAL_DECLARE_MUTEX(mutex);
34    DmacEvent event;
35};
36static struct Virtrng *g_virtRng;
37
38static bool Feature0(uint32_t features, uint32_t *supported, void *dev)
39{
40    (void)features;
41    (void)supported;
42    (void)dev;
43
44    return true;
45}
46
47static bool Feature1(uint32_t features, uint32_t *supported, void *dev)
48{
49    (void)dev;
50    if (features & VIRTIO_F_VERSION_1) {
51        *supported |= VIRTIO_F_VERSION_1;
52    } else {
53        HDF_LOGE("[%s]virtio-rng has no VERSION_1 feature", __func__);
54        return false;
55    }
56
57    return true;
58}
59
60static int VirtrngIO(char *buffer, size_t buflen)
61{
62    struct Virtq *q = &g_virtRng->dev.vq[0];
63    int32_t ret;
64
65    if ((ret = OsalMutexLock(&g_virtRng->mutex)) != HDF_SUCCESS) {
66        HDF_LOGE("[%s]acquire mutex failed: %#x", __func__, ret);
67        return -1;
68    }
69
70    q->desc[0].pAddr = VMM_TO_DMA_ADDR((VADDR_T)buffer);
71    q->desc[0].len = buflen;
72    q->desc[0].flag = VIRTQ_DESC_F_WRITE;
73    q->avail->ring[q->avail->index % q->qsz] = 0;
74    DSB;
75    q->avail->index++;
76    OSAL_WRITEL(0, g_virtRng->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
77
78    if ((ret = DmaEventWait(&g_virtRng->event, 1, HDF_WAIT_FOREVER)) != 1) {
79        HDF_LOGE("[%s]wait event failed: %#x", __func__, ret);
80        ret = -1;
81    } else {
82        ret = q->used->ring[0].len;  /* actual randoms acquired */
83    }
84
85    (void)OsalMutexUnlock(&g_virtRng->mutex);
86    return ret;
87}
88
89static uint32_t VirtrngIRQhandle(uint32_t swIrq, void *dev)
90{
91    (void)swIrq;
92    (void)dev;
93    struct Virtq *q = &g_virtRng->dev.vq[0];
94
95    if (!(OSAL_READL(g_virtRng->dev.base + VIRTMMIO_REG_INTERRUPTSTATUS) & VIRTMMIO_IRQ_NOTIFY_USED)) {
96        return 1;
97    }
98
99    (void)DmaEventSignal(&g_virtRng->event, 1);
100    q->last++;
101
102    OSAL_WRITEL(VIRTMMIO_IRQ_NOTIFY_USED, g_virtRng->dev.base + VIRTMMIO_REG_INTERRUPTACK);
103    return 0;
104}
105
106static void VirtrngDeInit(struct Virtrng *rng)
107{
108    if (rng->dev.irq & ~_IRQ_MASK) {
109        OsalUnregisterIrq(rng->dev.irq & _IRQ_MASK, rng);
110    }
111    if (rng->mutex.realMutex) {
112        OsalMutexDestroy(&rng->mutex);
113    }
114    LOS_DmaMemFree(rng);
115    g_virtRng = NULL;
116}
117
118static int VirtrngInitDevAux(struct Virtrng *rng)
119{
120    int32_t ret;
121
122    if ((ret = OsalMutexInit(&rng->mutex)) != HDF_SUCCESS) {
123        HDF_LOGE("[%s]initialize mutex failed: %d", __func__, ret);
124        return ret;
125    }
126
127    if ((ret = DmaEventInit(&rng->event)) != HDF_SUCCESS) {
128        HDF_LOGE("[%s]initialize event control block failed: %u", __func__, ret);
129        return ret;
130    }
131
132    ret = OsalRegisterIrq(rng->dev.irq, OSAL_IRQF_TRIGGER_NONE,
133                          (OsalIRQHandle)VirtrngIRQhandle, VIRTMMIO_RNG_NAME, rng);
134    if (ret != HDF_SUCCESS) {
135        HDF_LOGE("[%s]register IRQ failed: %d", __func__, ret);
136        return ret;
137    }
138    rng->dev.irq |= ~_IRQ_MASK;
139
140    return HDF_SUCCESS;
141}
142
143static struct Virtrng *VirtrngInitDev(void)
144{
145    struct Virtrng *rng = NULL;
146    VADDR_T base;
147    uint16_t qsz;
148    int32_t len;
149
150    /* NOTE: For simplicity, alloc all these data from physical continuous memory. */
151    len = sizeof(struct Virtrng) + VirtqSize(VIRTQ_REQUEST_QSZ);
152    rng = LOS_DmaMemAlloc(NULL, len, sizeof(UINTPTR), DMA_CACHE);
153    if (rng == NULL) {
154        HDF_LOGE("[%s]alloc rng memory failed", __func__);
155        return NULL;
156    }
157    (void)memset_s(rng, len, 0, len);
158
159    if (!VirtmmioDiscover(VIRTMMIO_DEVICE_ID_RNG, &rng->dev)) {
160        goto ERR_OUT;
161    }
162
163    VirtmmioInitBegin(&rng->dev);
164
165    if (!VirtmmioNegotiate(&rng->dev, Feature0, Feature1, rng)) {
166        goto ERR_OUT1;
167    }
168
169    base = ALIGN((VADDR_T)rng + sizeof(struct Virtrng), VIRTQ_ALIGN_DESC);
170    qsz = VIRTQ_REQUEST_QSZ;
171    if (VirtmmioConfigQueue(&rng->dev, base, &qsz, 1) == 0) {
172        goto ERR_OUT1;
173    }
174
175    if (VirtrngInitDevAux(rng) != HDF_SUCCESS) {
176        goto ERR_OUT1;
177    }
178
179    VritmmioInitEnd(&rng->dev);
180    return rng;
181
182ERR_OUT1:
183    VirtmmioInitFailed(&rng->dev);
184ERR_OUT:
185    VirtrngDeInit(rng);
186    return NULL;
187}
188
189
190/*
191 * random_hw code
192 */
193
194static int VirtrngSupport(void)
195{
196    return 1;
197}
198
199static void VirtrngOpen(void)
200{
201}
202
203static void VirtrngClose(void)
204{
205}
206
207static int VirtrngRead(char *buffer, size_t bytes)
208{
209    char *newbuf = buffer;
210    int len;
211
212    if (LOS_IsUserAddressRange((VADDR_T)buffer, bytes)) {
213        newbuf = OsalMemAlloc(bytes);
214        if (newbuf == NULL) {
215            HDF_LOGE("[%s]alloc memory failed", __func__);
216            return -1;
217        }
218    } else if ((VADDR_T)buffer + bytes < (VADDR_T)buffer) {
219        HDF_LOGE("[%s]invalid argument: buffer=%p, size=%#x\n", __func__, buffer, bytes);
220        return -1;
221    }
222
223    len = VirtrngIO(newbuf, bytes);
224
225    if (newbuf != buffer) {
226        if ((len > 0) && (LOS_ArchCopyToUser(buffer, newbuf, len)) != 0) {
227            HDF_LOGE("[%s]LOS_ArchCopyToUser error\n", __func__);
228            len = -1;
229        }
230
231        (void)OsalMemFree(newbuf);
232    }
233
234    return len;
235}
236
237void VirtrngInit(void)
238{
239    if ((g_virtRng = VirtrngInitDev()) == NULL) {
240        return;
241    }
242
243    int ret;
244    RandomOperations r = {
245        .support = VirtrngSupport,
246        .init = VirtrngOpen,
247        .deinit = VirtrngClose,
248        .read = VirtrngRead,
249    };
250    RandomOperationsInit(&r);
251    if ((ret = DevUrandomRegister()) != 0) {
252        HDF_LOGE("[%s]register /dev/urandom failed: %#x", __func__, ret);
253        VirtrngDeInit(g_virtRng);
254    }
255}
256
257
258/*
259 * When kernel decoupled with specific devices,
260 * these code can be removed.
261 */
262void HiRandomHwInit(void) {}
263void HiRandomHwDeinit(void) {}
264int HiRandomHwGetInteger(unsigned *result)
265{
266    /* kernel call this too early in mount.c */
267    if (g_virtRng == NULL) {
268        *result = 1;
269        return sizeof(unsigned);
270    }
271
272    return VirtrngRead((char*)result, sizeof(unsigned));
273}
274int HiRandomHwGetNumber(char *buffer, size_t buflen)
275{
276    return VirtrngRead(buffer, buflen);
277}
278