/*-1* Copyright (c) 1998 Michael Smith <[email protected]>2* Copyright (c) 2007 Semihalf, Rafal Jaworowski <[email protected]>3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>2829#include <stand.h>30#include <stdint.h>3132#include "api_public.h"33#include "glue.h"34#include "libuboot.h"3536/*37* MD primitives supporting placement of module data38*/3940#ifdef __arm__41#define KERN_ALIGN (2 * 1024 * 1024)42#else43#define KERN_ALIGN PAGE_SIZE44#endif4546/*47* Avoid low memory, u-boot puts things like args and dtb blobs there.48*/49#define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024))5051extern void _start(void); /* ubldr entry point address. */5253uint64_t loadbase;54bool loadbase_set = false;5556/*57* This is called for every object loaded (kernel, module, dtb file, etc). The58* expected return value is the next address at or after the given addr which is59* appropriate for loading the given object described by type and data. On each60* call the addr is the next address following the previously loaded object.61*/62static uint64_t63uboot_loadaddr(void)64{65struct sys_info *si;66uint64_t sblock, eblock, subldr, eubldr;67uint64_t biggest_block, this_block;68uint64_t biggest_size, this_size;69int i;70char *envstr;7172/*73* If the loader_kernaddr environment variable is set, blindly74* honor it. It had better be right. We force interpretation75* of the value in base-16 regardless of any leading 0x prefix,76* because that's the U-Boot convention.77*/78envstr = ub_env_get("loader_kernaddr");79if (envstr != NULL)80return (strtoul(envstr, NULL, 16));8182/*83* Find addr/size of largest DRAM block. Carve our own address84* range out of the block, because loading the kernel over the85* top ourself is a poor memory-conservation strategy. Avoid86* memory at beginning of the first block of physical ram,87* since u-boot likes to pass args and data there. Assume that88* u-boot has moved itself to the very top of ram and89* optimistically assume that we won't run into it up there.90*/91if ((si = ub_get_sys_info()) == NULL)92panic("could not retrieve system info");9394biggest_block = 0;95biggest_size = 0;96subldr = rounddown2((uintptr_t)_start, KERN_ALIGN);97eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN);98for (i = 0; i < si->mr_no; i++) {99if (si->mr[i].flags != MR_ATTR_DRAM)100continue;101sblock = roundup2((uint64_t)si->mr[i].start,102KERN_ALIGN);103eblock = rounddown2((uint64_t)si->mr[i].start +104si->mr[i].size, KERN_ALIGN);105if (biggest_size == 0)106sblock += KERN_MINADDR;107if (subldr >= sblock && subldr < eblock) {108if (subldr - sblock > eblock - eubldr) {109this_block = sblock;110this_size = subldr - sblock;111} else {112this_block = eubldr;113this_size = eblock - eubldr;114}115} else if (subldr < sblock && eubldr < eblock) {116/* Loader is below or engulfs the sblock */117this_block = (eubldr < sblock) ? sblock : eubldr;118this_size = eblock - this_block;119} else {120this_block = 0;121this_size = 0;122}123if (biggest_size < this_size) {124biggest_block = this_block;125biggest_size = this_size;126}127}128if (biggest_size == 0)129panic("Not enough DRAM to load kernel");130#if 0131printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n",132(uintmax_t)biggest_block,133(uintmax_t)biggest_block + biggest_size - 1,134(uintmax_t)biggest_size / 1024 / 1024);135#endif136return (biggest_block);137}138139ssize_t140uboot_copyin(const void *src, vm_offset_t dest, const size_t len)141{142if (!loadbase_set) {143loadbase = uboot_loadaddr();144loadbase_set = true;145}146147bcopy(src, (void *)(dest + loadbase), len);148return (len);149}150151ssize_t152uboot_copyout(const vm_offset_t src, void *dest, const size_t len)153{154bcopy((void *)(src + loadbase), dest, len);155return (len);156}157158ssize_t159uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len)160{161return (VECTX_READ(fd, (void *)dest, len));162}163164165