UART: Always allocate UART objects in the long-lived pool

Particularly when they have buffers that are written via IRQ or DMA,
UART objects do not relocate gracefully.  If such an object is
relocated to the long-lived pool after its original creation, the
IRQ or DMA will write to an unexpected location within the Python
heap, leading to a variety of symptoms.  The most frequent symptom
is inability to read from the UART.

Consider the particular case of atmel-samd: usart_uart_obj_t
contains a usart_async_descriptor contains a _usart_async_device.
In _sercom_init_irq_param the address of this contained
_usart_async_device is assigned to a global array
sercom_to_sercom_dev which is later used from the interrupt context
_sercom_usart_interrupt_handler to store the received data in the
right ring buffer.

When the UART object is relocated to the long-lived heap, there's no
mechanism to re-point these internal pointers, so instead take the
cowardly way and allocate the UART object as long-lived.

Happily, almost all UART objects are likely to be long-lived, so
this is unlikely to have a negative effect on memory usage or heap

Closes: #1056
Jeff Epler 5 years ago
parent 2e80f37709
commit e1b4e9b7c7

@ -66,7 +66,11 @@ extern const busio_uart_parity_obj_t busio_uart_parity_odd_obj;
STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true);
busio_uart_obj_t *self = m_new_obj(busio_uart_obj_t);
// Always initially allocate the UART object within the long-lived heap.
// This is needed to avoid crashes with certain UART implementations which
// cannot accomodate being moved after creation. (See
busio_uart_obj_t *self = m_new_ll_obj(busio_uart_obj_t);
self->base.type = &busio_uart_type;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);