1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 /*
9 * The interface of virtio_coreu is obsoleted.
10 * We reserve these code for future.
11 */
12
13 /*
14 * CoreU Virtualization
15 *
16 * CoreU is for PAVP session management. CoreU contains two parts, the daemon
17 * and library used by libpavp. When playback premium content which should be
18 * protected in GPU memory, media application calls libpavp which uses MEI to
19 * create PAVP session. CoreU daemon has the capability to read GPU registers
20 * to know whether the PAVP session status is valid. For now, CoreU is not open
21 * source.
22 *
23 * +---------------+
24 * | |
25 * +----------+----------+ | +------------------+
26 * | ACRN DM | | | Media APP |
27 * | +-----------------+ | | +------------------+
28 * | | CoreU Backend | | | |
29 * | +-----------------+ | | +------------------+
30 * +---------------------+ | | LibPAVP |
31 * | | +------------------+
32 * | | |
33 * +------------------+ | +------------------+
34 * | CoreU Service VM | | | CoreU User VM |
35 * | Daemon | | | Daemon |
36 * +------------------+ | +------------------+
37 * |
38 * Service VM User Space | User VM User Space
39 * |
40 * -------------------------- | ---------------------------
41 * |
42 * Service VM Kernel Space | User VM Kernel Space
43 * |
44 * | +------------------+
45 * | | CoreU Frontend |
46 * | +------------------+
47 * | |
48 * +-------------+
49 *
50 * Above diagram illustrates the CoreU architecture in ACRN. In Service VM, CoreU
51 * daemon starts upon the system boots. In User VM, CoreU daemon gets the PAVP
52 * session status by open/read/write /dev/coreu0 which is created by CoreU
53 * frontend, instead of accessing GPU. Then the CoreU frontend sends the
54 * requests to the CoreU backend thru virtio mechanism. CoreU backend talks to
55 * CoreU Service VM daemon to get the PAVP session status.
56 *
57 */
58
59 #include <sys/cdefs.h>
60 #include <sys/param.h>
61 #include <sys/uio.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include <pthread.h>
70 #include <sysexits.h>
71 #include <dlfcn.h>
72 #include <sys/socket.h>
73 #include <sys/un.h>
74
75 #include "dm.h"
76 #include "pci_core.h"
77 #include "virtio.h"
78
79 #define VIRTIO_COREU_RINGSZ 64
80 #define COREU_MSG_SIZE 72
81 #define VIRTIO_COREU_NUMQ 1
82 #define COREU_SERVICE_NAME "/var/run/coreu"
83
84 /* CoreU raw message */
85 struct coreu_msg {
86 uint8_t bytes[COREU_MSG_SIZE];
87 };
88
89 /* Per-device struct - VBS-U */
90 struct virtio_coreu {
91 struct virtio_base base;
92 struct virtio_vq_info queues[VIRTIO_COREU_NUMQ];
93 pthread_mutex_t mtx;
94
95 pthread_t rx_tid;
96 pthread_mutex_t rx_mtx;
97 pthread_cond_t rx_cond;
98
99 /* socket handle to CoreU daemon */
100 int fd;
101 };
102
103 /* VBS-U virtio_ops */
104 static void virtio_coreu_reset(void *);
105
106 static struct virtio_ops virtio_coreu_ops = {
107 "virtio_coreu", /* our name */
108 VIRTIO_COREU_NUMQ, /* we support one virtqueue */
109 0, /* config reg size */
110 virtio_coreu_reset, /* reset */
111 NULL, /* device-wide qnotify */
112 NULL, /* read virtio config */
113 NULL, /* write virtio config */
114 NULL, /* apply negotiated features */
115 NULL, /* called on guest set status */
116 };
117
118 /* Debug printf */
119 static int virtio_coreu_debug;
120 #define DPRINTF(params) do { if (virtio_coreu_debug) pr_dbg params; } while (0)
121 #define WPRINTF(params) (pr_err params)
122
123 static void
virtio_coreu_reset(void * vdev)124 virtio_coreu_reset(void *vdev)
125 {
126 struct virtio_coreu *vcoreu = vdev;
127
128 DPRINTF(("virtio_coreu: device reset requested\n"));
129 virtio_reset_dev(&vcoreu->base);
130 }
131
132 static int
send_and_receive(int fd,struct coreu_msg * msg)133 send_and_receive(int fd, struct coreu_msg *msg)
134 {
135 uint32_t msg_size = sizeof(struct coreu_msg);
136 int ret;
137
138 ret = send(fd, (void *)msg, msg_size, 0);
139 if (ret < 0) {
140 WPRINTF(("send error\n"));
141 return ret;
142 }
143
144 ret = recv(fd, (void *)msg, msg_size, 0);
145 if (ret < 0) {
146 WPRINTF(("recv error\n"));
147 return ret;
148 }
149
150 if (ret < msg_size) {
151 WPRINTF(("received part of the data, %d instead of %d\n",
152 ret,
153 msg_size));
154 return ret;
155 }
156
157 return 0;
158 }
159
160 static void *
virtio_coreu_thread(void * param)161 virtio_coreu_thread(void *param)
162 {
163 struct virtio_coreu *vcoreu = param;
164 struct virtio_vq_info *rvq = &vcoreu->queues[0];
165 struct iovec iov;
166 uint16_t idx;
167 int ret;
168 struct coreu_msg *msg;
169
170 for (;;) {
171 ret = 0;
172 pthread_mutex_lock(&vcoreu->rx_mtx);
173
174 /*
175 * Checking the avail ring here serves two purposes:
176 * - avoid vring processing due to spurious wakeups
177 * - catch missing notifications before acquiring rx_mtx
178 */
179 while (!ret && !vq_has_descs(rvq))
180 ret = pthread_cond_wait(&vcoreu->rx_cond, &vcoreu->rx_mtx);
181
182 pthread_mutex_unlock(&vcoreu->rx_mtx);
183
184 if (ret)
185 break;
186
187 do {
188 ret = vq_getchain(rvq, &idx, &iov, 1, NULL);
189 if (ret < 1) {
190 pr_err("%s: fail to getchain!\n", __func__);
191 return NULL;
192 }
193 if (ret != 1) {
194 pr_warn("%s: invalid chain!\n", __func__);
195 vq_relchain(rvq, idx, 0);
196 continue;
197 }
198
199 msg = (struct coreu_msg *)(iov.iov_base);
200
201 ret = send_and_receive(vcoreu->fd, msg);
202 if (ret < 0) {
203 close(vcoreu->fd);
204 vcoreu->fd = -1;
205 }
206
207 /* release this chain and handle more */
208 vq_relchain(rvq, idx, sizeof(struct coreu_msg));
209 } while (vq_has_descs(rvq));
210
211 /* at least one avail ring element has been processed */
212 vq_endchains(rvq, 1);
213 }
214
215 pthread_exit(NULL);
216 }
217
218 static int
connect_coreu_daemon()219 connect_coreu_daemon()
220 {
221 struct sockaddr_un addr;
222 int msg_size;
223 int fd;
224 int ret;
225
226 fd = socket(AF_UNIX, SOCK_STREAM, 0);
227 if (fd < 0) {
228 WPRINTF(("socket error %d\n", errno));
229 return -1;
230 }
231
232 memset(&addr, 0, sizeof(addr));
233 addr.sun_family = AF_UNIX;
234 strncpy(addr.sun_path, COREU_SERVICE_NAME, sizeof(addr.sun_path));
235
236 ret = connect(fd, &addr, sizeof(struct sockaddr_un));
237 if (ret < 0) {
238 WPRINTF(("connect error %d\n", errno));
239 close(fd);
240 return -1;
241 }
242
243 msg_size = sizeof(struct coreu_msg);
244 ret = setsockopt(fd, SOL_SOCKET,
245 SO_RCVLOWAT, &msg_size, sizeof(msg_size));
246 if (ret < 0) {
247 WPRINTF(("setsockopt error\n"));
248 close(fd);
249 return -1;
250 }
251 return fd;
252 }
253
254 static void
virtio_coreu_notify(void * vdev,struct virtio_vq_info * vq)255 virtio_coreu_notify(void *vdev, struct virtio_vq_info *vq)
256 {
257 struct virtio_coreu *vcoreu = vdev;
258
259 /* Any ring entries to process */
260 if (!vq_has_descs(vq))
261 return;
262
263 vcoreu->fd = (vcoreu->fd < 0) ? connect_coreu_daemon() : vcoreu->fd;
264 if (vcoreu->fd < 0)
265 {
266 WPRINTF(("Invalid CoreU daemon file descriptor\n"));
267 return;
268 }
269
270 /* Signal the thread for processing */
271 pthread_mutex_lock(&vcoreu->rx_mtx);
272 pthread_cond_signal(&vcoreu->rx_cond);
273 pthread_mutex_unlock(&vcoreu->rx_mtx);
274 }
275
276 static int
virtio_coreu_init(struct vmctx * ctx,struct pci_vdev * dev,char * opts)277 virtio_coreu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
278 {
279 struct virtio_coreu *vcoreu;
280 pthread_mutexattr_t attr;
281 char tname[MAXCOMLEN + 1];
282 int rc;
283
284 vcoreu = calloc(1, sizeof(struct virtio_coreu));
285 if (!vcoreu) {
286 WPRINTF(("vcoreu init: fail to alloc virtio_coreu\n"));
287 return -1;
288 }
289
290 /* init mutex attribute properly */
291 int mutexattr_type = virtio_uses_msix()
292 ? PTHREAD_MUTEX_DEFAULT
293 : PTHREAD_MUTEX_RECURSIVE;
294
295 rc = pthread_mutexattr_init(&attr);
296 if (rc)
297 WPRINTF(("vcoreu init: mutexattr init fail, erro %d\n", rc));
298 rc = pthread_mutexattr_settype(&attr, mutexattr_type);
299 if (rc)
300 WPRINTF(("vcoreu init: mutexattr_settype fail, erro %d\n", rc));
301 rc = pthread_mutex_init(&vcoreu->mtx, &attr);
302 if (rc)
303 WPRINTF(("vcoreu init: mutexattr_settype fail, erro %d\n", rc));
304
305 DPRINTF(("vcoreu init: using VBS-U...\n"));
306 virtio_linkup(&vcoreu->base, &virtio_coreu_ops,
307 vcoreu, dev, vcoreu->queues, BACKEND_VBSU);
308 vcoreu->base.mtx = &vcoreu->mtx;
309
310 vcoreu->queues[0].qsize = VIRTIO_COREU_RINGSZ;
311 vcoreu->queues[0].notify = virtio_coreu_notify;
312
313 /* initialize config space */
314 pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_COREU);
315 pci_set_cfgdata16(dev, PCIR_VENDOR, INTEL_VENDOR_ID);
316 pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_CRYPTO);
317 pci_set_cfgdata8(dev, PCIR_SUBCLASS, PCIS_SIMPLECOMM_OTHER);
318 pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_COREU);
319 pci_set_cfgdata16(dev, PCIR_SUBVEND_0, INTEL_VENDOR_ID);
320
321 if (virtio_interrupt_init(&vcoreu->base, virtio_uses_msix())) {
322 WPRINTF(("vcoreu init: interrupt init fail\n"));
323 free(vcoreu);
324 return -1;
325 }
326
327 virtio_set_io_bar(&vcoreu->base, 0);
328
329 /*
330 * connect to coreu daemon in init phase
331 *
332 * @FIXME if failed connecting to CoreU daemon, the return value should
333 * be set appropriately for Service VM not exposing the CoreU PCI device to User VM
334 */
335 vcoreu->fd = connect_coreu_daemon();
336 if (vcoreu->fd < 0) {
337 WPRINTF(("connection to server failed\n"));
338 pthread_mutex_destroy(&vcoreu->mtx);
339 free(vcoreu);
340 return -errno;
341 }
342
343 pthread_mutex_init(&vcoreu->rx_mtx, NULL);
344 pthread_cond_init(&vcoreu->rx_cond, NULL);
345 pthread_create(&vcoreu->rx_tid, NULL,
346 virtio_coreu_thread, (void *)vcoreu);
347 snprintf(tname, sizeof(tname), "vtcoreu-%d:%d tx",
348 dev->slot, dev->func);
349 pthread_setname_np(vcoreu->rx_tid, tname);
350
351 return 0;
352 }
353
354 static void
virtio_coreu_deinit(struct vmctx * ctx,struct pci_vdev * dev,char * opts)355 virtio_coreu_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
356 {
357 struct virtio_coreu *vcoreu = (struct virtio_coreu *)dev->arg;
358
359 if (!vcoreu)
360 return;
361
362 pthread_mutex_destroy(&vcoreu->mtx);
363 pthread_mutex_destroy(&vcoreu->rx_mtx);
364 pthread_cond_destroy(&vcoreu->rx_cond);
365 pthread_join(vcoreu->rx_tid, NULL);
366
367 virtio_coreu_reset(vcoreu);
368 free(vcoreu);
369 }
370
371 struct pci_vdev_ops pci_ops_virtio_coreu = {
372 .class_name = "virtio-coreu",
373 .vdev_init = virtio_coreu_init,
374 .vdev_deinit = virtio_coreu_deinit,
375 .vdev_barwrite = virtio_pci_write,
376 .vdev_barread = virtio_pci_read
377 };
378
379 DEFINE_PCI_DEVTYPE(pci_ops_virtio_coreu);
380