| /* |
| * Copyright (c) 2018 MediaTek Inc. |
| * |
| * 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 <assert.h> |
| #include <boot_mode.h> |
| #include <compiler.h> |
| #include <debug.h> |
| #include <kernel/vm.h> |
| #include <lib/mempool.h> |
| #include <libfdt.h> |
| #include <platform/psci.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <trace.h> |
| |
| #include "blx_common.h" |
| #include "blxboot_plat.h" |
| |
| #define LOCAL_TRACE 0 |
| |
| #ifndef SLAVE_CPU |
| #define SLAVE_CPU 3 |
| #endif |
| #define SLAVE_CPU_COLDBOOT_TAG 0x10000001 |
| |
| struct bl33boot { |
| struct blxboot commonboot; |
| |
| /* keep image data pointer for quick reference */ |
| struct image_load_data *bootimgdata; |
| struct image_load_data *dtboimgdata; |
| struct image_load_data *vpdimgdata; |
| }; |
| |
| enum { |
| ARM32_TO_ARM32, |
| ARM32_TO_ARM64, |
| ARM64_TO_ARM32, |
| ARM64_TO_ARM64, |
| }; |
| |
| static bool is_next_boot_64bits(void) |
| { |
| #if BL33_BOOT_NEXT_64BITS |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| static int arm_exec_mode_transition(void) |
| { |
| #if ARCH_ARM64 |
| #if BL33_BOOT_NEXT_64BITS |
| return ARM64_TO_ARM64; |
| #else |
| return ARM64_TO_ARM32; |
| #endif |
| #else |
| #if BL33_BOOT_NEXT_64BITS |
| return ARM32_TO_ARM64; |
| #else |
| return ARM32_TO_ARM32; |
| #endif |
| #endif |
| } |
| |
| /* various bl33 exit functions */ |
| /* 32bits bl33 to 32bits kernel */ |
| static void exitfn_jump32(ulong unused0, ulong kernel_ep, ulong machtype, |
| ulong fdt_addr) |
| { |
| jump_func_type jump_fn = (jump_func_type)kernel_ep; |
| (*jump_fn)(0, machtype, fdt_addr, 0); |
| } |
| |
| /* 64bits bl33 to 32bits kernel */ |
| static void exitfn_smc64_to_next32(ulong smc_fid, ulong kernel_ep, |
| ulong machtype, ulong fdt_addr) |
| { |
| /* x0 ~ x3 already in place, set x4 */ |
| __asm__ volatile("mov x4, %0\n\t" |
| "smc #0\n\t" |
| : : "I" (BOOT_AARCH32)); |
| } |
| |
| /* 32bits bl33 to 64bits kernel */ |
| static void exitfn_smc32_to_next64(ulong smc_fid, ulong kernel_ep, |
| ulong fdt_addr, ulong unused) |
| { |
| /* w0 ~ w3 already in place, set w4 */ |
| __asm__ volatile("mov w4, %0\n\t" |
| "smc #0\n\t" |
| : : "I" (BOOT_AARCH64)); |
| } |
| |
| void mtk_sip(ulong smc_fid, ulong kernel_hyp_ep, ulong fdt_addr) |
| { |
| jump_func_type jump_fn = (jump_func_type)kernel_hyp_ep; |
| (*jump_fn)(fdt_addr, 0, 0, 0); |
| } |
| |
| /* 64bits bl33 to 64bits kernel or hypervisor */ |
| static void exitfn_hvc64_to_next64(ulong smc_fid, ulong kernel_hyp_ep, |
| ulong fdt_addr, ulong unused0) |
| { |
| __asm__ volatile("hvc #0\n\t"); |
| } |
| |
| static void notify(struct blxboot *obj, struct imageinfo_t *img) |
| { |
| extern void platform_next_entry(addr_t entry) __attribute__((weak)); |
| int ret __UNUSED; |
| paddr_t entry_addr __UNUSED; |
| struct image_load_data *imgdata = img->imgdata; |
| struct bl33boot *bl33obj = (struct bl33boot *)obj; |
| |
| LTRACEF("%s: %s\n", __func__, imgdata->part_name); |
| switch (img->type) { |
| case IMGTYPE_KERNEL: |
| bl33obj->bootimgdata = imgdata; |
| break; |
| case IMGTYPE_DTBO: |
| bl33obj->dtboimgdata = imgdata; |
| break; |
| case IMGTYPE_VPD: |
| bl33obj->vpdimgdata = imgdata; |
| break; |
| #if ENABLE_SLAVE_CPU_LOAD |
| case IMGTYPE_SLAVE_CPU: |
| if (platform_next_entry) { |
| platform_next_entry(imgdata->kernel_entry); |
| /* entry point for slave CPU */ |
| entry_addr = kvaddr_to_paddr ? |
| kvaddr_to_paddr((void *)&platform_next_entry) : |
| ((void *)&platform_next_entry); |
| } else { |
| entry_addr = (paddr_t)imgdata->kernel_entry; |
| } |
| |
| ret = psci_cpu_on(SLAVE_CPU, (ulong)entry_addr, |
| SLAVE_CPU_COLDBOOT_TAG); |
| if (ret != E_PSCI_SUCCESS) { |
| /* error handling for adsp cpu on failure */ |
| LTRACEF("psci_cpu_on cpu%d fail, err=%d\n", SLAVE_CPU, ret); |
| } |
| break; |
| #endif |
| default: |
| break; |
| } |
| } |
| |
| static void setup_kernel32_boot_param(struct blxboot *obj, |
| struct boot_param *prm) |
| { |
| struct bl33boot *bl33obj = (struct bl33boot *)obj; |
| |
| prm->arg0 = MTK_SIP_KERNEL_BOOT_AARCH32; /* used in smc call */ |
| prm->arg1 = bl33obj->bootimgdata->kernel_entry; |
| prm->arg2 = MACH_TYPE; |
| prm->arg3 = kvaddr_to_paddr ? |
| kvaddr_to_paddr((void *)bl33obj->bootimgdata->dtb_load) : |
| bl33obj->bootimgdata->dtb_load; |
| } |
| |
| static void setup_kernel64_boot_param(struct blxboot *obj, |
| struct boot_param *prm) |
| { |
| struct bl33boot *bl33obj = (struct bl33boot *)obj; |
| |
| prm->arg0 = MTK_SIP_KERNEL_BOOT_AARCH64; |
| prm->arg1 = bl33obj->bootimgdata->kernel_entry; |
| prm->arg2 = kvaddr_to_paddr ? |
| kvaddr_to_paddr((void *)bl33obj->bootimgdata->dtb_load) : |
| bl33obj->bootimgdata->dtb_load; |
| prm->arg3 = 0; /* unused */ |
| } |
| |
| static void get_overlay_image(struct blxboot *obj, void **fdt_dtb, |
| void **dtbo, void **vpd) |
| { |
| struct bl33boot *bl33obj = (struct bl33boot *)obj; |
| |
| assert(obj); |
| |
| *fdt_dtb = NULL; |
| *dtbo = NULL; |
| *vpd = NULL; |
| |
| if (bl33obj->bootimgdata) |
| *fdt_dtb = (void *)bl33obj->bootimgdata->dtb_load; |
| |
| if (bl33obj->dtboimgdata) |
| *dtbo = (void *)bl33obj->dtboimgdata->dtb_load; |
| |
| if (bl33obj->vpdimgdata) |
| *vpd = (void *)bl33obj->vpdimgdata->dtb_load; |
| } |
| |
| struct blxboot *blxboot_create(void) |
| { |
| struct bl33boot *bl33obj; |
| struct blxboot *blxobj; |
| struct blxOps *ops; |
| struct blxCfg *cfg; |
| |
| bl33obj = mempool_alloc(sizeof(struct bl33boot), MEMPOOL_ANY); |
| if (!bl33obj) |
| return NULL; |
| |
| memset(bl33obj, 0, sizeof(struct bl33boot)); |
| |
| blxobj = (struct blxboot *)bl33obj; |
| |
| cfg = &blxobj->bootcfg; |
| cfg->boot_mode = NORMAL_BOOT; |
| cfg->ab_suffix = NULL; |
| |
| ops = &blxobj->ops; |
| ops->init = NULL; |
| ops->notify = notify; |
| ops->fixup_image = NULL; |
| ops->setup_boot_param = is_next_boot_64bits() ? |
| setup_kernel64_boot_param : setup_kernel32_boot_param; |
| ops->get_overlay_image = get_overlay_image; |
| |
| switch (arm_exec_mode_transition()) { |
| case ARM32_TO_ARM32: |
| ops->exit = exitfn_jump32; |
| break; |
| case ARM32_TO_ARM64: |
| ops->exit = exitfn_smc32_to_next64; |
| break; |
| case ARM64_TO_ARM32: |
| ops->exit = exitfn_smc64_to_next32; |
| break; |
| case ARM64_TO_ARM64: |
| ops->exit = exitfn_hvc64_to_next64; |
| break; |
| } |
| |
| return (struct blxboot *)bl33obj; |
| } |