Skip to content

Commit f2f0d25

Browse files
committed
Add critsec to rp2040 xfer, check endpoint status
- Implemented a critical section for the rp2040 port, which now prevents an IRQ from clearing the endpoint data structures while a transfer was in progress, which would then lead to a null pointer derefence. - Fixed a null-pointer dereference regarding ep->endpoint_control for endpoint 0, which does not have a control register.
1 parent 6b7ceed commit f2f0d25

File tree

3 files changed

+34
-11
lines changed

3 files changed

+34
-11
lines changed

src/portable/raspberrypi/rp2040/dcd_rp2040.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,12 @@ static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) {
9090
*ep->endpoint_control = reg;
9191
}
9292

93-
static void _hw_endpoint_close(struct hw_endpoint* ep) {
93+
static void _hw_endpoint_close(struct hw_endpoint *ep) {
9494
// Clear hardware registers and then zero the struct
9595
// Clears endpoint enable
96-
*ep->endpoint_control = 0;
96+
if (ep->endpoint_control) {
97+
*ep->endpoint_control = 0;
98+
}
9799
// Clears buffer available, etc
98100
*ep->buffer_control = 0;
99101
// Clear any endpoint state
@@ -160,6 +162,7 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
160162
// alloc a buffer and fill in endpoint control register
161163
_hw_endpoint_alloc(ep, transfer_type);
162164
}
165+
ep->configured = true;
163166
}
164167

165168
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {

src/portable/raspberrypi/rp2040/rp2040_usb.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep,
160160
}
161161

162162
// Prepare buffer control register value
163-
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
164-
uint32_t ep_ctrl = *ep->endpoint_control;
163+
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep) {
164+
uint32_t ep_ctrl = ep->endpoint_control ? *ep->endpoint_control : 0;
165165

166166
// always compute and start with buffer 0
167167
uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL;
@@ -190,7 +190,11 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep)
190190
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
191191
}
192192

193-
*ep->endpoint_control = ep_ctrl;
193+
uint8_t epnum = tu_edpt_number(ep->ep_addr);
194+
if (epnum != 0) // There's no endpoint control for endpoint 0
195+
{
196+
*ep->endpoint_control = ep_ctrl;
197+
}
194198

195199
TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
196200

@@ -202,7 +206,12 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep)
202206
void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) {
203207
hw_endpoint_lock_update(ep, 1);
204208

205-
if (ep->active) {
209+
// We need to make sure the ep didn't get cleared from under us by an IRQ
210+
if ( !ep->configured ) {
211+
return;
212+
}
213+
214+
if ( ep->active ) {
206215
// TODO: Is this acceptable for interrupt packets?
207216
TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\r\n", tu_edpt_number(ep->ep_addr),
208217
ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
@@ -273,7 +282,7 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep)
273282
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
274283

275284
// sync buffer 1 if double buffered
276-
if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
285+
if ( ep->endpoint_control && ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) ) {
277286
if (buf0_bytes == ep->wMaxPacketSize) {
278287
// sync buffer 1 if not short packet
279288
sync_ep_buffer(ep, 1);

src/portable/raspberrypi/rp2040/rp2040_usb.h

+15-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "common/tusb_common.h"
99

1010
#include "pico.h"
11+
#include "pico/critical_section.h"
1112
#include "hardware/structs/usb.h"
1213
#include "hardware/irq.h"
1314
#include "hardware/resets.h"
@@ -104,10 +105,20 @@ bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
104105
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
105106
void hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
106107

107-
TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
108-
// todo add critsec as necessary to prevent issues between worker and IRQ...
109-
// note that this is perhaps as simple as disabling IRQs because it would make
110-
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
108+
TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, int delta) {
109+
static critical_section_t hw_endpoint_crit_sec;
110+
static int hw_endpoint_crit_sec_ref = 0;
111+
if ( !critical_section_is_initialized(&hw_endpoint_crit_sec) ) {
112+
critical_section_init(&hw_endpoint_crit_sec);
113+
}
114+
115+
if ( delta > 0 && !hw_endpoint_crit_sec_ref ) {
116+
critical_section_enter_blocking(&hw_endpoint_crit_sec);
117+
}
118+
hw_endpoint_crit_sec_ref += delta;
119+
if ( hw_endpoint_crit_sec_ref == 0 ) {
120+
critical_section_exit(&hw_endpoint_crit_sec);
121+
}
111122
}
112123

113124
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);

0 commit comments

Comments
 (0)