On Fri, Mar 04, 2016 at 08:08:36PM +0100, Patrick Wildt wrote:
> On Fri, Mar 04, 2016 at 09:38:18AM -0500, Brandon Mercer wrote:
> > On Fri, Mar 04, 2016 at 03:15:23PM +0100, Patrick Wildt wrote:
> > > Hi,
> > >
> > > this diff makes armv7 map the FDT, if available, and uses it to read
> > > information about the machine's available memory and bootargs.
> > >
> > > I'd like to get some opinions about the way I have implemented some
> > > stuff. For instance, I need the size of the FDT so I can properly
> > > copy it. Does it make sense to implement fdt_get_size()? Is there
> > > another, preferred way?
> > >
> > > Additionally, reading memory information, like where a node is mapped,
> > > is a bit more complicated than just reading two values. Especially if
> > > there are translation ranges on a node's parent. I also have working
> > > code or that, but I skipped "ranges" to not bloat the diff any further.
> >
> > If memory serves me correctly we need ranges right out of the gate for
> > the exynos boards I have, but those aren't important to anyone except
> > for me so I'm ok with waiting a short while.
>
> Yes, I stumbled upon that on the raspberry pi. The default device tree
> delivered by the raspberry pi foundation makes use of that.
>
> Excerpt:
>
> / {
> #address-cells = <0x1>;
> #size-cells = <0x1>;
> interrupt-parent = <0x1>;
> compatible = "brcm,bcm2710", "brcm,bcm2709";
> model = "Raspberry Pi 3 Model B";
> [...]
> soc {
> compatible = "simple-bus";
> #address-cells = <0x1>;
> #size-cells = <0x1>;
> ranges = <0x7e000000 0x3f000000 0x1000000>;
> [...]
> interrupt-controller@7e00b200 {
> compatible = "brcm,bcm2708-armctrl-ic";
> reg = <0x7e00b200 0x200>;
> interrupt-controller;
> #interrupt-cells = <0x2>;
> linux,phandle = <0x1>;
> phandle = <0x1>;
> };
Yes, this is needed as well. However I was commenting on reserved memory
regions. I don't want to see this turn into a bikeshed session so I
suggest we start with the initial diff, and add these additional
features soon after.
> diff --git sys/dev/ofw/fdt.c sys/dev/ofw/fdt.c
> index 18f6077..0993c92 100644
> --- sys/dev/ofw/fdt.c
> +++ sys/dev/ofw/fdt.c
> @@ -33,6 +33,7 @@ void *skip_node_name(u_int32_t *);
> void *fdt_parent_node_recurse(void *, void *);
> int fdt_node_property_int(void *, char *, int *);
> int fdt_node_property_ints(void *, char *, int *, int);
> +int fdt_translate_memory_address(void *, struct fdt_memory *);
> #ifdef DEBUG
> void fdt_print_node_recurse(void *, int);
> #endif
> @@ -361,10 +362,94 @@ fdt_parent_node(void *node)
> if (!tree_inited)
> return NULL;
>
> + if (node == pnode)
> + return NULL;
> +
> return fdt_parent_node_recurse(pnode, node);
> }
>
> /*
> + * Translate memory address depending on parent's range.
> + */
> +int
> +fdt_translate_memory_address(void *node, struct fdt_memory *mem)
> +{
> + void *parent;
> + int pac, psc, ac, sc, ret, rlen, rone, *range;
> + uint64_t from, to, size;
> +
> + if (node == NULL || mem == NULL)
> + return 1;
> +
> + parent = fdt_parent_node(node);
> + if (parent == NULL)
> + return 0;
> +
> + /* Empty ranges, 1:1 mapping. No ranges, translation barrier. */
> + rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
> + if (range == NULL)
> + return 0;
> + if (rlen <= 0)
> + return fdt_translate_memory_address(parent, mem);
> +
> + /* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
> + ret = fdt_node_property_int(parent, "#address-cells", &pac);
> + if (ret != 1 || pac <= 0 || pac > 2)
> + return 1;
> +
> + /* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
> + ret = fdt_node_property_int(parent, "#size-cells", &psc);
> + if (ret != 1 || psc <= 0 || psc > 2)
> + return 1;
> +
> + /* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
> + ret = fdt_node_property_int(node, "#address-cells", &ac);
> + if (ret <= 0)
> + ac = pac;
> + else if (ret > 1 || ac <= 0 || ac > 2)
> + return 1;
> +
> + /* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
> + ret = fdt_node_property_int(node, "#size-cells", &sc);
> + if (ret <= 0)
> + sc = psc;
> + else if (ret > 1 || sc <= 0 || sc > 2)
> + return 1;
> +
> + /* Must have at least one range. */
> + rone = pac + ac + sc;
> + if (rlen < rone)
> + return 1;
> +
> + /* For each range. */
> + for (; rlen >= rone; rlen -= rone, range += rone) {
> + /* Extract from and size, so we can see if we fit. */
> + from = betoh32(range[0]);
> + if (ac == 2)
> + from = (from << 32) + betoh32(range[1]);
> + size = betoh32(range[ac + pac]);
> + if (sc == 2)
> + size = (size << 32) + betoh32(range[ac + pac + 1]);
> +
> + /* Try next, if we're not in the range. */
> + if (mem->addr < from || (mem->addr + mem->size) > (from + size))
> + continue;
> +
> + /* All good, extract to address and translate. */
> + to = betoh32(range[ac]);
> + if (pac == 2)
> + to = (to << 32) + betoh32(range[ac + 1]);
> +
> + mem->addr -= from;
> + mem->addr += to;
> + return fdt_translate_memory_address(parent, mem);
> + }
> +
> + /* Range must have been in there. */
> + return 1;
> +}
> +
> +/*
> * Parse the memory address and size of a node.
> */
> int
> @@ -404,10 +489,7 @@ fdt_get_memory_address(void *node, int idx, struct
> fdt_memory *mem)
> if (sc == 2)
> mem->size = (mem->size << 32) + betoh32(in[off + ac + 1]);
>
> - /* TODO: translate memory address in ranges */
> - //return fdt_translate_memory_address(parent, mem);
> -
> - return 0;
> + return fdt_translate_memory_address(parent, mem);
> }
>
> #ifdef DEBUG
>
>
> > >
> > > I would like to extend the fdt_* API and add helpers to the code, so
> > > that it's easier to use the device tree. Is this the correct way?
> > > Should I rather implement some OF_* API?
> >
> > My initial thought is that it makes sense to extend fdt for this.
> >
> > I have not tested this diff, but I like the direction that this is
> > going.
> > Cheers,
> > bmercer
> > >
> > > Patrick
> > >
> > > diff --git sys/arch/armv7/armv7/armv7_machdep.c
> > > sys/arch/armv7/armv7/armv7_machdep.c
> > > index 11bea90..0a067c2 100644
> > > --- sys/arch/armv7/armv7/armv7_machdep.c
> > > +++ sys/arch/armv7/armv7/armv7_machdep.c
> > > @@ -130,6 +130,7 @@
> > > #include <armv7/armv7/armv7_machdep.h>
> > >
> > > #include <dev/cons.h>
> > > +#include <dev/ofw/fdt.h>
> > >
> > > #include <net/if.h>
> > >
> > > @@ -387,8 +388,10 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > int loop, loop1, i, physsegs;
> > > u_int l1pagetable;
> > > pv_addr_t kernel_l1pt;
> > > + pv_addr_t fdt;
> > > paddr_t memstart;
> > > psize_t memsize;
> > > + void *config;
> > > extern uint32_t esym; /* &_end if no symbols are loaded */
> > >
> > > /* early bus_space_map support */
> > > @@ -423,6 +426,49 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > armv7_a4x_bs_tag.bs_map = bootstrap_bs_map;
> > > tmp_bs_tag.bs_map = bootstrap_bs_map;
> > >
> > > + /*
> > > + * Now, map the bootconfig/FDT area.
> > > + *
> > > + * As we don't know the size of a possible FDT, map the size of a
> > > + * typical bootstrap bs map. The FDT might not be aligned, so this
> > > + * might take up to two L1_S_SIZEd mappings. In the unlikely case
> > > + * that the FDT is bigger than L1_S_SIZE (0x00100000), we need to
> > > + * remap it.
> > > + *
> > > + * XXX: There's (currently) no way to unmap a bootstrap mapping, so
> > > + * we might lose a bit of the bootstrap address space.
> > > + */
> > > + bootstrap_bs_map(NULL, (bus_addr_t)arg2, L1_S_SIZE, 0,
> > > + (bus_space_handle_t *)&config);
> > > + if (fdt_init(config) && fdt_get_size(config) != 0) {
> > > + uint32_t size = fdt_get_size(config);
> > > + if (size > L1_S_SIZE)
> > > + bootstrap_bs_map(NULL, (bus_addr_t)arg2, size, 0,
> > > + (bus_space_handle_t *)&config);
> > > + }
> > > +
> > > + if (fdt_init(config) && fdt_get_size(config) != 0) {
> > > + struct fdt_memory mem;
> > > + void *node;
> > > +
> > > + node = fdt_find_node("/memory");
> > > + if (node == NULL || fdt_get_memory_address(node, 0, &mem))
> > > + panic("initarm: no memory specificed");
> > > +
> > > + memstart = mem.addr;
> > > + memsize = mem.size;
> > > + physical_start = mem.addr;
> > > + physical_end = MIN(mem.addr + mem.size, (paddr_t)-PAGE_SIZE);
> > > +
> > > + node = fdt_find_node("/chosen");
> > > + if (node != NULL) {
> > > + char *bootargs;
> > > + if (fdt_node_property(node, "bootargs", &bootargs))
> > > + process_kernel_args(bootargs);
> > > + }
> > > + }
> > > +
> > > + /* XXX: Use FDT information. */
> > > platform_init();
> > > platform_disable_l2_if_needed();
> > >
> > > @@ -433,41 +479,36 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > printf("\n%s booting ...\n", platform_boot_name());
> > >
> > > printf("arg0 %p arg1 %p arg2 %p\n", arg0, arg1, arg2);
> > > - parse_uboot_tags(arg2);
> > >
> > > - /*
> > > - * Examine the boot args string for options we need to know about
> > > - * now.
> > > - */
> > > - process_kernel_args(bootconfig.bootstring);
> > > + if (fdt_get_size(config) == 0) {
> > > + parse_uboot_tags(config);
> > > +
> > > + /*
> > > + * Examine the boot args string for options we need to know
> > > about
> > > + * now.
> > > + */
> > > + process_kernel_args(bootconfig.bootstring);
> > > +
> > > + /* normally u-boot will set up bootconfig.dramblocks */
> > > + bootconfig_dram(&bootconfig, &memstart, &memsize);
> > > +
> > > + /*
> > > + * Set up the variables that define the availablilty of
> > > + * physical memory.
> > > + *
> > > + * XXX pmap_bootstrap() needs an enema.
> > > + */
> > > + physical_start = bootconfig.dram[0].address;
> > > + physical_end = MIN((uint64_t)physical_start +
> > > + (bootconfig.dram[0].pages * PAGE_SIZE),
> > > (paddr_t)-PAGE_SIZE);
> > > + }
> > > +
> > > #ifdef RAMDISK_HOOKS
> > > - boothowto |= RB_DFLTROOT;
> > > + boothowto |= RB_DFLTROOT;
> > > #endif /* RAMDISK_HOOKS */
> > >
> > > - /* normally u-boot will set up bootconfig.dramblocks */
> > > - bootconfig_dram(&bootconfig, &memstart, &memsize);
> > > -
> > > - /*
> > > - * Set up the variables that define the availablilty of
> > > - * physical memory. For now, we're going to set
> > > - * physical_freestart to 0xa0200000 (where the kernel
> > > - * was loaded), and allocate the memory we need downwards.
> > > - * If we get too close to the page tables that RedBoot
> > > - * set up, we will panic. We will update physical_freestart
> > > - * and physical_freeend later to reflect what pmap_bootstrap()
> > > - * wants to see.
> > > - *
> > > - * XXX pmap_bootstrap() needs an enema.
> > > - */
> > > - physical_start = bootconfig.dram[0].address;
> > > - physical_end = MIN((uint64_t)physical_start +
> > > - (bootconfig.dram[0].pages * PAGE_SIZE), (paddr_t)-PAGE_SIZE);
> > > -
> > > - {
> > > - physical_freestart = (((unsigned long)esym - KERNEL_TEXT_BASE
> > > +0xfff) & ~0xfff) + memstart;
> > > - physical_freeend = MIN((uint64_t)memstart+memsize,
> > > - (paddr_t)-PAGE_SIZE);
> > > - }
> > > + physical_freestart = (((unsigned long)esym - KERNEL_TEXT_BASE +0xfff) &
> > > ~0xfff) + memstart;
> > > + physical_freeend = MIN((uint64_t)memstart+memsize, (paddr_t)-PAGE_SIZE);
> > >
> > > physmem = (physical_end - physical_start) / PAGE_SIZE;
> > >
> > > @@ -566,6 +607,15 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > #endif
> > >
> > > /*
> > > + * Allocate pages for an FDT copy.
> > > + */
> > > + if (fdt_get_size(config) != 0) {
> > > + uint32_t size = fdt_get_size(config);
> > > + valloc_pages(fdt, round_page(size) / PAGE_SIZE);
> > > + memcpy((void *)fdt.pv_pa, config, size);
> > > + }
> > > +
> > > + /*
> > > * XXX Defer this to later so that we can reclaim the memory
> > > * XXX used by the RedBoot page tables.
> > > */
> > > @@ -656,6 +706,12 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > pmap_map_entry(l1pagetable, vector_page, systempage.pv_pa,
> > > PROT_READ | PROT_WRITE | PROT_EXEC, PTE_CACHE);
> > >
> > > + /* Map the FDT. */
> > > + if (fdt.pv_va && fdt.pv_pa)
> > > + pmap_map_chunk(l1pagetable, fdt.pv_va, fdt.pv_pa,
> > > + round_page(fdt_get_size((void *)fdt.pv_pa)),
> > > + PROT_READ | PROT_WRITE, PTE_CACHE);
> > > +
> > > /*
> > > * map integrated peripherals at same address in l1pagetable
> > > * so that we can continue to use console.
> > > @@ -716,6 +772,10 @@ initarm(void *arg0, void *arg1, void *arg2)
> > > prefetch_abort_handler_address = (u_int)prefetch_abort_handler;
> > > undefined_handler_address = (u_int)undefinedinstruction_bounce;
> > >
> > > + /* Now we can reinit the FDT, using the virtual address. */
> > > + if (fdt.pv_va && fdt.pv_pa)
> > > + fdt_init((void *)fdt.pv_va);
> > > +
> > > /* Initialise the undefined instruction handlers */
> > > #ifdef VERBOSE_INIT_ARM
> > > printf("undefined ");
> > > diff --git sys/arch/armv7/conf/files.armv7 sys/arch/armv7/conf/files.armv7
> > > index 84c6064..3136ad0 100644
> > > --- sys/arch/armv7/conf/files.armv7
> > > +++ sys/arch/armv7/conf/files.armv7
> > > @@ -10,6 +10,7 @@ major {rd = 18}
> > >
> > > define fdt {}
> > > file arch/armv7/fdt/fdt_machdep.c fdt needs-flag
> > > +file dev/ofw/fdt.c
> > >
> > > file arch/arm/arm/conf.c
> > >
> > > diff --git sys/dev/ofw/fdt.c sys/dev/ofw/fdt.c
> > > index 6111faa..18f6077 100644
> > > --- sys/dev/ofw/fdt.c
> > > +++ sys/dev/ofw/fdt.c
> > > @@ -31,6 +31,8 @@ void *skip_property(u_int32_t *);
> > > void *skip_props(u_int32_t *);
> > > void *skip_node_name(u_int32_t *);
> > > void *fdt_parent_node_recurse(void *, void *);
> > > +int fdt_node_property_int(void *, char *, int *);
> > > +int fdt_node_property_ints(void *, char *, int *, int);
> > > #ifdef DEBUG
> > > void fdt_print_node_recurse(void *, int);
> > > #endif
> > > @@ -95,6 +97,21 @@ fdt_init(void *fdt)
> > > return version;
> > > }
> > >
> > > + /*
> > > + * Return the size of the FDT.
> > > + */
> > > +size_t
> > > +fdt_get_size(void *fdt)
> > > +{
> > > + if (!fdt)
> > > + return 0;
> > > +
> > > + if (!fdt_check_head(fdt))
> > > + return 0;
> > > +
> > > + return betoh32(((struct fdt_head *)fdt)->fh_size);
> > > +}
> > > +
> > > /*
> > > * Retrieve string pointer from strings table.
> > > */
> > > @@ -172,6 +189,35 @@ fdt_node_property(void *node, char *name, char **out)
> > > }
> > >
> > > /*
> > > + * Retrieves node property as integers and puts them in the given
> > > + * integer array.
> > > + */
> > > +int
> > > +fdt_node_property_ints(void *node, char *name, int *out, int outlen)
> > > +{
> > > + int *data;
> > > + int i, inlen;
> > > +
> > > + inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
> > > + if (inlen <= 0)
> > > + return -1;
> > > +
> > > + for (i = 0; i < inlen && i < outlen; i++)
> > > + out[i] = betoh32(data[i]);
> > > +
> > > + return i;
> > > +}
> > > +
> > > +/*
> > > + * Retrieves node property as an integer.
> > > + */
> > > +int
> > > +fdt_node_property_int(void *node, char *name, int *out)
> > > +{
> > > + return fdt_node_property_ints(node, name, out, 1);
> > > +}
> > > +
> > > +/*
> > > * Retrieves next node, skipping all the children nodes of the pointed
> > > node
> > > * if passed 0 wil return first node of the tree (root)
> > > */
> > > @@ -318,6 +364,52 @@ fdt_parent_node(void *node)
> > > return fdt_parent_node_recurse(pnode, node);
> > > }
> > >
> > > +/*
> > > + * Parse the memory address and size of a node.
> > > + */
> > > +int
> > > +fdt_get_memory_address(void *node, int idx, struct fdt_memory *mem)
> > > +{
> > > + void *parent;
> > > + int ac, sc, off, ret, *in, inlen;
> > > +
> > > + if (node == NULL)
> > > + return 1;
> > > +
> > > + parent = fdt_parent_node(node);
> > > + if (parent == NULL)
> > > + return 1;
> > > +
> > > + /* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
> > > + ret = fdt_node_property_int(parent, "#address-cells", &ac);
> > > + if (ret != 1 || ac <= 0 || ac > 2)
> > > + return 1;
> > > +
> > > + /* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
> > > + ret = fdt_node_property_int(parent, "#size-cells", &sc);
> > > + if (ret != 1 || sc <= 0 || sc > 2)
> > > + return 1;
> > > +
> > > + inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
> > > + if (inlen < ((idx + 1) * (ac + sc)))
> > > + return 1;
> > > +
> > > + off = idx * (ac + sc);
> > > +
> > > + mem->addr = betoh32(in[off]);
> > > + if (ac == 2)
> > > + mem->addr = (mem->addr << 32) + betoh32(in[off + 1]);
> > > +
> > > + mem->size = betoh32(in[off + ac]);
> > > + if (sc == 2)
> > > + mem->size = (mem->size << 32) + betoh32(in[off + ac + 1]);
> > > +
> > > + /* TODO: translate memory address in ranges */
> > > + //return fdt_translate_memory_address(parent, mem);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > #ifdef DEBUG
> > > /*
> > > * Debug methods for printing whole tree, particular odes and properies
> > > diff --git sys/dev/ofw/fdt.h sys/dev/ofw/fdt.h
> > > index 6a21239..ecb8bed 100644
> > > --- sys/dev/ofw/fdt.h
> > > +++ sys/dev/ofw/fdt.h
> > > @@ -38,6 +38,11 @@ struct fdt {
> > > int strings_size;
> > > };
> > >
> > > +struct fdt_memory {
> > > + uint64_t addr;
> > > + uint64_t size;
> > > +};
> > > +
> > > #define FDT_MAGIC 0xd00dfeed
> > > #define FDT_NODE_BEGIN 0x01
> > > #define FDT_NODE_END 0x02
> > > @@ -48,12 +53,14 @@ struct fdt {
> > > #define FDT_CODE_VERSION 0x11
> > >
> > > int fdt_init(void *);
> > > +size_t fdt_get_size(void *);
> > > void *fdt_next_node(void *);
> > > void *fdt_child_node(void *);
> > > char *fdt_node_name(void *);
> > > void *fdt_find_node(char *);
> > > int fdt_node_property(void *, char *, char **);
> > > void *fdt_parent_node(void *);
> > > +int fdt_get_memory_address(void *, int, struct fdt_memory *);
> > > #ifdef DEBUG
> > > void *fdt_print_property(void *, int);
> > > void fdt_print_node(void *, int);
> > >
> >