Skip to content

Commit 6d0218a

Browse files
committed
requester: enable chunking
Signed-off-by: leongross <leon.gross@9elements.com>
1 parent 58fb82b commit 6d0218a

4 files changed

Lines changed: 189 additions & 75 deletions

File tree

src/commands/chunk/mod.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Licensed under the Apache-2.0 license
2+
use crate::codec::CommonCodec;
3+
use crate::context::SpdmContext;
4+
use crate::protocol::*;
5+
use bitfield::bitfield;
6+
use zerocopy::{FromBytes, Immutable, IntoBytes};
7+
8+
/// The maximum number of chunks that can be transferred for a large response.
9+
/// This is determined by the size of the chunk sequence number field (u16) in the
10+
/// `ChunkGetReq` and `ChunkResponseFixed` messages.
11+
pub const MAX_NUM_CHUNKS: u16 = u16::MAX;
12+
13+
pub mod request;
14+
pub mod response;
15+
16+
#[derive(FromBytes, IntoBytes, Immutable)]
17+
#[repr(C)]
18+
pub(crate) struct ChunkGet {
19+
/// Reserved.
20+
param1: u8,
21+
22+
/// Shall contain a handle. This field shall be the same value as given in the
23+
/// `Handle` field of the `ERROR` message of `ErrorCode=LargeResponse`.
24+
///
25+
/// # Note
26+
///
27+
/// The fields name in spec is `Param2`.
28+
handle: u8,
29+
30+
/// Shall indicate the desired chunk sequence number of the Large SPDM Response
31+
/// to retrieve.
32+
chunk_seq_no: u16,
33+
}
34+
impl CommonCodec for ChunkGet {}
35+
36+
#[derive(FromBytes, IntoBytes, Immutable)]
37+
#[repr(C, packed)]
38+
/// The fixed fields of the `CHUNK_RESPONSE` message. The actual response payload
39+
/// follows these fixed fields in the message.
40+
///
41+
/// - `LargeResponseSize`: Field is only present in the first chunk (i.e., when `ChunkSeqNo` is 0).
42+
/// - `SPDMchunk`: The chunk of the large response message. The size of this field is determined by the
43+
struct ChunkResponseFixed {
44+
/// # Note
45+
///
46+
/// The fields name in spec is `Param1`.
47+
chunk_sender_attr: ChunkSenderAttr,
48+
49+
/// # Note
50+
///
51+
/// The fields name in spec is `Param2`.
52+
handle: u8,
53+
54+
chunk_seq_no: u16,
55+
reserved: u16,
56+
chunk_size: u32,
57+
}
58+
impl CommonCodec for ChunkResponseFixed {}
59+
60+
bitfield! {
61+
#[derive(FromBytes, IntoBytes, Immutable)]
62+
#[repr(C)]
63+
struct ChunkSenderAttr(u8);
64+
impl Debug;
65+
u8;
66+
/// If set, the chunk indicated by `ChunkSeqNo` shall represent the last chunk
67+
/// of the large SPDM message.
68+
pub last_chunk, set_last_chunk: 0, 0;
69+
reserved, _: 7, 1;
70+
}
71+
72+
bitfield! {
73+
#[derive(FromBytes, IntoBytes, Immutable)]
74+
#[repr(C)]
75+
struct ChunkReceiverAttr(u8);
76+
impl Debug;
77+
u8;
78+
/// If set, the receiver of a large SPDM request message detected an error in
79+
/// the Request before the last chunk was received. If set, the sender of the
80+
/// large SPDM request message shall terminate the transfer of any remaining chunks.
81+
/// After addressing the issue, the sender of the failed large SPDM request
82+
/// message can transfer the fixed large SPDM request message as a new transfer.
83+
pub early_error_detected, set_early_error_detected: 0, 0;
84+
reserved, _: 7, 1;
85+
}
86+
87+
#[derive(FromBytes, IntoBytes, Immutable)]
88+
#[repr(C)]
89+
struct LargeResponseSize(u32);
90+
impl CommonCodec for LargeResponseSize {}
91+
92+
#[derive(FromBytes, IntoBytes, Immutable)]
93+
#[repr(C)]
94+
/// The fixed size components of the `CHUNK_SEND` request.
95+
/// When sent, this struct is followed by
96+
/// - `LargeMessageSize` field (only present in the first chunk, i.e., when `ChunkSeqNo` is 0)
97+
pub(crate) struct ChunkSendFixed {
98+
param1: ChunkSenderAttr,
99+
100+
/// # Note
101+
///
102+
/// The fields name in spec is `Param2`.
103+
handle: u8,
104+
105+
/// Shall identify the chunk sequence number associated with SPDMchunk .
106+
chunk_seq_no: u32,
107+
108+
chunk_size: u32,
109+
}
110+
111+
impl ChunkSendFixed {
112+
pub fn new(handle: u8, chunk_seq_no: u32, chunk_size: u32, last_chunk: bool) -> Self {
113+
let mut sender_attr = ChunkSenderAttr(0);
114+
sender_attr.set_last_chunk(last_chunk as u8);
115+
Self {
116+
param1: sender_attr,
117+
handle,
118+
chunk_seq_no,
119+
chunk_size,
120+
}
121+
}
122+
}
123+
124+
#[derive(FromBytes, IntoBytes, Immutable)]
125+
#[repr(C)]
126+
/// Size of the large SPDM message being transferred. This field shall only be present when ChunkSeqNo is zero and shall have a non-zero value. Shall be greater than the DataTransferSize of the receiving SPDM endpoint.
127+
struct LargeMessageSize(u32);
128+
impl CommonCodec for LargeMessageSize {}
129+
130+
#[derive(FromBytes, IntoBytes, Immutable)]
131+
#[repr(C)]
132+
/// # Note
133+
/// This struct may be followed by the variable length field `ResponseToLargeRequest`.
134+
/// `ResponseToLargeRequest` shall be present on the last chunk (that is, when LastChunk is set),
135+
/// or when the `EarlyErrorDetected` bit in Param1 is set.
136+
///
137+
/// This field shall contain the response to the large SPDM request message.
138+
/// When the `EarlyErrorDetected` bit in Param1 is set, this field shall contain an ERROR message.
139+
pub(crate) struct ChunkSendAck {
140+
param1: ChunkReceiverAttr,
141+
/// # Note
142+
///
143+
/// The fields name in spec is `Param2`.
144+
handle: u8,
145+
chunk_seq_no: u32,
146+
}
147+
148+
/// Maximal size of the large response that can be transferred in chunks.
149+
/// If a large response exceeds this size, it cannot be transferred in chunks and
150+
/// the requester should not send a CHUNK_GET request for it.
151+
pub(crate) fn max_chunked_resp_size(ctx: &SpdmContext) -> usize {
152+
let min_data_transfer_size = ctx.min_data_transfer_size();
153+
let fixed_chunk_resp_size = size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>();
154+
155+
// compute max possible response size that can be transferred in chunks is less than the large response size
156+
(min_data_transfer_size).saturating_sub(fixed_chunk_resp_size) * MAX_NUM_CHUNKS as usize
157+
- size_of::<u32>()
158+
}
159+
160+
// Computes the chunk size based on the context and the chunk sequence number
161+
// Returns the chunk size and a boolean indicating if this is the last chunk
162+
fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
163+
let extra_field_size = if chunk_seq_num == 0 {
164+
size_of::<LargeResponseSize>()
165+
} else {
166+
0
167+
};
168+
let chunk_size = ctx.min_data_transfer_size().saturating_sub(
169+
size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>() + extra_field_size,
170+
);
171+
172+
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
173+
174+
if is_last_chunk {
175+
(remaining_len, true)
176+
} else {
177+
(chunk_size, false)
178+
}
179+
}

