From 05b6b11ecfce20406ad53d363e39bc8311dc3290 Mon Sep 17 00:00:00 2001
From: Julien Grall <julien.grall@linaro.org>
Date: Thu, 10 Apr 2014 16:59:14 +0100
Subject: [PATCH 46/48] arm: Add FDT support for Linux Boot ABI

---
 sys/arm/arm/machdep.c     | 57 ++++++++++++++++++++++++++++++++++++++++-------
 sys/arm/include/machdep.h |  3 ++-
 2 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c
index 739840c..37281c9 100644
--- a/sys/arm/arm/machdep.c
+++ b/sys/arm/arm/machdep.c
@@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/bio.h>
 #include <sys/buf.h>
 #include <sys/bus.h>
-#include <sys/cons.h>
+ #include <sys/cons.h>
 #include <sys/cpu.h>
 #include <sys/exec.h>
 #include <sys/imgact.h>
@@ -108,6 +108,7 @@ __FBSDID("$FreeBSD$");
 
 #ifdef FDT
 #include <dev/fdt/fdt_common.h>
+#include <libfdt.h>
 #include <dev/ofw/openfirm.h>
 #endif
 
@@ -167,6 +168,7 @@ char atags[LBABI_MAX_COMMAND_LINE * 2];
 uint32_t memstart[LBABI_MAX_BANKS];
 uint32_t memsize[LBABI_MAX_BANKS];
 uint32_t membanks;
+
 #endif
 
 static uint32_t board_revision;
@@ -784,14 +786,15 @@ makectx(struct trapframe *tf, struct pcb *pcb)
  * Fake up a boot descriptor table
  */
 vm_offset_t
-fake_preload_metadata(struct arm_boot_params *abp __unused)
+fake_preload_metadata(struct arm_boot_params *abp __unused,
+		      vm_offset_t fdt_addr)
 {
 #ifdef DDB
 	vm_offset_t zstart = 0, zend = 0;
 #endif
 	vm_offset_t lastaddr;
 	int i = 0;
-	static uint32_t fake_preload[35];
+	static uint32_t fake_preload[38];
 
 	fake_preload[i++] = MODINFO_NAME;
 	fake_preload[i++] = strlen("kernel") + 1;
@@ -807,6 +810,9 @@ fake_preload_metadata(struct arm_boot_params *abp __unused)
 	fake_preload[i++] = MODINFO_SIZE;
 	fake_preload[i++] = sizeof(uint32_t);
 	fake_preload[i++] = (uint32_t)&end - KERNVIRTADDR;
+	fake_preload[i++] = MODINFOMD_DTBP | MODINFO_METADATA;
+	fake_preload[i++] = sizeof(vm_offset_t);
+	fake_preload[i++] = fdt_addr;
 #ifdef DDB
 	if (*(uint32_t *)KERNVIRTADDR == MAGIC_TRAMP_NUMBER) {
 		fake_preload[i++] = MODINFO_METADATA|MODINFOMD_SSYM;
@@ -846,6 +852,7 @@ pcpu0_init(void)
 vm_offset_t
 linux_parse_boot_param(struct arm_boot_params *abp)
 {
+	void *ptr;
 	struct arm_lbabi_tag *walker;
 	uint32_t revision;
 	uint64_t serial;
@@ -859,10 +866,15 @@ linux_parse_boot_param(struct arm_boot_params *abp)
 		return 0;
 
 	board_id = abp->abp_r1;
-	walker = (struct arm_lbabi_tag *)
-	    (abp->abp_r2 + KERNVIRTADDR - abp->abp_physaddr);
+	ptr = (void *)(abp->abp_r2 + KERNVIRTADDR - abp->abp_physaddr);
+
+#ifdef FDT
+	if (!fdt_check_header(ptr))
+		return fake_preload_metadata(abp, (vm_offset_t)ptr);
+#endif
+
+	walker = ptr;
 
-	/* xxx - Need to also look for binary device tree */
 	if (ATAG_TAG(walker) != ATAG_CORE)
 		return 0;
 
@@ -901,7 +913,7 @@ linux_parse_boot_param(struct arm_boot_params *abp)
 	bcopy(atag_list, atags,
 	    (char *)walker - (char *)atag_list + ATAG_SIZE(walker));
 
-	return fake_preload_metadata(abp);
+	return fake_preload_metadata(abp, 0);
 }
 #endif
 
@@ -959,7 +971,7 @@ default_parse_boot_param(struct arm_boot_params *abp)
 		return lastaddr;
 #endif
 	/* Fall back to hardcoded metadata. */
-	lastaddr = fake_preload_metadata(abp);
+	lastaddr = fake_preload_metadata(abp, 0);
 
 	return lastaddr;
 }
@@ -1049,12 +1061,15 @@ initarm(struct arm_boot_params *abp)
 	struct mem_region mem_regions[FDT_MEM_REGIONS];
 	struct pv_addr kernel_l1pt;
 	struct pv_addr dpcpu;
+	struct pv_addr fdtpage;
 	vm_offset_t dtbp, freemempos, l2_start, lastaddr;
 	uint32_t memsize, l2size;
 	char *env;
 	void *kmdp;
 	u_int l1pagetable;
 	int i, j, err_devmap, mem_regions_sz;
+	vm_paddr_t fdt_paddr;
+	vm_size_t fdt_size;
 
 	lastaddr = parse_boot_param(abp);
 	arm_physmem_kernaddr = abp->abp_physaddr;
@@ -1086,6 +1101,13 @@ initarm(struct arm_boot_params *abp)
 	if (OF_init((void *)dtbp) != 0)
 		panic("OF_init failed with the found device tree");
 
+	/* Get the physical address of the device tree. This will be used
+	 * later to correctly map the FDT in virtual memory when Linux boot
+	 * ABI loader is used
+	 */
+	fdt_paddr = (dtbp - KERNVIRTADDR + abp->abp_physaddr);
+	fdt_size = fdt_totalsize(dtbp);
+
 	/* Grab physical memory regions information from device tree. */
 	if (fdt_get_mem_regions(mem_regions, &mem_regions_sz, &memsize) != 0)
 		panic("Cannot get physical memory regions");
@@ -1096,6 +1118,10 @@ initarm(struct arm_boot_params *abp)
 		arm_physmem_exclude_regions(mem_regions, mem_regions_sz, 
 		    EXFLAG_NODUMP | EXFLAG_NOALLOC);
 
+	/* TODO: get correctly the DTB size */
+	arm_physmem_exclude_region(fdt_paddr, fdt_size,
+					   EXFLAG_NODUMP | EXFLAG_NOALLOC);
+
 	/* Platform-specific initialisation */
 	platform_probe_and_attach();
 
@@ -1165,6 +1191,11 @@ initarm(struct arm_boot_params *abp)
 	valloc_pages(kernelstack, KSTACK_PAGES * MAXCPU);
 	valloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE);
 
