Skip to content

Commit bc419a1

Browse files
hh81478072mstsirkin
authored andcommitted
backends: Initial support for SPDM socket support
SPDM enables authentication, attestation and key exchange to assist in providing infrastructure security enablement. It's a standard published by the DMTF [1]. SPDM supports multiple transports, including PCIe DOE and MCTP. This patch adds support to QEMU to connect to an external SPDM instance. SPDM support can be added to any QEMU device by exposing a TCP socket to a SPDM server. The server can then implement the SPDM decoding/encoding support, generally using libspdm [2]. This is similar to how the current TPM implementation works and means that the heavy lifting of setting up certificate chains, capabilities, measurements and complex crypto can be done outside QEMU by a well supported and tested library. 1: https://www.dmtf.org/standards/SPDM 2: https://github.com/DMTF/libspdm Signed-off-by: Huai-Cheng Kuo <[email protected]> Signed-off-by: Chris Browy <[email protected]> Co-developed-by: Jonathan Cameron <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]> [ Changes by WM - Bug fixes from testing ] Signed-off-by: Wilfred Mallawa <[email protected]> [ Changes by AF: - Convert to be more QEMU-ified - Move to backends as it isn't PCIe specific ] Signed-off-by: Alistair Francis <[email protected]> Message-Id: <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent 78cc8c6 commit bc419a1

File tree

5 files changed

+302
-0
lines changed

5 files changed

