master
c 123 lines 3.9 KB
Raw
1 /*
2 * Nitro Enclave Vsock Serial
3 *
4 * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 *
6 * Authors:
7 * Alexander Graf <graf@amazon.com>
8 *
9 * With Nitro Enclaves in debug mode, the Nitro Hypervisor provides a vsock
10 * port that the parent can connect to to receive serial console output of
11 * the Enclave. This driver implements short-circuit logic to establish the
12 * vsock connection to that port and feed its data into a chardev, so that
13 * a machine model can use it as serial device.
14 *
15 * SPDX-License-Identifier: GPL-2.0-or-later
16 */
17
18 #include "qemu/osdep.h"
19 #include "qemu/error-report.h"
20 #include "qapi/error.h"
21 #include "chardev/char.h"
22 #include "chardev/char-fe.h"
23 #include "hw/core/qdev-properties.h"
24 #include "hw/core/qdev-properties-system.h"
25 #include "hw/nitro/serial-vsock.h"
26 #include "trace.h"
27
28 #define CONSOLE_PORT_START 10000
29 #define VMADDR_CID_HYPERVISOR_STR "0"
30
31 static int nitro_serial_vsock_can_read(void *opaque)
32 {
33 NitroSerialVsockState *s = opaque;
34
35 /* Refuse vsock input until the output backend is ready */
36 return qemu_chr_fe_backend_open(&s->output) ? 4096 : 0;
37 }
38
39 static void nitro_serial_vsock_read(void *opaque, const uint8_t *buf, int size)
40 {
41 NitroSerialVsockState *s = opaque;
42
43 /* Forward all vsock data to the output chardev */
44 qemu_chr_fe_write_all(&s->output, buf, size);
45 }
46
47 static void nitro_serial_vsock_event(void *opaque, QEMUChrEvent event)
48 {
49 /* No need to action on connect/disconnect events, but trace for debug */
50 trace_nitro_serial_vsock_event(event);
51 }
52
53 static void nitro_serial_vsock_enclave_started(NitroVsockDevice *dev,
54 uint32_t enclave_cid,
55 Error **errp)
56 {
57 NitroSerialVsockState *s = NITRO_SERIAL_VSOCK(dev);
58 uint32_t port = enclave_cid + CONSOLE_PORT_START;
59 g_autofree char *chardev_id = NULL;
60 Chardev *chr;
61 ChardevBackend *backend;
62 ChardevSocket *sock;
63
64 /*
65 * We know the Enclave CID to connect to now. Create a vsock
66 * client chardev that connects to the Enclave's console.
67 */
68 chardev_id = g_strdup_printf("nitro-console-%u", enclave_cid);
69
70 backend = g_new0(ChardevBackend, 1);
71 backend->type = CHARDEV_BACKEND_KIND_SOCKET;
72 sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
73 sock->addr = g_new0(SocketAddressLegacy, 1);
74 sock->addr->type = SOCKET_ADDRESS_TYPE_VSOCK;
75 sock->addr->u.vsock.data = g_new0(VsockSocketAddress, 1);
76 sock->addr->u.vsock.data->cid = g_strdup(VMADDR_CID_HYPERVISOR_STR);
77 sock->addr->u.vsock.data->port = g_strdup_printf("%u", port);
78 sock->server = false;
79 sock->has_server = true;
80
81 chr = qemu_chardev_new(chardev_id, TYPE_CHARDEV_SOCKET,
82 backend, NULL, errp);
83 if (!chr) {
84 return;
85 }
86
87 if (!qemu_chr_fe_init(&s->vsock, chr, errp)) {
88 return;
89 }
90
91 qemu_chr_fe_set_handlers(&s->vsock,
92 nitro_serial_vsock_can_read,
93 nitro_serial_vsock_read,
94 nitro_serial_vsock_event,
95 NULL, s, NULL, true);
96 }
97
98 static const Property nitro_serial_vsock_props[] = {
99 DEFINE_PROP_CHR("chardev", NitroSerialVsockState, output),
100 };
101
102 static void nitro_serial_vsock_class_init(ObjectClass *oc, const void *data)
103 {
104 DeviceClass *dc = DEVICE_CLASS(oc);
105 NitroVsockDeviceClass *ndc = NITRO_VSOCK_DEVICE_CLASS(oc);
106
107 device_class_set_props(dc, nitro_serial_vsock_props);
108 ndc->enclave_started = nitro_serial_vsock_enclave_started;
109 }
110
111 static const TypeInfo nitro_serial_vsock_info = {
112 .name = TYPE_NITRO_SERIAL_VSOCK,
113 .parent = TYPE_NITRO_VSOCK_DEVICE,
114 .instance_size = sizeof(NitroSerialVsockState),
115 .class_init = nitro_serial_vsock_class_init,
116 };
117
118 static void nitro_serial_vsock_register(void)
119 {
120 type_register_static(&nitro_serial_vsock_info);
121 }
122
123 type_init(nitro_serial_vsock_register);