From 3dbd8c34176312b0bd3aba2309d284ac83181411 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 4 Jun 2023 15:13:43 +0300 Subject: visionfive2 boots + Make 8250 serial driver more generic + Still TODO: write a tutorial on how to boot on the visionfive2 --- common/debug.c | 133 +++++++++++++++++++++++++++----------------------------- common/initrd.c | 1 + common/pmem.c | 73 ++++++++++++++++++++++++------- 3 files changed, 123 insertions(+), 84 deletions(-) (limited to 'common') diff --git a/common/debug.c b/common/debug.c index 346934a..f3cacdf 100644 --- a/common/debug.c +++ b/common/debug.c @@ -20,18 +20,21 @@ /** Debug context structure. */ struct dbg_info { - /** Address of serial device in memory. */ - pm_t dbg_ptr; - + /** Address of serial device in virtual memory. Always access registers + * through this address rather than \p addr. */ + pm_t base; + /** Shift count between registers of serial device. */ + size_t shift; /** Type of serial device. */ enum serial_dev dev; + /** Physical address of serial device. */ + pm_t addr; }; /** Static debugging information. */ static struct dbg_info dbg_info = (struct dbg_info){ 0 }; /* forward declarations. */ -static void __setup_dbg(pm_t pt, enum serial_dev dev); static struct dbg_info __dbg_from_fdt(const void *fdt); void init_dbg(const void *fdt) @@ -41,51 +44,41 @@ void init_dbg(const void *fdt) void setup_dmap_dbg() { - __setup_dbg(dbg_info.dbg_ptr, dbg_info.dev); + /* noop with the new 8250 driver, though might still be useful in the + * future if I add in some other kind of simple uart */ } void setup_io_dbg(struct vmem *b) { - vm_t io_ptr = map_io_dbg(b); - __setup_dbg(io_ptr, dbg_info.dev); + dbg_info.base = map_io_dbg(b); } vm_t map_io_dbg(struct vmem *b) { - return setup_kernel_io(b, dbg_info.dbg_ptr); + return setup_kernel_io(b, dbg_info.addr); } +/** 8250 data register index. */ +#define UART_8250_DATA 0 +/** 8250 irq register idex. */ +#define UART_8250_IRQ 1 +/** 8250 ird_id register index. */ +#define UART_8250_IRQ_ID 2 +/** 8250 lcr register index. */ +#define UART_8250_LCR 3 +/** 8250 mcr register index. */ +#define UART_8250_MCR 4 +/** 8250 lsr register index. */ +#define UART_8250_LSR 5 +/** 8250 msr register index. */ +#define UART_8250_MSR 6 +/** 8250 scr register index. */ +#define UART_8250_SCR 7 + /* if there arises a need for more supported serial drivers, I should probably * try to implement some kind of basic driver subsystem, but this is good enough * for now. */ -/** 8250 and compatible serial drivers. */ -struct __packed uart_8250 { - /** Receiver buffer/transmitter holding register. */ - uint8_t data; - - /** Interrupt enable register. */ - uint8_t irq; - - /** Interrupt identity/FIFO control register. */ - uint8_t irq_id; - - /** Line control register. */ - uint8_t lcr; - - /** Modem control register. */ - uint8_t mcr; - - /** Line status register. */ - uint8_t lsr; - - /** Modem status register. */ - uint8_t msr; - - /** Scratch register. */ - uint8_t scr; -}; - /** Line status data ready. */ #define LSR_DR (1 << 0) @@ -111,17 +104,11 @@ struct __packed uart_8250 { #define LSR_ERR (1 << 7) /** - * Address of generic 8250 port. If other serial drivers are added, this should - * maybe be made a void *. No support for quirks at the moment. - */ -static struct uart_8250 *port = 0; - -/** - * Serial transmitter empty. + * 8250 transmitter empty. * * @return \c 0 if not empty, non-zero otherwise. */ -static int __serial_tx_empty() +static int __8250_tx_empty() { /** * @todo visionfive2 loops on lsr indefinitely, why? @@ -129,23 +116,43 @@ static int __serial_tx_empty() * meaning the registers are spaced apart more in memory than my naive * struct. * */ - return port->lsr & LSR_THRE; + volatile uint8_t *lsr = (uint8_t *)dbg_info.base + + (UART_8250_LSR << dbg_info.shift); + return (*lsr) & LSR_THRE; } /** - * Put character out onto serial lines. + * Put character out onto 8250 serial lines. * * @param c Character to output. */ -static void __putchar(char c) +static void __8250_putchar(char c) { - if (!port) + if (!dbg_info.base) return; - while (__serial_tx_empty() == 0) + while (__8250_tx_empty() == 0) ; - port->data = c; + volatile uint8_t *data = (uint8_t *)dbg_info.base + + (UART_8250_DATA << dbg_info.shift); + *data = c; +} + +/** + * Put one character out on the serial lines. + * Automatically handles newlines. + * + * @param c Character to put. + */ +static void __putchar(char c) +{ + if (c == '\n') + __putchar('\r'); + + switch (dbg_info.dev) { + case UART_8250: __8250_putchar(c); return; + } } /** @@ -200,28 +207,18 @@ static struct dbg_info __dbg_from_fdt(const void *fdt) /* get serial device address */ struct cell_info ci = get_reginfo(fdt, stdout); void *reg_ptr = (void *)fdt_getprop(fdt, stdout_offset, "reg", NULL); - pm_t dbg_ptr = (pm_t)fdt_load_int_ptr(ci.addr_cells, reg_ptr); - return (struct dbg_info){ dbg_ptr, dev }; -} - -/** - * Set static port. - * - * @param pt Address of memory mapped serial device. - * @param dev Chosen device. - */ -void __setup_dbg(vm_t pt, enum serial_dev dev) -{ - switch (dev) { - case UART_8250: - port = (struct uart_8250 *)pt; - break; - } + /* get serial device offset if present */ + size_t shift = 0; + void *shift_ptr = (void *)fdt_getprop(fdt, stdout_offset, "reg-shift", + NULL); + if (shift_ptr) + shift = (size_t)fdt_load_int32_ptr(shift_ptr); - /* in the future possibly configure the serial connection, though the - * defaults (set by U-boot) seem to work alright */ + /* while in direct map, base == addr, and this changes only when we jump + * into virtually mapped io */ + return (struct dbg_info){ dbg_ptr, shift, dev, dbg_ptr }; } /** Printf formatting left align flag. */ diff --git a/common/initrd.c b/common/initrd.c index 4bb25e9..caff8b3 100644 --- a/common/initrd.c +++ b/common/initrd.c @@ -11,6 +11,7 @@ #include #include #include +#include #include /** GNU cpio, POSIX 'newc' format header. */ diff --git a/common/pmem.c b/common/pmem.c index c817c5a..0fa647f 100644 --- a/common/pmem.c +++ b/common/pmem.c @@ -518,6 +518,11 @@ pm_t probe_pmap(size_t ram_size) */ static void __mark_area_used(pm_t base, pm_t top) { + if (top < base) { + bug("top < base: %lx < %lx\n", top, base); + return; + } + size_t area_left = top - base; pm_t runner = base; while (area_left >= BASE_PAGE_SIZE) { @@ -537,20 +542,29 @@ static void __mark_area_used(pm_t base, pm_t top) */ static void __mark_reserved_mem(void *fdt) { - int rmem_offset = fdt_path_offset(fdt, "/reserved-memory/mmode_resv0"); - struct cell_info ci = get_reginfo(fdt, "/reserved-memory/mmode_resv0"); - uint8_t *rmem_reg = - (uint8_t *)fdt_getprop(fdt, rmem_offset, "reg", NULL); - - pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg); - - if (ci.addr_cells == 2) - rmem_reg += sizeof(fdt64_t); - else - rmem_reg += sizeof(fdt32_t); - - pm_t top = (pm_t)fdt_load_int_ptr(ci.size_cells, rmem_reg) + base; - __mark_area_used((pm_t)__va(base), (pm_t)__va(top)); + int rmem_offset = fdt_path_offset(fdt, "/reserved-memory"); + struct cell_info ci = get_reginfo(fdt, "/reserved-memory"); + + int node = 0; + fdt_for_each_subnode(node, fdt, rmem_offset) { + uint8_t *rmem_reg = + (uint8_t *)fdt_getprop(fdt, node, "reg", NULL); + + pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg); + + if (ci.addr_cells == 2) + rmem_reg += sizeof(fdt64_t); + else + rmem_reg += sizeof(fdt32_t); + + /** @todo make sure the top of a reserved memory area doesn't go + * against our assumptions in FW_MAX_SIZE? */ + pm_t top = + (pm_t)fdt_load_int_ptr(ci.size_cells, rmem_reg) + base; + __mark_area_used((pm_t)__va(base), (pm_t)__va(top)); + info("marked [%lx - %lx] reserved\n", (pm_t)__va( + base), (pm_t)__va(top)); + } } /** @@ -603,6 +617,13 @@ static pm_t __get_fdtbase(void *fdt) void init_pmem(void *fdt) { + /** @todo should I keep the info outputs? I suppose it's nice to see + * if any assumption is being broken in the serial log, but in that case + * I should really try adding more of them to other parts of the + * codebase as well, the pmem subsystem isn't really especially complex. + */ + info("initializing pmem\n"); + size_t max_order = 0; size_t base_bits = 0; size_t bits[NUM_ORDERS] = { 0 }; @@ -612,17 +633,29 @@ void init_pmem(void *fdt) pm_t ram_size = __get_ramtop(fdt) - get_ram_base(); pm_t ram_base = (pm_t)__va(get_ram_base()); + info("using ram range [%lx - %lx]\n", + ram_base, ram_base + ram_size); + /** @todo could probably improve error messages on failing to get fdt * values */ + pm_t initrd_base = get_initrdbase(fdt); pm_t initrd_top = get_initrdtop(fdt); + info("found initrd at [%lx - %lx]\n", initrd_base, initrd_top); + pm_t fdt_top = __get_fdttop(fdt); + pm_t fdt_base = __get_fdtbase(fdt); + info("found fdt at [%lx - %lx]\n", fdt_base, fdt_top); /* find probably most suitable contiguous region of ram for our physical * ram map */ pm_t pmap_base = align_up(MAX(initrd_top, fdt_top), sizeof(int)); + info("choosing to place pmem map at %lx\n", pmap_base); size_t probe_size = probe_pmap(ram_size); + info("pmem map probe size returned %lu\n", probe_size); + size_t actual_size = populate_pmap(ram_base, ram_size, pmap_base); + info("pmem map actual size %lu\n", actual_size); if (probe_size != actual_size) { bug("probe_size (%#lx) != actual_size (%#lx)\n", probe_size, @@ -632,17 +665,25 @@ void init_pmem(void *fdt) /* mark init stack, this should be unmapped once we get to executing * processes */ __mark_area_used(VM_STACK_BASE, VM_STACK_TOP); + info("marked stack [%lx - %lx] used\n", VM_STACK_BASE, VM_STACK_TOP); /* mark kernel */ /* this could be made more explicit, I suppose. */ __mark_area_used(VM_KERN, VM_KERN + PM_KERN_SIZE); + info("marked kernel [%lx - %lx] used\n", VM_KERN, + VM_KERN + PM_KERN_SIZE); /* mark fdt and initrd */ - __mark_area_used(get_initrdbase(fdt), initrd_top); - __mark_area_used(__get_fdtbase(fdt), fdt_top); + __mark_area_used(initrd_base, initrd_top); + info("marked initrd [%lx - %lx] used\n", initrd_base, initrd_top); + + __mark_area_used(fdt_base, fdt_top); + info("marked fdt [%lx - %lx] used\n", fdt_base, fdt_top); /* mark pmap */ __mark_area_used(pmap_base, pmap_base + actual_size); + info("marked pmap [%lx - %lx] used\n", pmap_base, + pmap_base + actual_size); /* mark reserved mem */ __mark_reserved_mem(fdt); -- cgit v1.3