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