+	/* Allocate virtual address for the device tree */
+	fdt_size = round_page(fdt_size + fdt_paddr & PAGE_MASK);
+	valloc_pages(fdtpage, fdt_size >> PAGE_SHIFT);
+	fdtpage.pv_pa = trunc_page(fdt_paddr);
+
 	/*
 	 * Now we start construction of the L1 page table
 	 * We start by mapping the L2 page tables into the L1.
@@ -1212,6 +1243,10 @@ initarm(struct arm_boot_params *abp)
 	pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa,
 	    VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, PTE_CACHE);
 
+	/* Map the DTB */
+	pmap_map_entry(l1pagetable, fdtpage.pv_va, fdtpage.pv_pa,
+		       VM_PROT_READ, PTE_CACHE);
+
 	/* Establish static device mappings. */
 	err_devmap = platform_devmap_init();
 	arm_devmap_bootstrap(l1pagetable, NULL);
@@ -1223,6 +1258,12 @@ initarm(struct arm_boot_params *abp)
 	cpu_tlb_flushID();
 	cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2));
 
+	/* We need to reinitalize OF subsystem to system the new DTB pointer */
+	/* TODO: Find a nicer way... */
+	dtbp = fdtpage.pv_va + (fdt_paddr - fdtpage.pv_pa);
+	if (OF_init((void *)dtbp) != 0)
+		panic("OF_init failed to update the DTB virtual address");
+
 	/*
 	 * Now that proper page tables are installed, call cpu_setup() to enable
 	 * instruction and data caches and other chip-specific features.
diff --git a/sys/arm/include/machdep.h b/sys/arm/include/machdep.h
index 907270e..05a0681 100644
--- a/sys/arm/include/machdep.h
+++ b/sys/arm/include/machdep.h
@@ -30,7 +30,8 @@ struct arm_boot_params;
 vm_offset_t default_parse_boot_param(struct arm_boot_params *abp);
 vm_offset_t freebsd_parse_boot_param(struct arm_boot_params *abp);
 vm_offset_t linux_parse_boot_param(struct arm_boot_params *abp);
-vm_offset_t fake_preload_metadata(struct arm_boot_params *abp);
+vm_offset_t fake_preload_metadata(struct arm_boot_params *abp,
+				  vm_offset_t fdt_addr);
 vm_offset_t parse_boot_param(struct arm_boot_params *abp);
 void arm_generic_initclocks(void);
 
-- 
2.1.0