src/commands/chunk/request.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Licensed under the Apache-2.0 license
Lines changed: 8 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,15 @@
22
use crate::chunk_ctx::ChunkError;
33
use crate::chunk_ctx::LargeResponse;
44
use crate::codec::{Codec, CommonCodec, MessageBuf};
5+
use crate::commands::chunk::{
6+
compute_chunk_size, max_chunked_resp_size, ChunkGet, ChunkResponseFixed, ChunkSenderAttr,
7+
LargeResponseSize,
8+
};
59
use crate::commands::error_rsp::ErrorCode;
610
use crate::context::SpdmContext;
711
use crate::error::{CommandError, CommandResult};
812
use crate::protocol::*;
913
use crate::state::ConnectionState;
10-
use bitfield::bitfield;
11-
use core::mem::size_of;
12-
use zerocopy::{FromBytes, Immutable, IntoBytes};
13-
14-
const MAX_NUM_CHUNKS: u16 = u16::MAX;
15-
16-
#[derive(FromBytes, IntoBytes, Immutable)]
17-
#[repr(C)]
18-
struct ChunkGetReq {
19-
param1: u8,
20-
handle: u8,
21-
chunk_seq_num: u16,
22-
}
23-
impl CommonCodec for ChunkGetReq {}
24-
25-
#[derive(FromBytes, IntoBytes, Immutable)]
26-
#[repr(C, packed)]
27-
struct ChunkResponseFixed {
28-
chunk_sender_attr: ChunkSenderAttr,
29-
handle: u8,
30-
chunk_seq_num: u16,
31-
reserved: u16,
32-
chunk_size: u32,
33-
}
34-
impl CommonCodec for ChunkResponseFixed {}
35-
36-
bitfield! {
37-
#[derive(FromBytes, IntoBytes, Immutable)]
38-
#[repr(C)]
39-
struct ChunkSenderAttr(u8);
40-
impl Debug;
41-
u8;
42-
pub last_chunk, set_last_chunk: 0, 0;
43-
reserved, _: 7, 1;
44-
}
45-
46-
#[derive(FromBytes, IntoBytes, Immutable)]
47-
#[repr(C)]
48-
struct LargeResponseSize(u32);
49-
impl CommonCodec for LargeResponseSize {}
50-
51-
pub(crate) fn max_chunked_resp_size(ctx: &SpdmContext) -> usize {
52-
let min_data_transfer_size = ctx.min_data_transfer_size();
53-
let fixed_chunk_resp_size = size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>();
54-
55-
// compute max possible response size that can be transferred in chunks is less than the large response size
56-
(min_data_transfer_size).saturating_sub(fixed_chunk_resp_size) * MAX_NUM_CHUNKS as usize
57-
- size_of::<u32>()
58-
}
59-
60-
// Computes the chunk size based on the context and the chunk sequence number
61-
// Returns the chunk size and a boolean indicating if this is the last chunk
62-
fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
63-
let extra_field_size = if chunk_seq_num == 0 {
64-
size_of::<LargeResponseSize>()
65-
} else {
66-
0
67-
};
68-
let chunk_size = ctx.min_data_transfer_size().saturating_sub(
69-
size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>() + extra_field_size,
70-
);
71-
72-
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
73-
74-
if is_last_chunk {
75-
(remaining_len, true)
76-
} else {
77-
(chunk_size, false)
78-
}
79-
}
8014

