hv_vuart: Implement RX & enough to support Linux

This works to get to a serial shell on Linux, and to use m1n1-as-LV1
proxyclient with M1N1DEVICE=/dev/m1n1-sec

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-08-23 17:02:28 +09:00
parent 4b4ae3ab90
commit 017f050fff
8 changed files with 101 additions and 10 deletions

View file

@ -983,7 +983,8 @@ class HV(Reloadable):
def map_vuart(self):
zone = irange(0x2_35200000, 0x4000)
self.p.hv_map_vuart(0x2_35200000, getattr(IODEV, self.iodev.name + "_SEC"))
irq = 605
self.p.hv_map_vuart(0x2_35200000, irq, getattr(IODEV, self.iodev.name + "_SEC"))
self.add_tracer(zone, "VUART", TraceMode.RESERVED)
def map_essential(self):

View file

@ -951,8 +951,8 @@ class M1N1Proxy(Reloadable):
return self.request(self.P_HV_TRANSLATE, addr, s1, w)
def hv_pt_walk(self, addr):
return self.request(self.P_HV_PT_WALK, addr)
def hv_map_vuart(self, base, iodev):
return self.request(self.P_HV_MAP_VUART, base, iodev)
def hv_map_vuart(self, base, irq, iodev):
return self.request(self.P_HV_MAP_VUART, base, irq, iodev)
def hv_trace_irq(self, evt_type, num, count, flags):
return self.request(self.P_HV_TRACE_IRQ, evt_type, num, count, flags)

View file

@ -147,4 +147,5 @@ void hv_tick(u64 *regs)
iodev_handle_events(uartproxy_iodev);
if (iodev_can_read(uartproxy_iodev))
hv_exc_proxy(regs, START_HV, HV_USER_INTERRUPT, NULL);
hv_vuart_poll();
}

View file

@ -59,7 +59,8 @@ bool hv_pa_rw(u64 addr, u64 *val, bool write, int width);
bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags);
/* Virtual peripherals */
void hv_map_vuart(u64 base, iodev_id_t iodev);
void hv_vuart_poll(void);
void hv_map_vuart(u64 base, int irq, iodev_id_t iodev);
/* Exceptions */
void hv_exc_proxy(u64 *regs, uartproxy_boot_reason_t reason, uartproxy_exc_code_t type,

View file

@ -1,42 +1,123 @@
/* SPDX-License-Identifier: MIT */
#include "hv.h"
#include "aic.h"
#include "uart.h"
#include "uart_regs.h"
static iodev_id_t vuart_iodev;
bool handle_vuart(u64 addr, u64 *val, bool write, int width)
bool active = false;
u32 ucon = 0;
u32 utrstat = 0;
u32 ufstat = 0;
int vuart_irq = 0;
static void update_irq(void)
{
ssize_t rx_queued;
iodev_handle_events(vuart_iodev);
utrstat |= UTRSTAT_TXBE | UTRSTAT_TXE;
utrstat &= ~UTRSTAT_RXD;
if ((rx_queued = iodev_can_read(vuart_iodev))) {
utrstat |= UTRSTAT_RXD;
if (rx_queued > 15)
ufstat = FIELD_PREP(UFSTAT_RXCNT, 15) | UFSTAT_RXFULL;
else
ufstat = FIELD_PREP(UFSTAT_RXCNT, rx_queued);
if (FIELD_GET(UCON_RXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_RXTO_ENA) {
utrstat |= UTRSTAT_RXTO;
}
}
if (FIELD_GET(UCON_TXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_TXTHRESH_ENA) {
utrstat |= UTRSTAT_TXTHRESH;
}
if (vuart_irq) {
uart_clear_irqs();
if (utrstat & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO)) {
aic_set_sw(vuart_irq, true);
} else {
aic_set_sw(vuart_irq, false);
}
}
// printf("HV: vuart UTRSTAT=0x%x UFSTAT=0x%x UCON=0x%x\n", utrstat, ufstat, ucon);
}
static bool handle_vuart(u64 addr, u64 *val, bool write, int width)
{
UNUSED(width);
addr &= 0xfff;
update_irq();
if (write) {
// printf("HV: vuart W 0x%lx <- 0x%lx (%d)\n", addr, *val, width);
switch (addr) {
case UCON:
ucon = *val;
break;
case UTXH: {
uint8_t b = *val;
if (iodev_can_write(vuart_iodev))
iodev_write(vuart_iodev, &b, 1);
break;
}
case UTRSTAT:
utrstat &= ~(*val & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO));
break;
}
} else {
switch (addr) {
case UCON:
*val = ucon;
break;
case URXH:
if (iodev_can_read(vuart_iodev)) {
uint8_t c;
iodev_read(vuart_iodev, &c, 1);
*val = c;
} else {
*val = 0;
}
break;
case UTRSTAT:
*val = 0x06;
*val = utrstat;
break;
case UFSTAT:
*val = ufstat;
break;
default:
*val = 0;
break;
}
// printf("HV: vuart R 0x%lx -> 0x%lx (%d)\n", addr, *val, width);
}
// printf("HV: vuart(0x%lx, 0x%lx, %d, %d)\n", addr, *val, write, width);
return true;
}
void hv_map_vuart(u64 base, iodev_id_t iodev)
void hv_vuart_poll(void)
{
if (!active)
return;
update_irq();
}
void hv_map_vuart(u64 base, int irq, iodev_id_t iodev)
{
hv_map_hook(base, handle_vuart, 0x1000);
vuart_iodev = iodev;
vuart_irq = irq;
active = true;
}

View file

@ -429,7 +429,7 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
reply->retval = hv_pt_walk(request->args[0]);
break;
case P_HV_MAP_VUART:
hv_map_vuart(request->args[0], request->args[1]);
hv_map_vuart(request->args[0], request->args[1], request->args[2]);
break;
case P_HV_TRACE_IRQ:
reply->retval = hv_trace_irq(request->args[0], request->args[1], request->args[2],

View file

@ -90,6 +90,11 @@ void uart_flush(void)
;
}
void uart_clear_irqs(void)
{
write32(UART_BASE + UTRSTAT, UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO);
}
int uart_printf(const char *fmt, ...)
{
va_list args;

View file

@ -22,6 +22,8 @@ void uart_setbaud(int baudrate);
void uart_flush(void);
void uart_clear_irqs(void);
int uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
#endif