[platform][arm-qemu] fix issue with uart driver on KVM
The accessing method the compiler is emitting for the *REG32 macros on arm32 and arm64 is occasionally generating load/stores with writeback. Though this has worked before, it seems to be rejected with whatever combination of qemu + linux + hardware on this Raspberry Pi 5. Convert the register accessors to inline asm that uses basic load/store instructions, which is really the only correct thing to do now and in the long run. Add a TODO to move this to reg.h and start to revamp how registers are accessed across LK, but for now keep it just here to fix things.
This commit is contained in:
@@ -32,13 +32,55 @@
|
||||
#define UART_ICR (0x44)
|
||||
#define UART_DMACR (0x48)
|
||||
|
||||
#define UARTREG(base, reg) (*REG32((base) + (reg)))
|
||||
|
||||
#define RXBUF_SIZE 16
|
||||
#define NUM_UART 1
|
||||
|
||||
static cbuf_t uart_rx_buf[NUM_UART];
|
||||
|
||||
// ARM & ARM64 specific register accessors that use specific instructions to avoid
|
||||
// trapping into a virtual machine.
|
||||
// TODO: make a generic version of this since trapping nonstandard instructions is
|
||||
// a general problem while running under a VM.
|
||||
#if __aarch64__
|
||||
static inline uint32_t __arm64_read_reg32(uint32_t *addr) {
|
||||
uint32_t val;
|
||||
__asm__ volatile("ldr %w0, %1" : "=r"(val) : "m"(*addr) : "memory");
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void __arm64_write_reg32(uint32_t *addr, uint32_t val) {
|
||||
__asm__ volatile("str %w0, %1" :: "r"(val), "m"(*addr) : "memory");
|
||||
}
|
||||
#elif __arm__
|
||||
static inline uint32_t __arm64_read_reg32(uint32_t *addr) {
|
||||
uint32_t val;
|
||||
__asm__ volatile("ldr %0, %1" : "=r"(val) : "m"(*addr) : "memory");
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void __arm64_write_reg32(uint32_t *addr, uint32_t val) {
|
||||
__asm__ volatile("str %0, %1" :: "r"(val), "m"(*addr) : "memory");
|
||||
}
|
||||
#else
|
||||
#error need for this arch
|
||||
#endif
|
||||
|
||||
static inline void write_uart_reg(uintptr_t base, size_t offset, uint32_t val) {
|
||||
__arm64_write_reg32((uint32_t *)(base + offset), val);
|
||||
}
|
||||
|
||||
static inline uint32_t read_uart_reg(uintptr_t base, size_t offset) {
|
||||
return __arm64_read_reg32((uint32_t *)(base + offset));
|
||||
}
|
||||
|
||||
static inline void set_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
|
||||
write_uart_reg(base, offset, read_uart_reg(base, offset) | bits);
|
||||
}
|
||||
|
||||
static inline void clear_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
|
||||
write_uart_reg(base, offset, read_uart_reg(base, offset) & ~bits);
|
||||
}
|
||||
|
||||
static inline uintptr_t uart_to_ptr(unsigned int n) {
|
||||
switch (n) {
|
||||
default:
|
||||
@@ -53,27 +95,27 @@ static enum handler_return uart_irq(void *arg) {
|
||||
uintptr_t base = uart_to_ptr(port);
|
||||
|
||||
/* read interrupt status and mask */
|
||||
uint32_t isr = UARTREG(base, UART_TMIS);
|
||||
uint32_t isr = read_uart_reg(base, UART_TMIS);
|
||||
|
||||
if (isr & (1<<4)) { // rxmis
|
||||
cbuf_t *rxbuf = &uart_rx_buf[port];
|
||||
|
||||
/* while fifo is not empty, read chars out of it */
|
||||
while ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
|
||||
while ((read_uart_reg(base, UART_TFR) & (1<<4)) == 0) {
|
||||
#if CONSOLE_HAS_INPUT_BUFFER
|
||||
if (port == DEBUG_UART) {
|
||||
char c = UARTREG(base, UART_DR);
|
||||
char c = read_uart_reg(base, UART_DR);
|
||||
cbuf_write_char(&console_input_cbuf, c, false);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* if we're out of rx buffer, mask the irq instead of handling it */
|
||||
if (cbuf_space_avail(rxbuf) == 0) {
|
||||
UARTREG(base, UART_IMSC) &= ~(1<<4); // !rxim
|
||||
clear_uart_reg_bits(base, UART_IMSC, (1<<4)); // !rxim
|
||||
break;
|
||||
}
|
||||
|
||||
char c = UARTREG(base, UART_DR);
|
||||
char c = read_uart_reg(base, UART_DR);
|
||||
cbuf_write_char(rxbuf, c, false);
|
||||
}
|
||||
|
||||
@@ -95,16 +137,16 @@ void uart_init(void) {
|
||||
register_int_handler(UART0_INT + i, &uart_irq, (void *)i);
|
||||
|
||||
// clear all irqs
|
||||
UARTREG(base, UART_ICR) = 0x3ff;
|
||||
write_uart_reg(base, UART_ICR, 0x3ff);
|
||||
|
||||
// set fifo trigger level
|
||||
UARTREG(base, UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
|
||||
write_uart_reg(base, UART_IFLS, 0); // 1/8 rxfifo, 1/8 txfifo
|
||||
|
||||
// enable rx interrupt
|
||||
UARTREG(base, UART_IMSC) = (1<<4); // rxim
|
||||
write_uart_reg(base, UART_IMSC, 1<<4); // rxim
|
||||
|
||||
// enable receive
|
||||
UARTREG(base, UART_CR) |= (1<<9); // rxen
|
||||
set_uart_reg_bits(base, UART_CR, (1<<9)); // rxen
|
||||
|
||||
// enable interrupt
|
||||
unmask_interrupt(UART0_INT + i);
|
||||
@@ -113,7 +155,7 @@ void uart_init(void) {
|
||||
|
||||
void uart_init_early(void) {
|
||||
for (size_t i = 0; i < NUM_UART; i++) {
|
||||
UARTREG(uart_to_ptr(i), UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
|
||||
write_uart_reg(uart_to_ptr(i), UART_CR, (1<<8)|(1<<0)); // tx_enable, uarten
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +163,9 @@ int uart_putc(int port, char c) {
|
||||
uintptr_t base = uart_to_ptr(port);
|
||||
|
||||
/* spin while fifo is full */
|
||||
while (UARTREG(base, UART_TFR) & (1<<5))
|
||||
while (read_uart_reg(base, UART_TFR) & (1<<5))
|
||||
;
|
||||
UARTREG(base, UART_DR) = c;
|
||||
write_uart_reg(base, UART_DR, c);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -133,7 +175,7 @@ int uart_getc(int port, bool wait) {
|
||||
|
||||
char c;
|
||||
if (cbuf_read_char(rxbuf, &c, wait) == 1) {
|
||||
UARTREG(uart_to_ptr(port), UART_IMSC) = (1<<4); // rxim
|
||||
write_uart_reg(uart_to_ptr(port), UART_IMSC, (1<<4)); // rxim
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -145,9 +187,9 @@ int uart_pputc(int port, char c) {
|
||||
uintptr_t base = uart_to_ptr(port);
|
||||
|
||||
/* spin while fifo is full */
|
||||
while (UARTREG(base, UART_TFR) & (1<<5))
|
||||
while (read_uart_reg(base, UART_TFR) & (1<<5))
|
||||
;
|
||||
UARTREG(base, UART_DR) = c;
|
||||
write_uart_reg(base, UART_DR, c);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -155,8 +197,8 @@ int uart_pputc(int port, char c) {
|
||||
int uart_pgetc(int port) {
|
||||
uintptr_t base = uart_to_ptr(port);
|
||||
|
||||
if ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
|
||||
return UARTREG(base, UART_DR);
|
||||
if ((read_uart_reg(base, UART_TFR) & (1<<4)) == 0) {
|
||||
return read_uart_reg(base, UART_DR);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user