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