Bring up minos on the Allwinner D1 (Lichee RV) over FEL/xfel and add bidirectional UART so the console can read keystrokes, not just print. D1 / Lichee RV boot: - riscv arch.c: select UART0 base by -DBOARD_D1 (0x02500000 vs QEMU 0x10000000) and add a reg-shift abstraction (uart_rd/uart_wr): the D1's Synopsys DW-8250 uses 32-bit registers at 4-byte spacing, which is what made the first hardware boot silent. - d1_uart0_init(): gate/deassert UART0 clock, mux PB8/PB9 to UART0, set 115200 8N1, clear FIFOs, force non-loopback, drain stale RX. - link-d1.ld: link for DRAM at 0x40000000 (xfel exec target, M-mode). - Makefile: 'lichee' builds the flat binary; 'run-lichee' loads it via xfel (ddr d1 / write / exec). - arch_name() reports the concrete board. UART input (all archs): - arch contract gains arch_uart_getc() and arch_uart_rx_ready(), implemented for riscv, x86 (COM1) and arm (Cadence UART). - console gains getc() and drain_rx(). - kmain() runs an interactive echo loop (seed of the M3 console). Verified: echo works under QEMU on x86/riscv/arm, and on real D1 hardware (banner once, then live keystroke echo over UART0 @115200).
93 lines
2.1 KiB
C
93 lines
2.1 KiB
C
/* arch/x86/arch.c — i386 implementation of the arch contract.
|
|
* Uses the legacy 16550 UART on COM1 (port 0x3F8), which QEMU wires to
|
|
* the -nographic serial. Port-mapped I/O — the CISC flavor. */
|
|
#include "arch.h"
|
|
#include "console.h"
|
|
|
|
#define COM1 0x3F8
|
|
|
|
static inline void outb(unsigned short port, unsigned char val)
|
|
{
|
|
__asm__ volatile("outb %0, %1" :: "a"(val), "Nd"(port));
|
|
}
|
|
static inline unsigned char inb(unsigned short port)
|
|
{
|
|
unsigned char r;
|
|
__asm__ volatile("inb %1, %0" : "=a"(r) : "Nd"(port));
|
|
return r;
|
|
}
|
|
|
|
void arch_early_init(void)
|
|
{
|
|
/* Program COM1: 115200 8N1, FIFO on. */
|
|
outb(COM1 + 1, 0x00); /* disable interrupts */
|
|
outb(COM1 + 3, 0x80); /* DLAB on */
|
|
outb(COM1 + 0, 0x01); /* divisor lo -> 115200 */
|
|
outb(COM1 + 1, 0x00); /* divisor hi */
|
|
outb(COM1 + 3, 0x03); /* 8N1, DLAB off */
|
|
outb(COM1 + 2, 0xC7); /* enable+clear FIFO */
|
|
outb(COM1 + 4, 0x0B); /* RTS/DSR set */
|
|
}
|
|
|
|
void arch_uart_putc(char c)
|
|
{
|
|
while ((inb(COM1 + 5) & 0x20) == 0) /* wait THR empty */
|
|
;
|
|
outb(COM1, (unsigned char)c);
|
|
}
|
|
|
|
char arch_uart_getc(void)
|
|
{
|
|
while ((inb(COM1 + 5) & 0x01) == 0) /* wait RX data ready (LSR.DR) */
|
|
;
|
|
return (char)inb(COM1); /* read RBR */
|
|
}
|
|
|
|
int arch_uart_rx_ready(void)
|
|
{
|
|
return (inb(COM1 + 5) & 0x01) ? 1 : 0;
|
|
}
|
|
|
|
/* M1: minimal IDT. We load a valid-but-empty IDT so the CPU has a
|
|
* table; M2 will fill gates and handle the timer (IRQ0). */
|
|
struct idt_entry {
|
|
unsigned short off_lo, sel;
|
|
unsigned char zero, flags;
|
|
unsigned short off_hi;
|
|
} __attribute__((packed));
|
|
|
|
struct idt_ptr {
|
|
unsigned short limit;
|
|
unsigned int base;
|
|
} __attribute__((packed));
|
|
|
|
static struct idt_entry idt[256];
|
|
|
|
void arch_set_trap_vector(void)
|
|
{
|
|
struct idt_ptr ptr = { sizeof(idt) - 1, (unsigned int)(unsigned long)idt };
|
|
__asm__ volatile("lidt %0" :: "m"(ptr));
|
|
}
|
|
|
|
const char *arch_name(void)
|
|
{
|
|
return "x86 (i386, multiboot)";
|
|
}
|
|
|
|
const char *arch_cpu_mode(void)
|
|
{
|
|
/* CPL = low 2 bits of CS. 0 = ring0 (kernel), 3 = ring3 (user). */
|
|
unsigned short cs;
|
|
__asm__ volatile("mov %%cs, %0" : "=r"(cs));
|
|
switch (cs & 3) {
|
|
case 0: return "ring0";
|
|
case 3: return "ring3";
|
|
default: return "ring?";
|
|
}
|
|
}
|
|
|
|
void arch_halt(void)
|
|
{
|
|
for (;;)
|
|
__asm__ volatile("hlt");
|
|
}
|