/* * Copyright (C) 2018 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include /* QEMU FW_CFG, dtb and kernel load addresses */ #define FW_CFG_BASE 0x09020000 #define DTB_ADDR 0x40000000 #define LOAD_ADDR 0x40080000 #define INITRD_ADDR 0x48000000 #define FW_CFG_KERNEL_SIZE 0x08 #define FW_CFG_KERNEL_DATA 0x11 #define FW_CFG_INITRD_SIZE 0x0b #define FW_CFG_INITRD_DATA 0x12 #define FW_CFG_DMA_CTL_READ 0x02 #define FW_CFG_DMA_CTL_SELECT 0x08 /* Reverse byte order for 16, 32 or 64 bit registers. Compiles to rev* opcode */ static uint64_t rev(uint64_t val, unsigned bits) { uint64_t mask = ~0ULL; for (unsigned shift = bits >> 1; shift >= 8; shift >>= 1) { mask ^= mask << shift; val = ((val >> shift) & mask) | ((val & mask) << shift); } return val; } static uint16_t rev16(uint16_t val) { return rev(val, 16); } static uint32_t rev32(uint32_t val) { return rev(val, 32); } static uint64_t rev64(uint64_t val) { return rev(val, 64); } static bool load_image(uint64_t addr, uint16_t cfg_data, uint16_t cfg_size) { volatile uint32_t* cfg_data32 = (void*)FW_CFG_BASE; volatile uint16_t* cfg_ctl = (void*)(FW_CFG_BASE + 0x8); volatile uint64_t* cfg_dma = (void*)(FW_CFG_BASE + 0x10); volatile struct { uint32_t control; uint32_t length; uint64_t address; } dma; /* * Setup dma description to select FW_CFG_KERNEL_DATA item and read from it. * The FW_CFG interface is big-endian and the cpu is little-endian so we * reverse the byte order. * * Interface is defined in external/qemu/hw/nvram/fw_cfg.c. */ dma.control = rev32(FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SELECT | cfg_data << 16); /* * Select FW_CFG_KERNEL_SIZE item and copy it to the dma lenght descriptor. * Both are big-endian so byte order reversal is needed. */ *cfg_ctl = rev16(cfg_size); dma.length = rev32(*cfg_data32); if (!dma.length) { /* Return if no image was provided */ return false; } /* * Set the target address for the DMA. Reverse the byte order of the address * since the dma descriptor expects big-endian byte order. */ dma.address = rev64(addr); /* Start the dma */ *cfg_dma = rev64((uint64_t)&dma); if (dma.control != 0) { /* Return if dma was not successful */ return false; } return true; } void boot_next(void) { typedef void (*entry_func_t)(uint64_t dtb_paddr); if (!load_image(LOAD_ADDR, FW_CFG_KERNEL_DATA, FW_CFG_KERNEL_SIZE)) { return; } load_image(INITRD_ADDR, FW_CFG_INITRD_DATA, FW_CFG_INITRD_SIZE); /* Jump to the image we just loaded with the dtb address in x0 */ ((entry_func_t)LOAD_ADDR)(DTB_ADDR); }