master
c 271 lines 7.23 KB
Raw
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /*
3 * QEMU SPDM socket support
4 *
5 * This is based on:
6 * https://github.com/DMTF/spdm-emu/blob/07c0a838bcc1c6207c656ac75885c0603e344b6f/spdm_emu/spdm_emu_common/command.c
7 * but has been re-written to match QEMU style
8 *
9 * Copyright (c) 2021, DMTF. All rights reserved.
10 * Copyright (c) 2023. Western Digital Corporation or its affiliates.
11 */
12
13 #include "qemu/osdep.h"
14 #include "system/spdm-socket.h"
15 #include "qapi/error.h"
16 #include "hw/core/qdev-properties.h"
17 #include "hw/core/qdev-properties-system.h"
18 #include "hw/core/qdev-prop-internal.h"
19
20 static bool read_bytes(const int socket, uint8_t *buffer,
21 size_t number_of_bytes)
22 {
23 ssize_t number_received = 0;
24 ssize_t result;
25
26 while (number_received < number_of_bytes) {
27 result = recv(socket, buffer + number_received,
28 number_of_bytes - number_received, 0);
29 if (result <= 0) {
30 return false;
31 }
32 number_received += result;
33 }
34 return true;
35 }
36
37 static bool read_data32(const int socket, uint32_t *data)
38 {
39 bool result;
40
41 result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t));
42 if (!result) {
43 return result;
44 }
45 *data = ntohl(*data);
46 return true;
47 }
48
49 static bool read_multiple_bytes(const int socket, uint8_t *buffer,
50 uint32_t *bytes_received,
51 uint32_t max_buffer_length)
52 {
53 uint32_t length;
54 bool result;
55
56 result = read_data32(socket, &length);
57 if (!result) {
58 return result;
59 }
60
61 if (length > max_buffer_length) {
62 return false;
63 }
64
65 if (bytes_received) {
66 *bytes_received = length;
67 }
68
69 if (length == 0) {
70 return true;
71 }
72
73 return read_bytes(socket, buffer, length);
74 }
75
76 static bool receive_platform_data(const int socket,
77 uint32_t transport_type,
78 uint32_t *command,
79 uint8_t *receive_buffer,
80 uint32_t *bytes_to_receive)
81 {
82 bool result;
83 uint32_t response;
84 uint32_t bytes_received;
85
86 result = read_data32(socket, &response);
87 if (!result) {
88 return result;
89 }
90 *command = response;
91
92 result = read_data32(socket, &transport_type);
93 if (!result) {
94 return result;
95 }
96
97 bytes_received = 0;
98 result = read_multiple_bytes(socket, receive_buffer, &bytes_received,
99 *bytes_to_receive);
100 if (!result) {
101 return result;
102 }
103 *bytes_to_receive = bytes_received;
104
105 return result;
106 }
107
108 static bool write_bytes(const int socket, const uint8_t *buffer,
109 uint32_t number_of_bytes)
110 {
111 ssize_t number_sent = 0;
112 ssize_t result;
113
114 while (number_sent < number_of_bytes) {
115 result = send(socket, buffer + number_sent,
116 number_of_bytes - number_sent, 0);
117 if (result == -1) {
118 return false;
119 }
120 number_sent += result;
121 }
122 return true;
123 }
124
125 static bool write_data32(const int socket, uint32_t data)
126 {
127 data = htonl(data);
128 return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t));
129 }
130
131 static bool write_multiple_bytes(const int socket, const uint8_t *buffer,
132 uint32_t bytes_to_send)
133 {
134 bool result;
135
136 result = write_data32(socket, bytes_to_send);
137 if (!result) {
138 return result;
139 }
140
141 return write_bytes(socket, buffer, bytes_to_send);
142 }
143
144 static bool send_platform_data(const int socket,
145 uint32_t transport_type, uint32_t command,
146 const uint8_t *send_buffer, size_t bytes_to_send)
147 {
148 bool result;
149
150 result = write_data32(socket, command);
151 if (!result) {
152 return result;
153 }
154
155 result = write_data32(socket, transport_type);
156 if (!result) {
157 return result;
158 }
159
160 return write_multiple_bytes(socket, send_buffer, bytes_to_send);
161 }
162
163 int spdm_socket_connect(uint16_t port, Error **errp)
164 {
165 int client_socket;
166 struct sockaddr_in server_addr;
167
168 client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
169 if (client_socket < 0) {
170 error_setg_errno(errp, errno, "cannot create socket");
171 return -1;
172 }
173
174 memset((char *)&server_addr, 0, sizeof(server_addr));
175 server_addr.sin_family = AF_INET;
176 server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
177 server_addr.sin_port = htons(port);
178
179
180 if (connect(client_socket, (struct sockaddr *)&server_addr,
181 sizeof(server_addr)) < 0) {
182 error_setg_errno(errp, errno, "cannot connect");
183 close(client_socket);
184 return -1;
185 }
186
187 return client_socket;
188 }
189
190 static bool spdm_socket_command_valid(uint32_t command)
191 {
192 switch (command) {
193 case SPDM_SOCKET_COMMAND_NORMAL:
194 case SPDM_SOCKET_STORAGE_CMD_IF_SEND:
195 case SPDM_SOCKET_STORAGE_CMD_IF_RECV:
196 case SOCKET_SPDM_STORAGE_ACK_STATUS:
197 case SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE:
198 case SPDM_SOCKET_COMMAND_CONTINUE:
199 case SPDM_SOCKET_COMMAND_SHUTDOWN:
200 case SPDM_SOCKET_COMMAND_UNKOWN:
201 case SPDM_SOCKET_COMMAND_TEST:
202 return true;
203 default:
204 return false;
205 }
206 }
207
208 uint32_t spdm_socket_receive(const int socket, uint32_t transport_type,
209 void *rsp, uint32_t rsp_len)
210 {
211 uint32_t command;
212 bool result;
213
214 result = receive_platform_data(socket, transport_type, &command,
215 (uint8_t *)rsp, &rsp_len);
216
217 /* we may have received some data, but check if the command is valid */
218 if (!result || !spdm_socket_command_valid(command)) {
219 return 0;
220 }
221
222 return rsp_len;
223 }
224
225 bool spdm_socket_send(const int socket, uint32_t socket_cmd,
226 uint32_t transport_type, void *req, uint32_t req_len)
227 {
228 return send_platform_data(socket, transport_type, socket_cmd, req,
229 req_len);
230 }
231
232 uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
233 void *req, uint32_t req_len,
234 void *rsp, uint32_t rsp_len)
235 {
236 bool result;
237
238 result = spdm_socket_send(socket, SPDM_SOCKET_COMMAND_NORMAL,
239 transport_type, req, req_len);
240 if (!result) {
241 return 0;
242 }
243
244 return spdm_socket_receive(socket, transport_type, rsp, rsp_len);
245 }
246
247 void spdm_socket_close(const int socket, uint32_t transport_type)
248 {
249 send_platform_data(socket, transport_type,
250 SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
251 }
252
253 const QEnumLookup SpdmTransport_lookup = {
254 .array = (const char *const[]) {
255 [SPDM_SOCKET_TRANSPORT_TYPE_UNSPEC] = "unspecified",
256 [SPDM_SOCKET_TRANSPORT_TYPE_MCTP] = "mctp",
257 [SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE] = "doe",
258 [SPDM_SOCKET_TRANSPORT_TYPE_SCSI] = "scsi",
259 [SPDM_SOCKET_TRANSPORT_TYPE_NVME] = "nvme",
260 },
261 .size = SPDM_SOCKET_TRANSPORT_TYPE_MAX
262 };
263
264 const PropertyInfo qdev_prop_spdm_trans = {
265 .type = "SpdmTransportType",
266 .description = "Spdm Transport, doe/nvme/mctp/scsi/unspecified",
267 .enum_table = &SpdmTransport_lookup,
268 .get = qdev_propinfo_get_enum,
269 .set = qdev_propinfo_set_enum,
270 .set_default_value = qdev_propinfo_set_default_value_enum,
271 };