master
c 115 lines 3.16 KB
Raw
1 /*
2 * Nitro Enclave Heartbeat device
3 *
4 * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 *
6 * Authors:
7 * Alexander Graf <graf@amazon.com>
8 *
9 * The Nitro Enclave init process sends a heartbeat byte (0xB7) to
10 * CID 3 (parent) port 9000 on boot to signal it reached initramfs.
11 * The parent must accept the connection, read the byte, and echo it
12 * back. If the enclave init cannot reach the listener, it exits.
13 *
14 * SPDX-License-Identifier: GPL-2.0-or-later
15 */
16
17 #include "qemu/osdep.h"
18 #include "qapi/error.h"
19 #include "chardev/char.h"
20 #include "chardev/char-fe.h"
21 #include "hw/nitro/heartbeat.h"
22 #include "trace.h"
23
24 #define HEARTBEAT_PORT 9000
25 #define VMADDR_CID_ANY_STR "4294967295"
26
27 static int nitro_heartbeat_can_read(void *opaque)
28 {
29 NitroHeartbeatState *s = opaque;
30
31 /* One-shot protocol: stop reading after the first heartbeat */
32 return s->done ? 0 : 1;
33 }
34
35 static void nitro_heartbeat_read(void *opaque, const uint8_t *buf, int size)
36 {
37 NitroHeartbeatState *s = opaque;
38
39 if (s->done || size < 1) {
40 return;
41 }
42
43 /* Echo the heartbeat byte back and disconnect */
44 qemu_chr_fe_write_all(&s->vsock, buf, 1);
45 s->done = true;
46 qemu_chr_fe_deinit(&s->vsock, true);
47
48 trace_nitro_heartbeat_done();
49 }
50
51 static void nitro_heartbeat_event(void *opaque, QEMUChrEvent event)
52 {
53 trace_nitro_heartbeat_event(event);
54 }
55
56 static void nitro_heartbeat_realize(DeviceState *dev, Error **errp)
57 {
58 NitroHeartbeatState *s = NITRO_HEARTBEAT(dev);
59 g_autofree char *chardev_id = NULL;
60 Chardev *chr;
61 ChardevBackend *backend;
62 ChardevSocket *sock;
63
64 chardev_id = g_strdup_printf("nitro-heartbeat");
65
66 backend = g_new0(ChardevBackend, 1);
67 backend->type = CHARDEV_BACKEND_KIND_SOCKET;
68 sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
69 sock->addr = g_new0(SocketAddressLegacy, 1);
70 sock->addr->type = SOCKET_ADDRESS_TYPE_VSOCK;
71 sock->addr->u.vsock.data = g_new0(VsockSocketAddress, 1);
72 sock->addr->u.vsock.data->cid = g_strdup(VMADDR_CID_ANY_STR);
73 sock->addr->u.vsock.data->port = g_strdup_printf("%u", HEARTBEAT_PORT);
74 sock->server = true;
75 sock->has_server = true;
76 sock->wait = false;
77 sock->has_wait = true;
78
79 chr = qemu_chardev_new(chardev_id, TYPE_CHARDEV_SOCKET,
80 backend, NULL, errp);
81 if (!chr) {
82 return;
83 }
84
85 if (!qemu_chr_fe_init(&s->vsock, chr, errp)) {
86 return;
87 }
88
89 qemu_chr_fe_set_handlers(&s->vsock,
90 nitro_heartbeat_can_read,
91 nitro_heartbeat_read,
92 nitro_heartbeat_event,
93 NULL, s, NULL, true);
94 }
95
96 static void nitro_heartbeat_class_init(ObjectClass *oc, const void *data)
97 {
98 DeviceClass *dc = DEVICE_CLASS(oc);
99
100 dc->realize = nitro_heartbeat_realize;
101 }
102
103 static const TypeInfo nitro_heartbeat_info = {
104 .name = TYPE_NITRO_HEARTBEAT,
105 .parent = TYPE_NITRO_VSOCK_DEVICE,
106 .instance_size = sizeof(NitroHeartbeatState),
107 .class_init = nitro_heartbeat_class_init,
108 };
109
110 static void nitro_heartbeat_register(void)
111 {
112 type_register_static(&nitro_heartbeat_info);
113 }
114
115 type_init(nitro_heartbeat_register);