diff --git a/platform/qemu-virt-arm/uart.c b/platform/qemu-virt-arm/uart.c index 530f1e64e..1c3a402ad 100644 --- a/platform/qemu-virt-arm/uart.c +++ b/platform/qemu-virt-arm/uart.c @@ -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; }