From 02cc4345b5def59e96ef1eb576889922e9468113 Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 24 Jan 2024 17:06:37 -0800 Subject: [PATCH] gimlet-inspector: first draft. --- Cargo.lock | 56 +++++++---- Cargo.toml | 1 + app/gimlet/base.toml | 17 ++++ task/gimlet-inspector/Cargo.toml | 28 ++++++ task/gimlet-inspector/build.rs | 8 ++ task/gimlet-inspector/src/main.rs | 152 ++++++++++++++++++++++++++++++ 6 files changed, 245 insertions(+), 17 deletions(-) create mode 100644 task/gimlet-inspector/Cargo.toml create mode 100644 task/gimlet-inspector/build.rs create mode 100644 task/gimlet-inspector/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index f841cbf1c..15439a60a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,7 +588,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -610,7 +610,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -2268,6 +2268,15 @@ dependencies = [ "stm32h7", ] +[[package]] +name = "gimlet-inspector-protocol" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/gimlet-inspector-protocol#030004d082f4ec70e33cbc00886096b15ef3c2cc" +dependencies = [ + "hubpack", + "serde", +] + [[package]] name = "gimletlet" version = "0.1.0" @@ -3251,9 +3260,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -3273,9 +3282,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -3570,9 +3579,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] @@ -3597,13 +3606,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -3679,7 +3688,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -3936,7 +3945,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -3958,9 +3967,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -4143,6 +4152,19 @@ dependencies = [ "zerocopy 0.6.4", ] +[[package]] +name = "task-gimlet-inspector" +version = "0.1.0" +dependencies = [ + "build-util", + "drv-gimlet-seq-api", + "gimlet-inspector-protocol", + "hubpack", + "serde", + "task-net-api", + "userlib", +] + [[package]] name = "task-hiffy" version = "0.1.0" @@ -5408,7 +5430,7 @@ checksum = "56097d5b91d711293a42be9289403896b68654625021732067eac7a4ca388a1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] @@ -5419,7 +5441,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.48", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b18577287..59d0cae76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,6 +116,7 @@ zip = { version = "0.6", default-features = false, features = ["bzip2"] } attest-data = { git = "https://github.com/oxidecomputer/dice-util", default-features = false, version = "0.1.0" } dice-mfg-msgs = { git = "https://github.com/oxidecomputer/dice-util", default-features = false, version = "0.2.1" } gateway-messages = { git = "https://github.com/oxidecomputer/management-gateway-service", default-features = false, features = ["smoltcp"] } +gimlet-inspector-protocol = { git = "https://github.com/oxidecomputer/gimlet-inspector-protocol", version = "0.1.0" } hif = { git = "https://github.com/oxidecomputer/hif", default-features = false } humpty = { git = "https://github.com/oxidecomputer/humpty", default-features = false, version = "0.1.3" } hubtools = { git = "https://github.com/oxidecomputer/hubtools", default-features = false, version = "0.4.1" } diff --git a/app/gimlet/base.toml b/app/gimlet/base.toml index 704ddbd1d..08e019150 100644 --- a/app/gimlet/base.toml +++ b/app/gimlet/base.toml @@ -154,6 +154,16 @@ copy-to-archive = ["register_defs"] fpga_image = "fpga-b.bin" register_defs = "gimlet-regs-b.json" +[tasks.gimlet_inspector] +name = "task-gimlet-inspector" +priority = 6 +features = ["vlan"] +max-sizes = {flash = 16384, ram = 2048 } +stacksize = 1600 +start = true +task-slots = ["net", {seq = "gimlet_seq"}] +notifications = ["socket"] + [tasks.hash_driver] name = "drv-stm32h7-hash-server" features = ["h753"] @@ -1271,6 +1281,13 @@ port = 11113 tx = { packets = 3, bytes = 1024 } rx = { packets = 3, bytes = 1024 } +[config.net.sockets.inspector] +kind = "udp" +owner = {name = "gimlet_inspector", notification = "socket"} +port = 23547 +tx = { packets = 3, bytes = 1024 } +rx = { packets = 3, bytes = 512 } + [config.sprot] # ROT_IRQ (af=0 for GPIO, af=15 when EXTI is implemneted) rot_irq = { port = "E", pin = 3, af = 0} diff --git a/task/gimlet-inspector/Cargo.toml b/task/gimlet-inspector/Cargo.toml new file mode 100644 index 000000000..230c91cd9 --- /dev/null +++ b/task/gimlet-inspector/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "task-gimlet-inspector" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { workspace = true } +hubpack = { workspace = true } +gimlet-inspector-protocol = { workspace = true } + +task-net-api = { path = "../net-api" } +drv-gimlet-seq-api = { path = "../../drv/gimlet-seq-api" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } + + +[build-dependencies] +build-util = { path = "../../build/util" } + +[features] +vlan = ["task-net-api/vlan"] + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[[bin]] +name = "task-gimlet-inspector" +test = false +doctest = false +bench = false diff --git a/task/gimlet-inspector/build.rs b/task/gimlet-inspector/build.rs new file mode 100644 index 000000000..d57bdbf7b --- /dev/null +++ b/task/gimlet-inspector/build.rs @@ -0,0 +1,8 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +fn main() -> Result<(), Box> { + build_util::build_notifications()?; + Ok(()) +} diff --git a/task/gimlet-inspector/src/main.rs b/task/gimlet-inspector/src/main.rs new file mode 100644 index 000000000..d0cedb950 --- /dev/null +++ b/task/gimlet-inspector/src/main.rs @@ -0,0 +1,152 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! The Gimlet Inspector provides a deliberately limited set of IPCs over the +//! network, for extracting diagnostic data from a live system. This is intended +//! to supplement the more general `dump_agent`. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicUsize, Ordering}; +use drv_gimlet_seq_api::{SeqError, Sequencer}; +use gimlet_inspector_protocol::{ + QueryV0, Request, SequencerRegistersResponseV0, ANY_RESPONSE_V0_MAX_SIZE, + REQUEST_TRAILER, +}; +use hubpack::SerializedSize; +use task_net_api::*; +use userlib::*; + +task_slot!(NET, net); +task_slot!(SEQ, seq); + +#[no_mangle] +static CTR_RECVD: AtomicUsize = AtomicUsize::new(0); +#[no_mangle] +static CTR_REJECTED: AtomicUsize = AtomicUsize::new(0); +#[no_mangle] +static CTR_RESPONSES: AtomicUsize = AtomicUsize::new(0); + +#[export_name = "main"] +fn main() -> ! { + // Look up our peer task IDs and make clients. + let net = Net::from(NET.get_task_id()); + let seq = Sequencer::from(SEQ.get_task_id()); + + const SOCKET: SocketName = SocketName::inspector; + + loop { + // These buffers are currently kept kinda small because our protocol + // messages are small. + let mut rx_data_buf = [0u8; Request::MAX_SIZE + REQUEST_TRAILER]; + let mut tx_data_buf = [0u8; ANY_RESPONSE_V0_MAX_SIZE]; + + match net.recv_packet( + SOCKET, + LargePayloadBehavior::Discard, + &mut rx_data_buf, + ) { + Ok(mut meta) => { + CTR_RECVD.fetch_add(1, Ordering::Relaxed); + + let Ok((request, _trailer)) = hubpack::deserialize::(&rx_data_buf) else { + // We ignore malformatted, truncated, etc. packets. + CTR_REJECTED.fetch_add(1, Ordering::Relaxed); + continue; + }; + + match request { + Request::V0(QueryV0::SequencerRegisters) => { + let result = seq.read_fpga_regs(); + let (resp, trailer) = match result { + Ok(regs) => ( + SequencerRegistersResponseV0::Success, + Some(regs), + ), + Err(SeqError::ServerRestarted) => ( + SequencerRegistersResponseV0::SequencerTaskDead, + None, + ), + Err(_) => { + // The SeqError type represents a mashing + // together of all possible errors for all + // possible sequencer IPC operations. The only + // one we _expect_ here is ReadRegsFailed. + (SequencerRegistersResponseV0::SequencerReadRegsFailed, None) + } + }; + let mut len = + hubpack::serialize(&mut tx_data_buf, &resp) + .unwrap_lite(); + if let Some(t) = trailer { + tx_data_buf[len..len + t.len()].copy_from_slice(&t); + len += t.len(); + } + meta.size = len as u32; + } + } + + // With the response packet prepared, we may need to attempt + // sending more than once. + loop { + match net.send_packet( + SOCKET, + meta, + &tx_data_buf[0..(meta.size as usize)], + ) { + Ok(()) => { + CTR_RESPONSES.fetch_add(1, Ordering::Relaxed); + break; + } + // If `net` just restarted, immediately retry our send. + Err(SendError::ServerRestarted) => continue, + // If our tx queue is full, wait for space. This is the + // same notification we get for incoming packets, so we + // might spuriously wake up due to an incoming packet + // (which we can't service anyway because we are still + // waiting to respond to a previous request); once we + // finally succeed in sending we'll peel any queued + // packets off our recv queue at the top of our main + // loop. + Err(SendError::QueueFull) => { + sys_recv_closed( + &mut [], + notifications::SOCKET_MASK, + TaskId::KERNEL, + ) + .unwrap_lite(); + } + // These errors should be impossible if we're configured + // correctly. + Err(SendError::NotYours | SendError::InvalidVLan) => { + unreachable!() + } + // Unclear under what conditions we could sse `Other` - + // just panic for now? At the time of this writing + // `Other` should only come back if the destination + // address in `meta` is bogus or our socket is closed, + // neither of which should be possible here. + Err(SendError::Other) => panic!(), + } + } + } + Err(RecvError::QueueEmpty) => { + // Our incoming queue is empty. Wait for more packets. + sys_recv_closed( + &mut [], + notifications::SOCKET_MASK, + TaskId::KERNEL, + ) + .unwrap_lite(); + } + Err(RecvError::ServerRestarted) => { + // `net` restarted (probably due to the watchdog); just retry. + } + Err(RecvError::NotYours | RecvError::Other) => panic!(), + } + } +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs"));