8115
fn process_chunk_get<'a>(
8216
ctx: &mut SpdmContext<'a>,
@@ -93,13 +27,13 @@ fn process_chunk_get<'a>(
9327
Err(ctx.generate_error_response(req_payload, ErrorCode::UnsupportedRequest, 0, None))?;
9428
}
9529
// Decode the request payload
96-
let chunk_get_req = ChunkGetReq::decode(req_payload).map_err(|_| {
30+
let chunk_get_req = ChunkGet::decode(req_payload).map_err(|_| {
9731
ctx.generate_error_response(req_payload, ErrorCode::InvalidRequest, 0, None)
9832
})?;
9933

10034
if !ctx
10135
.large_resp_context
102-
.valid(chunk_get_req.handle, chunk_get_req.chunk_seq_num)
36+
.valid(chunk_get_req.handle, chunk_get_req.chunk_seq_no)
10337
{
10438
Err(ctx.generate_error_response(req_payload, ErrorCode::InvalidRequest, 0, None))?;
10539
}
@@ -111,7 +45,7 @@ fn process_chunk_get<'a>(
11145
Err(ctx.generate_error_response(req_payload, ErrorCode::ResponseTooLarge, 0, None))?;
11246
}
11347

114-
Ok((chunk_get_req.handle, chunk_get_req.chunk_seq_num))
48+
Ok((chunk_get_req.handle, chunk_get_req.chunk_seq_no))
11549
}
11650

11751
fn encode_chunk_resp_fixed_fields(
@@ -131,7 +65,7 @@ fn encode_chunk_resp_fixed_fields(
13165
let chunk_response_fixed = ChunkResponseFixed {
13266
chunk_sender_attr,
13367
handle,
134-
chunk_seq_num,
68+
chunk_seq_no: chunk_seq_num,
13569
reserved: 0,
13670
chunk_size: chunk_size as u32,
13771
};

src/commands/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub mod algorithms;
44
pub mod capabilities;
55
pub mod certificate;
66
pub mod challenge;
7-
pub mod chunk_get_rsp;
7+
pub mod chunk;
88
pub mod digests;
99
pub mod error_rsp;
1010
pub mod measurements_rsp;

0 commit comments

Comments
 (0)