Skip to content

Commit 63b81c8

Browse files
cbifflemkeeter
andauthored
stm32xx-i2c: remove I2cControl function table (#2121)
It turns out that, in practice, we only ever provide a single function for each of `wfi` and `enable` here. This makes the table itself an unnecessary level of indirection, which has some deleterious effects: - It opens the possibility for _different_ I2cControl structs containing _different_ function pointers to be passed to each call, complicating analysis. (Searching for this phenomenon was what led me to notice that we only ever use one implementation.) - It makes it a lot harder to recognize use of syscalls (or misuse of syscalls) in the I2C layer. - It routes important parts of the I2C stack through indirect function calls, defeating stack analysis. - It adds a bunch of code that isn't really necessary, as it turns out. This commit removes the dispatch table, inlining the existing functions into their callsites (and introducing a utility function for the wfi behavior). --------- Co-authored-by: Matt Keeter <[email protected]>
1 parent 7584e1d commit 63b81c8

File tree

7 files changed

+63
-107
lines changed

7 files changed

+63
-107
lines changed

drv/stm32xx-i2c-server/src/main.rs

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ fn configure_mux(
9595
port: PortIndex,
9696
mux: Option<(Mux, Segment)>,
9797
muxes: &[I2cMux<'_>],
98-
ctrl: &I2cControl,
9998
) -> Result<(), ResponseCode> {
10099
let bus = (controller.controller, port);
101100

@@ -127,7 +126,7 @@ fn configure_mux(
127126
// (errant) address conflict can be pretty brutal.
128127
//
129128
find_mux(controller, port, muxes, current_id, |mux| {
130-
mux.driver.enable_segment(mux, controller, None, ctrl)
129+
mux.driver.enable_segment(mux, controller, None)
131130
})
132131
.map_err(|err| {
133132
//
@@ -157,7 +156,7 @@ fn configure_mux(
157156
// mux state as unknown.
158157
//
159158
all_muxes(controller, port, muxes, |mux| {
160-
match mux.driver.enable_segment(mux, controller, None, ctrl) {
159+
match mux.driver.enable_segment(mux, controller, None) {
161160
Err(ResponseCode::MuxMissing) => {
162161
//
163162
// The mux is gone entirely. We really don't expect
@@ -201,7 +200,7 @@ fn configure_mux(
201200
if let Some((id, segment)) = mux {
202201
find_mux(controller, port, muxes, id, |mux| {
203202
mux.driver
204-
.enable_segment(mux, controller, Some(segment), ctrl)
203+
.enable_segment(mux, controller, Some(segment))
205204
.map_err(|err| {
206205
//
207206
// We have failed to enable our new mux+segment.
@@ -378,40 +377,7 @@ fn main() -> ! {
378377
// Field messages.
379378
let mut buffer = [0; 4];
380379

381-
let ctrl = I2cControl {
382-
enable: |notification| {
383-
sys_irq_control(notification, true);
384-
},
385-
wfi: |notification, timeout| {
386-
const TIMER_NOTIFICATION: u32 = 1 << 31;
387-
388-
// If the driver passes in a timeout that is large enough that it
389-
// would overflow the kernel's 64-bit timestamp space... well, we'll
390-
// do the best we can without compiling in an unlikely panic.
391-
let dead = sys_get_timer().now.saturating_add(timeout.0);
392-
393-
sys_set_timer(Some(dead), TIMER_NOTIFICATION);
394-
395-
let notification =
396-
sys_recv_notification(notification | TIMER_NOTIFICATION);
397-
398-
if notification == TIMER_NOTIFICATION {
399-
I2cControlResult::TimedOut
400-
} else {
401-
sys_set_timer(None, TIMER_NOTIFICATION);
402-
I2cControlResult::Interrupted
403-
}
404-
},
405-
};
406-
407-
configure_muxes(
408-
&muxes,
409-
&controllers,
410-
&pins,
411-
&mut portmap,
412-
&mut muxmap,
413-
&ctrl,
414-
);
380+
configure_muxes(&muxes, &controllers, &pins, &mut portmap, &mut muxmap);
415381

416382
loop {
417383
hl::recv_without_notification(&mut buffer, |op, msg| match op {
@@ -438,14 +404,8 @@ fn main() -> ! {
438404

439405
configure_port(&mut portmap, controller, port, &pins);
440406

441-
match configure_mux(
442-
&mut muxmap,
443-
controller,
444-
port,
445-
mux,
446-
&muxes,
447-
&ctrl,
448-
) {
407+
match configure_mux(&mut muxmap, controller, port, mux, &muxes)
408+
{
449409
Ok(_) => {}
450410
Err(code) => {
451411
ringbuf_entry!(Trace::MuxError(code.into()));
@@ -511,7 +471,6 @@ fn main() -> ! {
511471

512472
rbuf.write_at(pos, byte)
513473
},
514-
&ctrl,
515474
);
516475
match controller_result {
517476
Err(code) => {
@@ -775,7 +734,6 @@ fn configure_muxes(
775734
pins: &[I2cPins],
776735
map: &mut PortMap,
777736
muxmap: &mut MuxMap,
778-
ctrl: &I2cControl,
779737
) {
780738
let sys = SYS.get_task_id();
781739
let sys = Sys::from(sys);
@@ -788,7 +746,7 @@ fn configure_muxes(
788746
let mut reset_attempted = false;
789747

790748
loop {
791-
match mux.driver.configure(mux, controller, &sys, ctrl) {
749+
match mux.driver.configure(mux, controller, &sys) {
792750
Ok(_) => {
793751
//
794752
// We are going to attempt to disable all segments. If we
@@ -815,7 +773,7 @@ fn configure_muxes(
815773
// deal with the reset).
816774
//
817775
if let Err(code) =
818-
mux.driver.enable_segment(mux, controller, None, ctrl)
776+
mux.driver.enable_segment(mux, controller, None)
819777
{
820778
ringbuf_entry!(Trace::SegmentFailed(code.into()));
821779

drv/stm32xx-i2c/src/lib.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,6 @@ pub enum I2cControlResult {
7676
TimedOut,
7777
}
7878

79-
///
80-
/// A structure that defines interrupt control flow functions that will be
81-
/// used to pass control flow into the kernel to either enable or wait for
82-
/// interrupts. Note that this is deliberately a struct and not a trait,
83-
/// allowing the [`I2cMuxDriver`] trait to itself be a trait object.
84-
///
85-
pub struct I2cControl {
86-
pub enable: fn(u32),
87-
pub wfi: fn(u32, I2cTimeout) -> I2cControlResult,
88-
}
89-
9079
pub struct I2cTargetControl {
9180
pub enable: fn(u32),
9281
pub wfi: fn(u32),
@@ -109,7 +98,6 @@ pub trait I2cMuxDriver {
10998
mux: &I2cMux<'_>,
11099
controller: &I2cController<'_>,
111100
sys: &sys_api::Sys,
112-
ctrl: &I2cControl,
113101
) -> Result<(), drv_i2c_api::ResponseCode>;
114102

115103
/// Reset the mux
@@ -126,7 +114,6 @@ pub trait I2cMuxDriver {
126114
mux: &I2cMux<'_>,
127115
controller: &I2cController<'_>,
128116
segment: Option<drv_i2c_api::Segment>,
129-
ctrl: &I2cControl,
130117
) -> Result<(), drv_i2c_api::ResponseCode>;
131118
}
132119

@@ -503,15 +490,18 @@ impl I2cController<'_> {
503490
}
504491

505492
///
506-
/// A common routine to wait for interrupts with a timeout.
493+
/// A common routine to wait for interrupts, panicking the driver if the
494+
/// interrupt doesn't arrive in an arbitrarily chosen period of time.
507495
///
508-
fn wfi(&self, ctrl: &I2cControl) -> Result<(), drv_i2c_api::ResponseCode> {
496+
fn wfi(&self) -> Result<(), drv_i2c_api::ResponseCode> {
509497
//
510-
// A 100 ms timeout is much, much longer than the I2C timeouts.
498+
// A 100 ms timeout is 4x longer than the I2C timeouts, but much shorter
499+
// than potential context switches when dumps are being recorded by a
500+
// high priority task.
511501
//
512502
const TIMEOUT: I2cTimeout = I2cTimeout(100);
513503

514-
match (ctrl.wfi)(self.notification, TIMEOUT) {
504+
match wfi_raw(self.notification, TIMEOUT) {
515505
I2cControlResult::TimedOut => {
516506
//
517507
// This really shouldn't happen: it means that not only did
@@ -607,7 +597,6 @@ impl I2cController<'_> {
607597
getbyte: impl Fn(usize) -> Option<u8>,
608598
mut rlen: ReadLength,
609599
mut putbyte: impl FnMut(usize, u8) -> Option<()>,
610-
ctrl: &I2cControl,
611600
) -> Result<(), drv_i2c_api::ResponseCode> {
612601
// Assert our preconditions as described above
613602
assert!(wlen > 0 || rlen != ReadLength::Fixed(0));
@@ -652,8 +641,8 @@ impl I2cController<'_> {
652641
break;
653642
}
654643

655-
self.wfi(ctrl)?;
656-
(ctrl.enable)(notification);
644+
self.wfi()?;
645+
sys_irq_control(notification, true);
657646
}
658647

659648
// Get a single byte.
@@ -682,8 +671,8 @@ impl I2cController<'_> {
682671
break;
683672
}
684673

685-
self.wfi(ctrl)?;
686-
(ctrl.enable)(notification);
674+
self.wfi()?;
675+
sys_irq_control(notification, true);
687676
}
688677
}
689678

@@ -730,8 +719,8 @@ impl I2cController<'_> {
730719
}
731720

732721
loop {
733-
self.wfi(ctrl)?;
734-
(ctrl.enable)(notification);
722+
self.wfi()?;
723+
sys_irq_control(notification, true);
735724

736725
let isr = i2c.isr.read();
737726
ringbuf_entry!(Trace::Read(Register::ISR, isr.bits()));
@@ -785,8 +774,8 @@ impl I2cController<'_> {
785774

786775
self.check_errors(&isr)?;
787776

788-
self.wfi(ctrl)?;
789-
(ctrl.enable)(notification);
777+
self.wfi()?;
778+
sys_irq_control(notification, true);
790779
}
791780
}
792781

@@ -820,7 +809,6 @@ impl I2cController<'_> {
820809
&self,
821810
addr: u8,
822811
ops: &[I2cKonamiCode],
823-
ctrl: &I2cControl,
824812
) -> Result<(), drv_i2c_api::ResponseCode> {
825813
let i2c = self.registers;
826814
let notification = self.notification;
@@ -864,8 +852,8 @@ impl I2cController<'_> {
864852
break;
865853
}
866854

867-
self.wfi(ctrl)?;
868-
(ctrl.enable)(notification);
855+
self.wfi()?;
856+
sys_irq_control(notification, true);
869857
}
870858
}
871859

@@ -1191,3 +1179,35 @@ impl I2cController<'_> {
11911179
}
11921180
}
11931181
}
1182+
1183+
/// Waits for `event_mask` or `timeout`, whichever comes first. This is
1184+
/// factored out of `wfi` above for clarity.
1185+
///
1186+
/// This function, like `userlib::hl`, assumes that notification bit 31 is safe
1187+
/// to use for the timer. If `event_mask` also has bit 31 set, weird things
1188+
/// will happen, don't do that.
1189+
fn wfi_raw(event_mask: u32, timeout: I2cTimeout) -> I2cControlResult {
1190+
const TIMER_NOTIFICATION: u32 = 1 << 31;
1191+
1192+
// If the driver passes in a timeout that is large enough that it would
1193+
// overflow the kernel's 64-bit timestamp space... well, we'll do the
1194+
// best we can without compiling in an unlikely panic.
1195+
let dead = sys_get_timer().now.saturating_add(timeout.0);
1196+
1197+
sys_set_timer(Some(dead), TIMER_NOTIFICATION);
1198+
1199+
let received = sys_recv_notification(event_mask | TIMER_NOTIFICATION);
1200+
1201+
// If the event arrived _and_ our timer went off, prioritize the event and
1202+
// ignore the timeout.
1203+
if received & event_mask != 0 {
1204+
// Attempt to clear our timer. Note that this does not protect against
1205+
// the race where the kernel decided to wake us up, but the timer went
1206+
// off before we got to this point.
1207+
sys_set_timer(None, TIMER_NOTIFICATION);
1208+
I2cControlResult::Interrupted
1209+
} else {
1210+
// The event_mask bit was not set, so:
1211+
I2cControlResult::TimedOut
1212+
}
1213+
}

drv/stm32xx-i2c/src/ltc4306.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ fn read_reg_u8(
8888
mux: &I2cMux<'_>,
8989
controller: &I2cController<'_>,
9090
reg: u8,
91-
ctrl: &I2cControl,
9291
) -> Result<u8, ResponseCode> {
9392
let mut rval = 0u8;
9493
let wlen = 1;
@@ -102,7 +101,6 @@ fn read_reg_u8(
102101
rval = byte;
103102
Some(())
104103
},
105-
ctrl,
106104
);
107105
match controller_result {
108106
Err(code) => Err(mux.error_code(code)),
@@ -115,15 +113,13 @@ fn write_reg_u8(
115113
controller: &I2cController<'_>,
116114
reg: u8,
117115
val: u8,
118-
ctrl: &I2cControl,
119116
) -> Result<(), ResponseCode> {
120117
match controller.write_read(
121118
mux.address,
122119
2,
123120
|pos| Some(if pos == 0 { reg } else { val }),
124121
ReadLength::Fixed(0),
125122
|_, _| Some(()),
126-
ctrl,
127123
) {
128124
Err(code) => Err(mux.error_code(code)),
129125
_ => Ok(()),
@@ -136,7 +132,6 @@ impl I2cMuxDriver for Ltc4306 {
136132
mux: &I2cMux<'_>,
137133
_controller: &I2cController<'_>,
138134
gpio: &sys_api::Sys,
139-
_ctrl: &I2cControl,
140135
) -> Result<(), drv_i2c_api::ResponseCode> {
141136
mux.configure(gpio)
142137
}
@@ -146,7 +141,6 @@ impl I2cMuxDriver for Ltc4306 {
146141
mux: &I2cMux<'_>,
147142
controller: &I2cController<'_>,
148143
segment: Option<Segment>,
149-
ctrl: &I2cControl,
150144
) -> Result<(), ResponseCode> {
151145
let mut reg3 = Register3(0);
152146

@@ -170,8 +164,8 @@ impl I2cMuxDriver for Ltc4306 {
170164
}
171165
}
172166

173-
write_reg_u8(mux, controller, 3, reg3.0, ctrl)?;
174-
let reg0 = Register0(read_reg_u8(mux, controller, 0, ctrl)?);
167+
write_reg_u8(mux, controller, 3, reg3.0)?;
168+
let reg0 = Register0(read_reg_u8(mux, controller, 0)?);
175169

176170
if !reg0.not_failed() {
177171
Err(ResponseCode::SegmentDisconnected)

0 commit comments

Comments
 (0)