aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/debug.c133
-rw-r--r--common/initrd.c1
-rw-r--r--common/pmem.c67
3 files changed, 120 insertions, 81 deletions
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 <kmi/string.h>
#include <kmi/utils.h>
#include <kmi/attrs.h>
+#include <kmi/debug.h>
#include <libfdt.h>
/** 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);
+ int rmem_offset = fdt_path_offset(fdt, "/reserved-memory");
+ struct cell_info ci = get_reginfo(fdt, "/reserved-memory");
- pm_t base = (pm_t)fdt_load_int_ptr(ci.addr_cells, rmem_reg);
+ int node = 0;
+ fdt_for_each_subnode(node, fdt, rmem_offset) {
+ uint8_t *rmem_reg =
+ (uint8_t *)fdt_getprop(fdt, node, "reg", NULL);
- if (ci.addr_cells == 2)
- rmem_reg += sizeof(fdt64_t);
- else
- rmem_reg += sizeof(fdt32_t);
+ 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));
+ /** @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);