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