Index: head/sys/arm64/arm64/locore.S =================================================================== --- head/sys/arm64/arm64/locore.S +++ head/sys/arm64/arm64/locore.S @@ -43,6 +43,24 @@ .globl kernbase .set kernbase, KERNBASE + +/* U-Boot booti related constants. */ +#if defined(LINUX_BOOT_ABI) +#define FDT_MAGIC 0xEDFE0DD0 /* FDT blob Magic */ + +#ifndef UBOOT_IMAGE_OFFSET +#define UBOOT_IMAGE_OFFSET 0 /* Image offset from start of */ +#endif /* 2 MiB page */ + +#ifndef UBOOT_IMAGE_SIZE /* Total size of image */ +#define UBOOT_IMAGE_SIZE _end - _start +#endif + +#ifndef UBOOT_IMAGE_FLAGS +#define UBOOT_IMAGE_FLAGS 0 /* LE kernel, unspecified */ +#endif /* page size */ +#endif /* defined(LINUX_BOOT_ABI) */ + /* * We assume: * MMU on with an identity map, or off @@ -54,6 +72,21 @@ .text .globl _start _start: +#if defined(LINUX_BOOT_ABI) + /* U-boot image header */ + b 1f /* code 0 */ + .long 0 /* code 1 */ + .quad UBOOT_IMAGE_OFFSET /* Image offset in 2 MiB page, LE */ + .quad UBOOT_IMAGE_SIZE /* Image size, LE */ + .quad UBOOT_IMAGE_FLAGS /* Flags for kernel. LE */ + .quad 0 /* Reserved */ + .quad 0 /* Reserved */ + .quad 0 /* Reserved */ + .long 0x644d5241 /* Magic "ARM\x64", LE */ + .long 0 /* Reserved for PE COFF offset*/ +1: +#endif /* defined(LINUX_BOOT_ABI) */ + /* Drop to EL1 */ bl drop_to_el1 @@ -350,11 +383,50 @@ /* Find the size of the kernel */ mov x6, #(KERNBASE) + +#if defined(LINUX_BOOT_ABI) + /* X19 is used as 'map FDT data' flag */ + mov x19, xzr + + /* No modules or FDT pointer ? */ + cbz x0, booti_no_fdt + + /* Test if modulep points to modules descriptor or to FDT */ + ldr w8, [x0] + ldr w7, =FDT_MAGIC + cmp w7, w8 + b.eq booti_fdt +#endif + + /* Booted with modules pointer */ /* Find modulep - begin */ sub x8, x0, x6 /* Add two 2MiB pages for the module data and round up */ ldr x7, =(3 * L2_SIZE - 1) add x8, x8, x7 + b common + +#if defined(LINUX_BOOT_ABI) +booti_fdt: + /* Booted by U-Boot booti with FDT data */ + /* Set 'map FDT data' flag */ + mov x19, #1 + +booti_no_fdt: + /* Booted by U-Boot booti without FTD data */ + /* Find the end - begin */ + ldr x7, .Lend + sub x8, x7, x6 + + /* + * Add one 2MiB page for copy of FDT data (maximum FDT size), + * one for metadata and round up + */ + ldr x7, =(3 * L2_SIZE - 1) + add x8, x8, x7 +#endif + +common: /* Get the number of l2 pages to allocate, rounded down */ lsr x10, x8, #(L2_SHIFT) @@ -404,9 +476,21 @@ bl build_l1_block_pagetable #endif - /* - * Create the VA = PA map - */ +#if defined(LINUX_BOOT_ABI) + /* Map FDT data ? */ + cbz x19, 1f + + /* Create the identity mapping for FDT data (2 MiB max) */ + mov x7, #(ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE)) + mov x9, x0 + mov x8, x0 /* VA start (== PA start) */ + mov x10, #1 + bl build_l1_block_pagetable + +1: +#endif + + /* Create the VA = PA map */ mov x7, #(ATTR_nG | ATTR_IDX(VM_MEMATTR_UNCACHEABLE)) mov x9, x27 mov x8, x9 /* VA start (== PA start) */ Index: head/sys/arm64/arm64/machdep.c =================================================================== --- head/sys/arm64/arm64/machdep.c +++ head/sys/arm64/arm64/machdep.c @@ -964,6 +964,15 @@ vm_offset_t dtbp; dtbp = MD_FETCH(kmdp, MODINFOMD_DTBP, vm_offset_t); +#if defined(FDT_DTB_STATIC) + /* + * In case the device tree blob was not retrieved (from metadata) try + * to use the statically embedded one. + */ + if (dtbp == 0) + dtbp = (vm_offset_t)&fdt_static_dtb; +#endif + if (dtbp == (vm_offset_t)NULL) { printf("ERROR loading DTB\n"); return; @@ -974,6 +983,8 @@ if (OF_init((void *)dtbp) != 0) panic("OF_init failed with the found device tree"); + + parse_fdt_bootargs(); } #endif @@ -1082,26 +1093,18 @@ caddr_t kmdp; bool valid; - /* Set the module data location */ - preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); + /* Parse loader or FDT boot parametes. Determine last used address. */ + lastaddr = parse_boot_param(abp); /* Find the kernel address */ kmdp = preload_search_by_type("elf kernel"); if (kmdp == NULL) kmdp = preload_search_by_type("elf64 kernel"); - boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); - init_static_kenv(MD_FETCH(kmdp, MODINFOMD_ENVP, char *), 0); link_elf_ireloc(kmdp); - -#ifdef FDT try_load_dtb(kmdp); -#endif efi_systbl_phys = MD_FETCH(kmdp, MODINFOMD_FW_HANDLE, vm_paddr_t); - - /* Find the address to start allocating from */ - lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); /* Load the physical memory ranges */ efihdr = (struct efi_map_header *)preload_search_info(kmdp, Index: head/sys/arm64/arm64/machdep_boot.c =================================================================== --- head/sys/arm64/arm64/machdep_boot.c +++ head/sys/arm64/arm64/machdep_boot.c @@ -0,0 +1,254 @@ +/*- + * Copyright (c) 2004 Olivier Houchard + * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#ifdef FDT +#include +#endif + +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#endif + +extern int *end; +static char *loader_envp; +static char static_kenv[4096]; + + +#ifdef FDT +#define CMDLINE_GUARD "FreeBSD:" +#define LBABI_MAX_COMMAND_LINE 512 +static char linux_command_line[LBABI_MAX_COMMAND_LINE + 1]; +#endif + +/* + * Fake up a boot descriptor table + */ + #define PRELOAD_PUSH_VALUE(type, value) do { \ + *(type *)(preload_ptr + size) = (value); \ + size += sizeof(type); \ +} while (0) + + #define PRELOAD_PUSH_STRING(str) do { \ + uint32_t ssize; \ + ssize = strlen(str) + 1; \ + PRELOAD_PUSH_VALUE(uint32_t, ssize); \ + strcpy((char*)(preload_ptr + size), str); \ + size += ssize; \ + size = roundup(size, sizeof(u_long)); \ +} while (0) + + +/* Build minimal set of metatda. */ +static vm_offset_t +fake_preload_metadata(void *dtb_ptr, size_t dtb_size) +{ +#ifdef DDB + vm_offset_t zstart = 0, zend = 0; +#endif + vm_offset_t lastaddr; + static char fake_preload[256]; + caddr_t preload_ptr; + size_t size; + + preload_ptr = (caddr_t)&fake_preload[0]; + size = 0; + + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_NAME); + PRELOAD_PUSH_STRING("kernel"); + + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_TYPE); + PRELOAD_PUSH_STRING("elf kernel"); + + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_ADDR); + PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); + PRELOAD_PUSH_VALUE(uint64_t, VM_MIN_KERNEL_ADDRESS); + + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_SIZE); + PRELOAD_PUSH_VALUE(uint32_t, sizeof(size_t)); + PRELOAD_PUSH_VALUE(uint64_t, (size_t)(&end - VM_MIN_KERNEL_ADDRESS)); +#ifdef DDB + if (*(uint64_t *)VM_MIN_KERNEL_ADDRESS == MAGIC_TRAMP_NUMBER) { + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA|MODINFOMD_SSYM); + PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); + PRELOAD_PUSH_VALUE(uint64_t, + *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 4)); + + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_ESYM); + PRELOAD_PUSH_VALUE(uint32_t, sizeof(vm_offset_t)); + PRELOAD_PUSH_VALUE(uint64_t, + *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 8)); + + lastaddr = *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 8); + zend = lastaddr; + zstart = *(uint64_t *)(VM_MIN_KERNEL_ADDRESS + 4); + db_fetch_ksymtab(zstart, zend); + } else +#endif + lastaddr = (vm_offset_t)&end; + if (dtb_ptr != NULL) { + /* Copy DTB to KVA space and insert it into module chain. */ + lastaddr = roundup(lastaddr, sizeof(int)); + PRELOAD_PUSH_VALUE(uint32_t, MODINFO_METADATA | MODINFOMD_DTBP); + PRELOAD_PUSH_VALUE(uint32_t, sizeof(uint64_t)); + PRELOAD_PUSH_VALUE(uint64_t, (uint64_t)lastaddr); + memmove((void *)lastaddr, dtb_ptr, dtb_size); + lastaddr += dtb_size; + lastaddr = roundup(lastaddr, sizeof(int)); + } + /* End marker */ + PRELOAD_PUSH_VALUE(uint32_t, 0); + PRELOAD_PUSH_VALUE(uint32_t, 0); + + preload_metadata = (caddr_t)(uintptr_t)fake_preload; + + init_static_kenv(NULL, 0); + + return (lastaddr); +} + +#ifdef FDT + +/* Convert the U-Boot command line into FreeBSD kenv and boot options. */ +static void +cmdline_set_env(char *cmdline, const char *guard) +{ + size_t guard_len; + + /* Skip leading spaces. */ + while (isspace(*cmdline)) + cmdline++; + + /* Test and remove guard. */ + if (guard != NULL && guard[0] != '\0') { + guard_len = strlen(guard); + if (strncasecmp(cmdline, guard, guard_len) != 0) + return; + cmdline += guard_len; + } + + boothowto |= boot_parse_cmdline(cmdline); +} + +void +parse_fdt_bootargs(void) +{ + + if (loader_envp == NULL && fdt_get_chosen_bootargs(linux_command_line, + LBABI_MAX_COMMAND_LINE) == 0) { + init_static_kenv(static_kenv, sizeof(static_kenv)); + cmdline_set_env(linux_command_line, CMDLINE_GUARD); + } +} + +#endif + +#if defined(LINUX_BOOT_ABI) && defined(FDT) +static vm_offset_t +linux_parse_boot_param(struct arm64_bootparams *abp) +{ + struct fdt_header *dtb_ptr; + size_t dtb_size; + + if (abp->modulep == 0) + return (0); + /* Test if modulep point to valid DTB. */ + dtb_ptr = (struct fdt_header *)abp->modulep; + if (fdt_check_header(dtb_ptr) != 0) + return (0); + dtb_size = fdt_totalsize(dtb_ptr); + return (fake_preload_metadata(dtb_ptr, dtb_size)); +} + +#endif + +static vm_offset_t +freebsd_parse_boot_param(struct arm64_bootparams *abp) +{ + vm_offset_t lastaddr = 0; + void *kmdp; +#ifdef DDB + vm_offset_t ksym_start; + vm_offset_t ksym_end; +#endif + + if (abp->modulep == 0) + return (0); + + preload_metadata = (caddr_t)(uintptr_t)(abp->modulep); + kmdp = preload_search_by_type("elf kernel"); + if (kmdp == NULL) + return (0); + + boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); + loader_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *); + init_static_kenv(static_kenv, 0); + lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); +#ifdef DDB + ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t); + ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t); + db_fetch_ksymtab(ksym_start, ksym_end); +#endif + return (lastaddr); +} + +vm_offset_t +parse_boot_param(struct arm64_bootparams *abp) +{ + vm_offset_t lastaddr; + +#if defined(LINUX_BOOT_ABI) && defined(FDT) + lastaddr = linux_parse_boot_param(abp); + if (lastaddr != 0) + return (lastaddr); +#endif + lastaddr = freebsd_parse_boot_param(abp); + if (lastaddr != 0) + return (lastaddr); + + /* Fall back to hardcoded metadata. */ + lastaddr = fake_preload_metadata(NULL, 0); + + return (lastaddr); +} Index: head/sys/arm64/include/machdep.h =================================================================== --- head/sys/arm64/include/machdep.h +++ head/sys/arm64/include/machdep.h @@ -47,6 +47,10 @@ void dbg_init(void); void initarm(struct arm64_bootparams *); +vm_offset_t parse_boot_param(struct arm64_bootparams *abp); +#ifdef FDT +void parse_fdt_bootargs(void); +#endif extern void (*pagezero)(void *); #endif /* _MACHINE_MACHDEP_H_ */ Index: head/sys/conf/Makefile.arm64 =================================================================== --- head/sys/conf/Makefile.arm64 +++ head/sys/conf/Makefile.arm64 @@ -27,10 +27,24 @@ INCLUDES+= -I$S/contrib/libfdt +#SYSTEM_LD:= ${SYSTEM_LD:$S/conf/ldscript.$M=ldscript.$M} +#SYSTEM_DEP:= ${SYSTEM_DEP:$S/conf/ldscript.$M=ldscript.$M} + .if !empty(DDB_ENABLED) CFLAGS += -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer .endif +SYSTEM_LD_ = ${LD} -m ${LD_EMULATION} -Bdynamic -T ldscript.$M.noheader \ + ${_LDFLAGS} --no-warn-mismatch --warn-common --export-dynamic \ + --dynamic-linker /red/herring \ + -o ${FULLKERNEL}.noheader -X ${SYSTEM_OBJS} vers.o +SYSTEM_LD_TAIL +=;sed s/" + SIZEOF_HEADERS"// $(LDSCRIPT)\ + >ldscript.$M.noheader;\ + ${SYSTEM_LD_}; \ + ${OBJCOPY} -S -O binary ${FULLKERNEL}.noheader \ + ${KERNEL_KO}.bin; \ + rm ${FULLKERNEL}.noheader + %BEFORE_DEPEND %OBJS @@ -42,6 +56,7 @@ %FILES.m %CLEAN +CLEAN+= ldscript.$M ${KERNEL_KO}.bin ldscript.$M.noheader %RULES Index: head/sys/conf/files.arm64 =================================================================== --- head/sys/conf/files.arm64 +++ head/sys/conf/files.arm64 @@ -156,6 +156,7 @@ arm64/arm64/in_cksum.c optional inet | inet6 arm64/arm64/locore.S standard no-obj arm64/arm64/machdep.c standard +arm64/arm64/machdep_boot.c standard arm64/arm64/mem.c standard arm64/arm64/memcpy.S standard arm64/arm64/memmove.S standard Index: head/sys/conf/options.arm64 =================================================================== --- head/sys/conf/options.arm64 +++ head/sys/conf/options.arm64 @@ -6,6 +6,7 @@ SOCDEV_VA opt_global.h THUNDERX_PASS_1_1_ERRATA opt_global.h VFP opt_global.h +LINUX_BOOT_ABI opt_global.h # Binary compatibility COMPAT_FREEBSD32 opt_global.h