diff --git a/arch/Kconfig b/arch/Kconfig index af0fd00f6a9..8ce7ca2edf8 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -492,6 +492,23 @@ config ARCH_HAVE_RESET bool default n +config ARCH_LDST_16BIT_NOT_ATOMIC + bool + default n + select ARCH_HAVE_8BIT_SERIAL_BUFSIZE + ---help--- + This configuration option denotes architecture incapable of loading + 16-bit values atomically, ie. in a single instruction. If this is set, + it means that the architecture needs two (or more) instructions + to perform such load AND that a interrupt service routine may run + between their execution. The same applies for stores. + + If set, any code running in non-interrupt context that reads 16-bit + variable that may be written in interrupt context must disable + interrupts or use another method that makes the read atomic. + (The same applies for non-interrupt context write and interrupt + context read.) + config ARCH_HAVE_TESTSET bool default n diff --git a/arch/avr/Kconfig b/arch/avr/Kconfig index 2c391c4a974..394194e792e 100644 --- a/arch/avr/Kconfig +++ b/arch/avr/Kconfig @@ -49,6 +49,7 @@ config ARCH_FAMILY_AVR bool default n select ARCH_HAVE_STACKCHECK + select ARCH_LDST_16BIT_NOT_ATOMIC config ARCH_FAMILY_AVR32 bool diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 43bf6c8e0e1..f6b9100a15d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -7,6 +7,15 @@ config ARCH_HAVE_SERIAL_TERMIOS bool default n +config ARCH_HAVE_8BIT_SERIAL_BUFSIZE + bool + default n + ---help--- + This is set by ARCH_LDST_16BIT_NOT_ATOMIC if the architecture does + not support atomic 16-bit load/store instructions. Receive and transmit + buffer sizes are stored in uint8_t (instead of int16_t) variable + in such case. + menuconfig SERIAL bool "Serial Driver Support" default y diff --git a/drivers/serial/Kconfig-usart b/drivers/serial/Kconfig-usart index e11045ad469..b14fb200293 100644 --- a/drivers/serial/Kconfig-usart +++ b/drivers/serial/Kconfig-usart @@ -58,18 +58,26 @@ menu "USART0 Configuration" config USART0_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART0_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART0_BAUD int "BAUD rate" default 115200 @@ -130,18 +138,26 @@ menu "USART1 Configuration" config USART1_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART1_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART1_BAUD int "BAUD rate" default 115200 @@ -202,18 +218,26 @@ menu "USART2 Configuration" config USART2_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART2_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART2_BAUD int "BAUD rate" default 115200 @@ -273,18 +297,26 @@ menu "USART3 Configuration" config USART3_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART3_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART3_BAUD int "BAUD rate" default 115200 @@ -345,18 +377,26 @@ menu "USART4 Configuration" config USART4_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART4_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART4_BAUD int "BAUD rate" default 115200 @@ -417,18 +457,26 @@ menu "USART5 Configuration" config USART5_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART5_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART5_BAUD int "BAUD rate" default 115200 @@ -489,18 +537,26 @@ menu "USART6 Configuration" config USART6_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART6_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART6_BAUD int "BAUD rate" default 115200 @@ -561,18 +617,26 @@ menu "USART7 Configuration" config USART7_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART7_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART7_BAUD int "BAUD rate" default 115200 @@ -633,18 +697,26 @@ menu "USART8 Configuration" config USART8_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART8_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART8_BAUD int "BAUD rate" default 115200 @@ -705,18 +777,26 @@ menu "USART9 Configuration" config USART9_RXBUFSIZE int "Receive buffer size" - default 256 + default 252 ---help--- Characters are buffered as they are received. This specifies the size of the receive buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART9_TXBUFSIZE int "Transmit buffer size" - default 256 + default 252 ---help--- Characters are buffered before being sent. This specifies the size of the transmit buffer. + Note that on architectures that are unable to load 16-bit + values atomically without eg. disabling interrupts, the size + of the buffer is stored in uint8_t variable. + config USART9_BAUD int "BAUD rate" default 115200 diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 9f25b2a04ca..f5aaec1953f 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -901,13 +901,13 @@ static ssize_t uart_readv(FAR struct file *filep, FAR struct uio *uio) #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS unsigned int nbuffered; unsigned int watermark; - int16_t head; + sbuf_size_t head; #endif irqstate_t flags; ssize_t recvd = 0; ssize_t buflen; bool echoed = false; - int16_t tail; + sbuf_size_t tail; char ch; int ret; @@ -954,9 +954,13 @@ static ssize_t uart_readv(FAR struct file *filep, FAR struct uio *uio) * index is only modified in this function. Therefore, no * special handshaking is required here. * - * The head and tail pointers are 16-bit values. The only time that - * the following could be unsafe is if the CPU made two non-atomic - * 8-bit accesses to obtain the 16-bit head index. + * The head and tail pointers values are sized based + * on the architecture. If the architecture reads 16-bit values + * atomically by nature, they are 16-bit values. On architectures + * where 16-bit access is split into two non-atomic 8-bit accesses, + * the pointers are 8-bit. + * + * The following code is therefore safe even with interrupts enabled. */ tail = rxbuf->tail; diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 8e916ecab1b..57eb81492f2 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -123,15 +123,29 @@ /* This structure defines one serial I/O buffer. * The serial infrastructure will initialize the 'sem' field but all other * fields must be initialized by the caller of uart_register(). + * + * Maximum buffer size is reduced to 8 bits on architectures where 16bit + * load takes two instructions and is therefore not atomic. This prevents + * corrupted read if the value is changed in an interrupt handler while + * being loaded in non-interrupt code. */ +#ifndef CONFIG_ARCH_LDST_16BIT_NOT_ATOMIC +typedef int16_t sbuf_size_t; +#else +typedef uint8_t sbuf_size_t; +#endif + struct uart_buffer_s { - mutex_t lock; /* Used to control exclusive access to the buffer */ - volatile int16_t head; /* Index to the head [IN] index in the buffer */ - volatile int16_t tail; /* Index to the tail [OUT] index in the buffer */ - int16_t size; /* The allocated size of the buffer */ - FAR char *buffer; /* Pointer to the allocated buffer memory */ + mutex_t lock; /* Used to control exclusive access + * to the buffer */ + volatile sbuf_size_t head; /* Index to the head [IN] index + * in the buffer */ + volatile sbuf_size_t tail; /* Index to the tail [OUT] index + * in the buffer */ + sbuf_size_t size; /* The allocated size of the buffer */ + FAR char *buffer; /* Pointer to the allocated buffer memory */ }; #if defined(CONFIG_SERIAL_RXDMA) || defined(CONFIG_SERIAL_TXDMA)