Skip to content

Commit c430692

Browse files
committed
cpu_seq: Encode ereports into static buffers (#2225)
In [this comment][1], @cbiffle reminded me that we can use `ClaimOnceCell` to use a `static` to create byte buffers for encoding ereports. This way, the buffer is never on the stack, reducing the risk of stack overflows when recording an ereport. This commit changes `drv_gimlet_seq_server` and `drv_cosmo_seq_server` to use that technique for their ereports. [1]: #2214 (comment)
1 parent 7501828 commit c430692

File tree

7 files changed

+81
-53
lines changed

7 files changed

+81
-53
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

drv/cosmo-seq-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ringbuf = { path = "../../lib/ringbuf" }
2121
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }
2222
task-jefe-api = { path = "../../task/jefe-api" }
2323
task-packrat-api = { path = "../../task/packrat-api", features = ["serde"] }
24+
static-cell = { path = "../../lib/static-cell" }
2425

2526
cfg-if = { workspace = true }
2627
idol-runtime.workspace = true

drv/cosmo-seq-server/src/main.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,13 @@ struct ServerImpl {
378378
hf: HostFlash,
379379
seq: fmc_periph::Sequencer,
380380
vcore: VCore,
381+
/// Static buffer for encoding ereports. This is a static so that we don't
382+
/// have it on the stack when encoding ereports.
383+
ereport_buf: &'static mut [u8; EREPORT_BUF_LEN],
381384
}
382385

