Skip to content

Commit 1e0ad41

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

File tree

4 files changed

+189
-75
lines changed

4 files changed

+189
-75
lines changed

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
@@ -14,81 +14,15 @@
1414
use crate::chunk_ctx::ChunkError;
1515
use crate::chunk_ctx::LargeResponse;
1616
use crate::codec::{Codec, CommonCodec, MessageBuf};
17+
use crate::commands::chunk::{
18+
compute_chunk_size, max_chunked_resp_size, ChunkGet, ChunkResponseFixed, ChunkSenderAttr,
19+
LargeResponseSize,
20+
};
1721
use crate::commands::error_rsp::ErrorCode;
1822
use crate::context::SpdmContext;
1923
use crate::error::{CommandError, CommandResult};
2024
use crate::protocol::*;
2125
use crate::state::ConnectionState;
22-
use bitfield::bitfield;
23-
use core::mem::size_of;
24-
use zerocopy::{FromBytes, Immutable, IntoBytes};
25-
26-
const MAX_NUM_CHUNKS: u16 = u16::MAX;
27-
28-
#[derive(FromBytes, IntoBytes, Immutable)]
29-
#[repr(C)]
30-
struct ChunkGetReq {
31-
param1: u8,
32-
handle: u8,
33-
chunk_seq_num: u16,
34-
}
35-
impl CommonCodec for ChunkGetReq {}
36-
37-
#[derive(FromBytes, IntoBytes, Immutable)]
38-
#[repr(C, packed)]
39-
struct ChunkResponseFixed {
40-
chunk_sender_attr: ChunkSenderAttr,
41-
handle: u8,
42-
chunk_seq_num: u16,
43-
reserved: u16,
44-
chunk_size: u32,
45-
}
46-
impl CommonCodec for ChunkResponseFixed {}
47-
48-
bitfield! {
49-
#[derive(FromBytes, IntoBytes, Immutable)]
50-
#[repr(C)]
51-
struct ChunkSenderAttr(u8);
52-
impl Debug;
53-
u8;
54-
pub last_chunk, set_last_chunk: 0, 0;
55-
reserved, _: 7, 1;
56-
}
57-
58-
#[derive(FromBytes, IntoBytes, Immutable)]
59-
#[repr(C)]
60-
struct LargeResponseSize(u32);
61-
impl CommonCodec for LargeResponseSize {}
62-
63-
pub(crate) fn max_chunked_resp_size(ctx: &SpdmContext) -> usize {
64-
let min_data_transfer_size = ctx.min_data_transfer_size();
65-
let fixed_chunk_resp_size = size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>();
66-
67-
// compute max possible response size that can be transferred in chunks is less than the large response size
68-
(min_data_transfer_size).saturating_sub(fixed_chunk_resp_size) * MAX_NUM_CHUNKS as usize
69-
- size_of::<u32>()
70-
}
71-
72-
// Computes the chunk size based on the context and the chunk sequence number
73-
// Returns the chunk size and a boolean indicating if this is the last chunk
74-
fn compute_chunk_size(ctx: &SpdmContext, chunk_seq_num: u16) -> (usize, bool) {
75-
let extra_field_size = if chunk_seq_num == 0 {
76-
size_of::<LargeResponseSize>()
77-
} else {
78-
0
79-
};
80-
let chunk_size = ctx.min_data_transfer_size().saturating_sub(
81-
size_of::<SpdmMsgHdr>() + size_of::<ChunkResponseFixed>() + extra_field_size,
82-
);
83-
84-
let (is_last_chunk, remaining_len) = ctx.large_resp_context.last_chunk(chunk_size);
85-
86-
if is_last_chunk {
87-
(remaining_len, true)
88-
} else {
89-
(chunk_size, false)
90-
}
91-
}
9226

9327
fn process_chunk_get<'a>(
9428
ctx: &mut SpdmContext<'a>,
@@ -105,13 +39,13 @@ fn process_chunk_get<'a>(
10539
Err(ctx.generate_error_response(req_payload, ErrorCode::UnsupportedRequest, 0, None))?;
10640
}
10741
// Decode the request payload
108-
let chunk_get_req = ChunkGetReq::decode(req_payload).map_err(|_| {
42+
let chunk_get_req = ChunkGet::decode(req_payload).map_err(|_| {
10943
ctx.generate_error_response(req_payload, ErrorCode::InvalidRequest, 0, None)
11044
})?;
11145

11246
if !ctx
11347
.large_resp_context
114-
.valid(chunk_get_req.handle, chunk_get_req.chunk_seq_num)
48+
.valid(chunk_get_req.handle, chunk_get_req.chunk_seq_no)
11549
{
11650
Err(ctx.generate_error_response(req_payload, ErrorCode::InvalidRequest, 0, None))?;
11751
}
@@ -123,7 +57,7 @@ fn process_chunk_get<'a>(
12357
Err(ctx.generate_error_response(req_payload, ErrorCode::ResponseTooLarge, 0, None))?;
12458
}
12559

126-
Ok((chunk_get_req.handle, chunk_get_req.chunk_seq_num))
60+
Ok((chunk_get_req.handle, chunk_get_req.chunk_seq_no))
12761
}
12862

12963
fn encode_chunk_resp_fixed_fields(
@@ -143,7 +77,7 @@ fn encode_chunk_resp_fixed_fields(
14377
let chunk_response_fixed = ChunkResponseFixed {
14478
chunk_sender_attr,
14579
handle,
146-
chunk_seq_num,
80+
chunk_seq_no: chunk_seq_num,
14781
reserved: 0,
14882
chunk_size: chunk_size as u32,
14983
};

src/commands/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub mod algorithms;
1616
pub mod capabilities;
1717
pub mod certificate;
1818
pub mod challenge;
19-
pub mod chunk_get_rsp;
19+
pub mod chunk;
2020
pub mod digests;
2121
pub mod error_rsp;
2222
pub mod measurements_rsp;

0 commit comments

Comments
 (0)