Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/cosmo/base.toml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ features = ["stm32h753", "usart6", "baud_rate_3M", "hardware_flow_control", "vla
uses = ["usart6", "dbgmcu"]
interrupts = {"usart6.irq" = "usart-irq"}
priority = 9
max-sizes = {flash = 69120, ram = 65536}
max-sizes = {flash = 69792, ram = 65536}
stacksize = 5400
start = true
task-slots = ["sys", { cpu_seq = "cosmo_seq" }, "hf", "control_plane_agent", "net", "packrat", "i2c_driver", { spi_driver = "spi2_driver" }, "sprot", "auxflash"]
Expand Down
2 changes: 1 addition & 1 deletion app/gimlet/base.toml
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ features = ["stm32h753", "uart7", "baud_rate_3M", "hardware_flow_control", "vlan
uses = ["uart7", "dbgmcu"]
interrupts = {"uart7.irq" = "usart-irq"}
priority = 8
max-sizes = {flash = 67680, ram = 65536}
max-sizes = {flash = 68608, ram = 65536}
stacksize = 5376
start = true
task-slots = ["sys", { cpu_seq = "gimlet_seq" }, "hf", "control_plane_agent", "net", "packrat", "i2c_driver", { spi_driver = "spi2_driver" }, "sprot"]
Expand Down
113 changes: 110 additions & 3 deletions task/host-sp-comms/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,25 @@ const MAX_HOST_FAIL_MESSAGE_LEN: usize = 4096;
// of that for us.
const NUM_HOST_MAC_ADDRESSES: u16 = 3;

// The same IO path can be used for both IPCC, and a lower-level debugging
// interface, for testing the IO path itself. We differentiate between the two
// by detecting specific header types, and parsing the corresponding messages
// into a typed enum: debug message handling is then short-circuited.
enum DebugCmd<'a> {
Discard,
Echo(&'a [u8]),
CharGen(u16),
}

#[derive(Debug, Clone, Copy, PartialEq, counters::Count)]
enum Trace {
#[count(skip)]
None,
UartRxOverrun,
ParseError(#[count(children)] DecodeFailureReason),
DebugDiscard,
DebugEcho(u64),
DebugCharGen(u16),
SetState {
now: u64,
#[count(children)]
Expand Down Expand Up @@ -786,6 +799,56 @@ impl ServerImpl {
}
}

// Process a request message from the host.
fn process_message(
&mut self,
reset_tx_buf: bool,
) -> Result<(), DecodeFailureReason> {
// Debug messages have a distinct header that separates them from normal
// IPCC messages.
if is_debug_message(self.rx_buf) {
self.process_debug_message(reset_tx_buf)
} else {
self.process_ipcc_message(reset_tx_buf)
}
}

// Process a framed debug packet
fn process_debug_message(
&mut self,
reset_tx_buf: bool,
) -> Result<(), DecodeFailureReason> {
match parse_debug_message(self.rx_buf) {
Ok(cmd) => {
if reset_tx_buf {
self.tx_buf.reset();
}
match cmd {
DebugCmd::Discard => ringbuf_entry!(Trace::DebugDiscard),
DebugCmd::Echo(data) => {
ringbuf_entry!(Trace::DebugEcho(data.len() as u64));
let _ = self.tx_buf.try_copy_raw_data(data);
}
DebugCmd::CharGen(count) => {
ringbuf_entry!(Trace::DebugCharGen(count));
// const CHARGEN: &'static [u8] =
// b"!\"#$%&'()*+,-./0123456789:;\
// <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]\
// ^_abcdefghijklmnopqrstuvwxyz{|}~`";
let mut it = (b'!'..=b'~').cycle().take(count.into());
let _ = self.tx_buf.try_fill(&mut it);
}
}
Ok(())
}
Err(err) => {
ringbuf_entry!(Trace::ParseError(err));
self.rx_buf.clear();
Err(err)
}
}
}

// Process the framed packet sitting in `self.rx_buf`. If it warrants a
// response, we configure `self.tx_buf` appropriate: either populating it
// with a response if we can come up with that response immediately, or
Expand All @@ -800,10 +863,11 @@ impl ServerImpl {
//
// This method always (i.e., on success or failure) clears `rx_buf` before
// returning to prepare for the next packet.
fn process_message(
fn process_ipcc_message(
&mut self,
reset_tx_buf: bool,
) -> Result<(), DecodeFailureReason> {
// If the message was Not a debug message, then parse it normally.
let (header, request, data) = match parse_received_message(self.rx_buf)
{
Ok((header, request, data)) => (header, request, data),
Expand Down Expand Up @@ -1663,8 +1727,9 @@ impl NotificationHandler for ServerImpl {
}
}

// This is conceptually a method on `ServerImpl`, but it takes a reference to
// `rx_buf` instead of `self` to avoid borrow checker issues.
// Parse a received message. This is conceptually a method on `ServerImpl`,
// but it takes a reference to `rx_buf` instead of `self` to avoid borrow
// checker issues.
fn parse_received_message(
rx_buf: &mut [u8],
) -> Result<(Header, HostToSp, &[u8]), DecodeFailureReason> {
Expand All @@ -1690,6 +1755,48 @@ fn parse_received_message(
Ok((header, request, data))
}

// Debug messages have a distinct header that separates them from IPCC
// messages. The first 5 bytes are the ASCII characters, "DEBUG". The
// 6th and 7th bytes encode the command number, as 0 padded ASCII hexadecimal
// string (using upper case for the non-numeric hexadigits). The commands
// are:
//
// "07" (0x07 == 7 dec) Echo
// "09" (0x09 == 9 dec) Discard
// "13" (0x13 == 19 dec) CharGen
//
// The command values correspond to the IANA-assigned port numbers for the
// corresponding UDP and TCP/IP services.
//
// This is conceptually a method on `ServerImpl`, but it takes references to
// several of its fields instead of `self` to avoid borrow checker issues.
fn is_debug_message(msg: &[u8]) -> bool {
if msg.len() < 7 {
return false;
}
msg[0..5] == *b"DEBUG"
}

// This is conceptually a method on `ServerImpl`, but it takes references to
// several of its fields instead of `self` to avoid borrow checker issues.
fn parse_debug_message(
msg: &[u8],
) -> Result<DebugCmd<'_>, DecodeFailureReason> {
assert!(is_debug_message(msg));
match &msg[5..7] {
b"07" => Ok(DebugCmd::Echo(&msg[7..])),
b"09" => Ok(DebugCmd::Discard),
b"13" if msg.len() == 11 => {
let nstr = str::from_utf8(&msg[7..11])
.map_err(|_| DecodeFailureReason::Deserialize)?;
let n = u16::from_str_radix(nstr, 16)
.map_err(|_| DecodeFailureReason::Deserialize)?;
Ok(DebugCmd::CharGen(n))
}
_ => Err(DecodeFailureReason::Deserialize),
}
}

// This is conceptually a method on `ServerImpl`, but it takes references to
// several of its fields instead of `self` to avoid borrow checker issues.
fn handle_reboot_waiting_in_a2_timer(
Expand Down
34 changes: 34 additions & 0 deletions task/host-sp-comms/src/tx_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,40 @@ impl TxBuf {
let n = corncobs::encode_buf(&self.msg[..msg_len], &mut self.pkt[1..]);
self.state = State::ToSend(0..n + 1);
}

// Copies a "raw" slice of bytes into the output packet buffer.
pub(crate) fn try_copy_raw_data(&mut self, bs: &[u8]) -> Result<usize, ()> {
let n = usize::min(self.pkt.len() - 2, bs.len());
if bs[..n].contains(&0) {
return Err(());
}
let end = n + 1;
self.pkt[0] = 0;
self.pkt[1..end].copy_from_slice(&bs[..n]);
self.pkt[end] = 0;
self.state = State::ToSend(0..end + 1);
Ok(n)
}

pub(crate) fn try_fill(
&mut self,
it: &mut impl Iterator<Item = u8>,
) -> Result<(), ()> {
let max = self.pkt.len() - 2;
let mut idx = 0;
self.pkt[idx] = 0;
idx += 1;
for b in it.take(max) {
if b == 0 {
return Err(());
}
self.pkt[idx] = b;
idx += 1;
}
self.pkt[idx] = 0;
self.state = State::ToSend(0..idx + 1);
Ok(())
}
}

enum State {
Expand Down
Loading