386+
const EREPORT_BUF_LEN: usize = 256;
387+
383388
impl ServerImpl {
384389
fn new(
385390
token: drv_spartan7_loader_api::Spartan7Token,
@@ -399,13 +404,21 @@ impl ServerImpl {
399404
let jefe = Jefe::from(JEFE.get_task_id());
400405
jefe.set_state(PowerState::A2 as u32);
401406

407+
let ereport_buf = {
408+
use static_cell::ClaimOnceCell;
409+
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
410+
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
411+
EREPORT_BUF.claim()
412+
};
413+
402414
ServerImpl {
403415
state: PowerState::A2,
404416
jefe,
405417
sys: Sys::from(SYS.get_task_id()),
406418
hf: HostFlash::from(HF.get_task_id()),
407419
seq,
408420
vcore: VCore::new(I2C.get_task_id(), packrat),
421+
ereport_buf,
409422
}
410423
}
411424

@@ -741,7 +754,8 @@ impl ServerImpl {
741754
vddcr_cpu0: ifr.pwr_cont1_to_fpga1_alert,
742755
vddcr_cpu1: ifr.pwr_cont2_to_fpga1_alert,
743756
};
744-
self.vcore.handle_pmbus_alert(which_rails, now);
757+
self.vcore
758+
.handle_pmbus_alert(which_rails, now, self.ereport_buf);
745759

746760
// We need not instruct the sequencer to reset. PMBus alerts from
747761
// the RAA229620As are divided into two categories, "warnings" and

drv/cosmo-seq-server/src/vcore.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use drv_i2c_api::ResponseCode;
1717
use drv_i2c_devices::raa229620a::{self, Raa229620A};
1818
use ringbuf::*;
1919
use serde::Serialize;
20-
use task_packrat_api::{self, Packrat};
2120
use userlib::{sys_get_timer, units, TaskId};
2221

2322
pub(super) struct VCore {
@@ -162,18 +161,31 @@ impl VCore {
162161
Ok(())
163162
}
164163

165-
pub fn handle_pmbus_alert(&self, mut rails: Rails, now: u64) {
164+
pub fn handle_pmbus_alert(
165+
&self,
166+
mut rails: Rails,
167+
now: u64,
168+
ereport_buf: &mut [u8],
169+
) {
166170
ringbuf_entry!(Trace::PmbusAlert {
167171
timestamp: now,
168172
rails,
169173
});
170174

171-
let cpu0_state =
172-
self.record_pmbus_status(now, Rail::VddcrCpu0, rails.vddcr_cpu0);
175+
let cpu0_state = self.record_pmbus_status(
176+
now,
177+
Rail::VddcrCpu0,
178+
rails.vddcr_cpu0,
179+
ereport_buf,
180+
);
173181
rails.vddcr_cpu0 |= cpu0_state.faulted;
174182

175-
let cpu1_state =
176-
self.record_pmbus_status(now, Rail::VddcrCpu1, rails.vddcr_cpu1);
183+
let cpu1_state = self.record_pmbus_status(
184+
now,
185+
Rail::VddcrCpu1,
186+
rails.vddcr_cpu1,
187+
ereport_buf,
188+
);
177189
rails.vddcr_cpu1 |= cpu1_state.faulted;
178190

179191
if cpu0_state.input_fault || cpu1_state.input_fault {
@@ -246,6 +258,7 @@ impl VCore {
246258
now: u64,
247259
rail: Rail,
248260
alerted: bool,
261+
ereport_buf: &mut [u8],
249262
) -> RegulatorState {
250263
use pmbus::commands::raa229620a::STATUS_WORD;
251264

@@ -372,7 +385,18 @@ impl VCore {
372385
status,
373386
pwr_good: power_good,
374387
};
375-
deliver_ereport(rail, &self.packrat, &ereport);
388+
match self.packrat.serialize_ereport(&ereport, ereport_buf) {
389+
Ok(len) => ringbuf_entry!(Trace::EreportSent(rail, len)),
390+
Err(task_packrat_api::EreportSerializeError::Packrat {
391+
len,
392+
err,
393+
}) => {
394+
ringbuf_entry!(Trace::EreportLost(rail, len, err))
395+
}
396+
Err(task_packrat_api::EreportSerializeError::Serialize(_)) => {
397+
ringbuf_entry!(Trace::EreportTooBig(rail))
398+
}
399+
}
376400

377401
RegulatorState {
378402
faulted,
@@ -432,25 +456,3 @@ fn retry_i2c_txn<T>(
432456
}
433457
}
434458
}
435-
436-
// This is in its own function so that the ereport buffer is only on the stack
437-
// while we're using it, and not for the entireity of `record_pmbus_status`,
438-
// which calls into a bunch of other functions. This may reduce our stack depth
439-
// a bit.
440-
#[inline(never)]
441-
fn deliver_ereport(
442-
rail: Rail,
443-
packrat: &Packrat,
444-
data: &impl serde::Serialize,
445-
) {
446-
let mut ereport_buf = [0u8; 256];
447-
match packrat.serialize_ereport(data, &mut ereport_buf[..]) {
448-
Ok(len) => ringbuf_entry!(Trace::EreportSent(rail, len)),
449-
Err(task_packrat_api::EreportSerializeError::Packrat { len, err }) => {
450-
ringbuf_entry!(Trace::EreportLost(rail, len, err))
451-
}
452-
Err(task_packrat_api::EreportSerializeError::Serialize(_)) => {
453-
ringbuf_entry!(Trace::EreportTooBig(rail))
454-
}
455-
}
456-
}

drv/gimlet-seq-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ ringbuf = { path = "../../lib/ringbuf" }
2020
task-jefe-api = { path = "../../task/jefe-api" }
2121
task-packrat-api = { path = "../../task/packrat-api", features = ["serde"] }
2222
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }
23+
static-cell = { path = "../../lib/static-cell" }
2324

2425
cfg-if = { workspace = true }
2526
cortex-m = { workspace = true }

drv/gimlet-seq-server/src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,13 @@ struct ServerImpl<S: SpiServer> {
201201
hf: hf_api::HostFlash,
202202
vcore: vcore::VCore,
203203
deadline: u64,
204+
// Buffer for encoding ereports. This is a static so that it's not on the
205+
// stack when handling interrupts.
206+
ereport_buf: &'static mut [u8; EREPORT_BUF_LEN],
204207
}
205208

206209
const TIMER_INTERVAL: u32 = 10;
210+
const EREPORT_BUF_LEN: usize = 128;
207211

208212
impl<S: SpiServer + Clone> ServerImpl<S> {
209213
fn init(
@@ -483,6 +487,13 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
483487

484488
let (device, rail) = i2c_config::pmbus::vdd_vcore(I2C.get_task_id());
485489

490+
let ereport_buf = {
491+
use static_cell::ClaimOnceCell;
492+
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
493+
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
494+
EREPORT_BUF.claim()
495+
};
496+
486497
let mut server = Self {
487498
state: PowerState::A2,
488499
sys: sys.clone(),
@@ -491,6 +502,7 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
491502
hf,
492503
deadline: 0,
493504
vcore: vcore::VCore::new(sys, packrat, &device, rail),
505+
ereport_buf,
494506
};
495507

496508
// Power on, unless suppressed by the `stay-in-a2` feature
@@ -530,7 +542,7 @@ impl<S: SpiServer> NotificationHandler for ServerImpl<S> {
530542

531543
fn handle_notification(&mut self, bits: u32) {
532544
if (bits & self.vcore.mask()) != 0 {
533-
self.vcore.handle_notification();
545+
self.vcore.handle_notification(self.ereport_buf);
534546
}
535547

536548
if (bits & notifications::TIMER_MASK) == 0 {

drv/gimlet-seq-server/src/vcore.rs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl VCore {
139139
Ok(())
140140
}
141141

142-
pub fn handle_notification(&self) {
142+
pub fn handle_notification(&self, ereport_buf: &mut [u8]) {
143143
let now = sys_get_timer().now;
144144
let asserted = self.sys.gpio_read(VCORE_TO_SP_ALERT_L) == 0;
145145

@@ -149,7 +149,7 @@ impl VCore {
149149
});
150150

151151
if asserted {
152-
self.read_pmbus_status(now);
152+
self.read_pmbus_status(now, ereport_buf);
153153
// Clear the fault now so that PMALERT_L is reasserted if a
154154
// subsequent fault occurs. Note that if the fault *condition*
155155
// continues, the fault bits in the status registers will remain
@@ -162,7 +162,7 @@ impl VCore {
162162
let _ = self.sys.gpio_irq_control(self.mask(), IrqControl::Enable);
163163
}
164164

165-
fn read_pmbus_status(&self, now: u64) {
165+
fn read_pmbus_status(&self, now: u64, ereport_buf: &mut [u8]) {
166166
use pmbus::commands::raa229618::STATUS_WORD;
167167

168168
// Read PMBus status registers and prepare an ereport.
@@ -275,7 +275,21 @@ impl VCore {
275275
pwr_good,
276276
status,
277277
};
278-
deliver_ereport(&self.packrat, &ereport);
278+
match self
279+
.packrat
280+
.serialize_ereport(&ereport, &mut ereport_buf[..])
281+
{
282+
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
283+
Err(task_packrat_api::EreportSerializeError::Packrat {
284+
len,
285+
err,
286+
}) => {
287+
ringbuf_entry!(Trace::EreportLost(len, err))
288+
}
289+
Err(task_packrat_api::EreportSerializeError::Serialize(_)) => {
290+
ringbuf_entry!(Trace::EreportTooBig)
291+
}
292+
}
279293

280294
// If the `INPUT_FAULT` bit in `STATUS_WORD` is set, or any bit is hot
281295
// in `STATUS_INPUT`, sample Vin in order to record the voltage dip in
@@ -331,21 +345,3 @@ struct Ereport {
331345
pwr_good: Option<bool>,
332346
status: PmbusStatus,
333347
}
334-
335-
// This is in its own function so that the ereport buffer and `Ereport` struct
336-
// are only on the stack while we're using it, and not for the entireity of
337-
// `record_pmbus_status`, which calls into a bunch of other functions. This may
338-
// reduce our stack depth a bit.
339-
#[inline(never)]
340-
fn deliver_ereport(packrat: &packrat_api::Packrat, data: &impl Serialize) {
341-
let mut ereport_buf = [0u8; 128];
342-
match packrat.serialize_ereport(data, &mut ereport_buf[..]) {
343-
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
344-
Err(task_packrat_api::EreportSerializeError::Packrat { len, err }) => {
345-
ringbuf_entry!(Trace::EreportLost(len, err))
346-
}
347-
Err(task_packrat_api::EreportSerializeError::Serialize(_)) => {
348-
ringbuf_entry!(Trace::EreportTooBig)
349-
}
350-
}
351-
}

0 commit comments

Comments
 (0)