xref: /openbmc/qemu/backends/spdm-socket.c (revision c494afbb7d552604ad26036127655c534a2645e5)
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/qdev-properties.h"
17 #include "hw/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(errp, "cannot create socket: %s", strerror(errno));
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(errp, "cannot connect: %s", strerror(errno));
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 };
272