+302
-0
lines changed

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,12 @@ F: tests/qtest/*tpm*
34013401
F: docs/specs/tpm.rst
34023402
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
34033403

3404+
SPDM
3405+
M: Alistair Francis <[email protected]>
3406+
S: Maintained
3407+
F: backends/spdm-socket.c
3408+
F: include/sysemu/spdm-socket.h
3409+
34043410
Checkpatch
34053411
S: Odd Fixes
34063412
F: scripts/checkpatch.pl

backends/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ source tpm/Kconfig
33
config IOMMUFD
44
bool
55
depends on VFIO
6+
7+
config SPDM_SOCKET
8+
bool
9+
default y

backends/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ endif
3333
system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
3434
system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
3535

36+
system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c'))
37+
3638
subdir('tpm')

backends/spdm-socket.c

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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 "sysemu/spdm-socket.h"
15+
#include "qapi/error.h"
16+
17+
static bool read_bytes(const int socket, uint8_t *buffer,
18+
size_t number_of_bytes)
19+
{
20+
ssize_t number_received = 0;
21+
ssize_t result;
22+
23+
while (number_received < number_of_bytes) {
24+
result = recv(socket, buffer + number_received,
25+
number_of_bytes - number_received, 0);
26+
if (result <= 0) {
27+
return false;
28+
}
29+
number_received += result;
30+
}
31+
return true;
32+
}
33+
34+
static bool read_data32(const int socket, uint32_t *data)
35+
{
36+
bool result;
37+
38+
result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t));
39+
if (!result) {
40+
return result;
41+
}
42+
*data = ntohl(*data);
43+
return true;
44+
}
45+
46+
static bool read_multiple_bytes(const int socket, uint8_t *buffer,
47+
uint32_t *bytes_received,
48+
uint32_t max_buffer_length)
49+
{
50+
uint32_t length;
51+
bool result;
52+
53+
result = read_data32(socket, &length);
54+
if (!result) {
55+
return result;
56+
}
57+
58+
if (length > max_buffer_length) {
59+
return false;
60+
}
61+
62+
if (bytes_received) {
63+
*bytes_received = length;
64+
}
65+
66+
if (length == 0) {
67+
return true;
68+
}
69+
70+
return read_bytes(socket, buffer, length);
71+
}
72+
73+
static bool receive_platform_data(const int socket,
74+
uint32_t transport_type,
75+
uint32_t *command,
76+
uint8_t *receive_buffer,
77+
uint32_t *bytes_to_receive)
78+
{
79+
bool result;
80+
uint32_t response;
81+
uint32_t bytes_received;
82+
83+
result = read_data32(socket, &response);
84+
if (!result) {
85+
return result;
86+
}
87+
*command = response;
88+
89+
result = read_data32(socket, &transport_type);
90+
if (!result) {
91+
return result;
92+
}
93+
94+
bytes_received = 0;
95+
result = read_multiple_bytes(socket, receive_buffer, &bytes_received,
96+
*bytes_to_receive);
97+
if (!result) {
98+
return result;
99+
}
100+
*bytes_to_receive = bytes_received;
101+
102+
return result;
103+
}
104+
105+
static bool write_bytes(const int socket, const uint8_t *buffer,
106+
uint32_t number_of_bytes)
107+
{
108+
ssize_t number_sent = 0;
109+
ssize_t result;
110+
111+
while (number_sent < number_of_bytes) {
112+
result = send(socket, buffer + number_sent,
113+
number_of_bytes - number_sent, 0);
114+
if (result == -1) {
115+
return false;
116+
}
117+
number_sent += result;
118+
}
119+
return true;
120+
}
121+
122+
static bool write_data32(const int socket, uint32_t data)
123+
{
124+
data = htonl(data);
125+
return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t));
126+
}
127+
128+
static bool write_multiple_bytes(const int socket, const uint8_t *buffer,
129+
uint32_t bytes_to_send)
130+
{
131+
bool result;
132+
133+
result = write_data32(socket, bytes_to_send);
134+
if (!result) {
135+
return result;
136+
}
137+
138+
return write_bytes(socket, buffer, bytes_to_send);
139+
}
140+
141+
static bool send_platform_data(const int socket,
142+
uint32_t transport_type, uint32_t command,
143+
const uint8_t *send_buffer, size_t bytes_to_send)
144+
{
145+
bool result;
146+
147+
result = write_data32(socket, command);
148+
if (!result) {
149+
return result;
150+
}
151+
152+
result = write_data32(socket, transport_type);
153+
if (!result) {
154+
return result;
155+
}
156+
157+
return write_multiple_bytes(socket, send_buffer, bytes_to_send);
158+
}
159+
160+
int spdm_socket_connect(uint16_t port, Error **errp)
161+
{
162+
int client_socket;
163+
struct sockaddr_in server_addr;
164+
165+
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
166+
if (client_socket < 0) {
167+
error_setg(errp, "cannot create socket: %s", strerror(errno));
168+
return -1;
169+
}
170+
171+
memset((char *)&server_addr, 0, sizeof(server_addr));
172+
server_addr.sin_family = AF_INET;
173+
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
174+
server_addr.sin_port = htons(port);
175+
176+
177+
if (connect(client_socket, (struct sockaddr *)&server_addr,
178+
sizeof(server_addr)) < 0) {
179+
error_setg(errp, "cannot connect: %s", strerror(errno));
180+
close(client_socket);
181+
return -1;
182+
}
183+
184+
return client_socket;
185+
}
186+
187+
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
188+
void *req, uint32_t req_len,
189+
void *rsp, uint32_t rsp_len)
190+
{
191+
uint32_t command;
192+
bool result;
193+
194+
result = send_platform_data(socket, transport_type,
195+
SPDM_SOCKET_COMMAND_NORMAL,
196+
req, req_len);
197+
if (!result) {
198+
return 0;
199+
}
200+
201+
result = receive_platform_data(socket, transport_type, &command,
202+
(uint8_t *)rsp, &rsp_len);
203+
if (!result) {
204+
return 0;
205+
}
206+
207+
assert(command != 0);
208+
209+
return rsp_len;
210+
}
211+
212+
void spdm_socket_close(const int socket, uint32_t transport_type)
213+
{
214+
send_platform_data(socket, transport_type,
215+
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
216+
}

include/sysemu/spdm-socket.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* QEMU SPDM socket support
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*/
22+
23+
#ifndef SPDM_REQUESTER_H
24+
#define SPDM_REQUESTER_H
25+
26+
/**
27+
* spdm_socket_connect: connect to an external SPDM socket
28+
* @port: port to connect to
29+
* @errp: error object handle
30+
*
31+
* This will connect to an external SPDM socket server. On error
32+
* it will return -1 and errp will be set. On success this function
33+
* will return the socket number.
34+
*/
35+
int spdm_socket_connect(uint16_t port, Error **errp);
36+
37+
/**
38+
* spdm_socket_rsp: send and receive a message to a SPDM server
39+
* @socket: socket returned from spdm_socket_connect()
40+
* @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
41+
* @req: request buffer
42+
* @req_len: request buffer length
43+
* @rsp: response buffer
44+
* @rsp_len: response buffer length
45+
*
46+
* Send platform data to a SPDM server on socket and then receive
47+
* a response.
48+
*/
49+
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
50+
void *req, uint32_t req_len,
51+
void *rsp, uint32_t rsp_len);
52+
53+
/**
54+
* spdm_socket_close: send a shutdown command to the server
55+
* @socket: socket returned from spdm_socket_connect()
56+
* @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
57+
*
58+
* This will issue a shutdown command to the server.
59+
*/
60+
void spdm_socket_close(const int socket, uint32_t transport_type);
61+
62+
#define SPDM_SOCKET_COMMAND_NORMAL 0x0001
63+
#define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001
64+
#define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD
65+
#define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE
66+
#define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF
67+
#define SPDM_SOCKET_COMMAND_TEST 0xDEAD
68+
69+
#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
70+
#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
71+
72+
#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
73+
74+
#endif

0 commit comments

Comments
 (0)