#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PrjCfg.h" #include "sys_storage_partition.h" #include "sys_linuxboot.h" #include "sys_fdt.h" #include #include #include #include #include #include "DxHunting.h" #include "sys_fastboot.h" #if HUNTING_CAMERA_MCU == ENABLE #include "sf_mcu.h" #include "sf_battery.h" #endif #define CFG_BOOTARG_EXTRA_MAX_LEN 0x100 /* for init.d script parsing by (dmesg | grep xxx) */ #define CFG_INDEP_RAMDISK ENABLE #define CFG_LINUX_START_OFFSET 0x8000 #define CFG_BOOTARG_MAX_LEN 0x1000 #define CFG_LINUX_MAX_CODE_SIZE 0x01000000 //for 64MB dram, assume linux (ro+rw+bss) limited on this size //#define CFG_LINUX_COMP_MAX_SIZE 0xA00000 //max uImage size //#define CFG_RAMFS_COMP_MAX_SIZE 0x500000 //max ramdisk size #define CFG_LINUX_COMP_MAX_SIZE 0x330000 //max uImage size #define CFG_RAMFS_COMP_MAX_SIZE 0x330000 //max ramdisk size #define CFG_MULTI_MKIMAGE_LEN 0x8 //mkimage's multi image always comes 8 bytes for sub-image size #define CFG_GZ_WORK_SIZE 0x10000 //64KB are enough #define CFG_CORE2_ENTRY_REG 0xF07F8000 #ifndef CONFIG_SYS_FDT_PAD #define CONFIG_SYS_FDT_PAD 0x3000 // from uboot #endif #define CACHE_LINE 32 #define MEM_PATH_LINUX "/memory" #define MEM_PATH_DRAM "/nvt_memory_cfg/dram" #define MEM_PATH_LINUXTMP "/nvt_memory_cfg/linuxtmp" #define MEM_PATH_BRIDGE "/nvt_memory_cfg/bridge" #define MEM_PATH_FDT "/nvt_memory_cfg/fdt" #define MEM_PATH_RTOS "/nvt_memory_cfg/rtos" #define MEM_PATH_HDAL "/hdal-memory/media" #define MEM_PATH_SHMEM SHMEM_PATH // sync from uboot #define CONFIG_BOOTARGS_COMMON "earlyprintk console=ttyS0,115200 rootwait nprofile_irq_duration=on rtos_boot=on " #if defined(_NVT_ROOTFS_TYPE_RAMDISK_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "root=/dev/ram0 rootfstype=ramfs rdinit=/linuxrc " #elif defined(_NVT_ROOTFS_TYPE_NAND_UBI_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "root=ubi0:rootfs rootfstype=ubifs ubi.fm_autoconvert=1 init=/linuxrc " #elif defined(_NVT_ROOTFS_TYPE_SQUASH_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "rootfstype=squashfs ro " #elif defined(_NVT_ROOTFS_TYPE_NAND_JFFS2_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "rootfstype=jffs2 rw " #elif defined(_NVT_ROOTFS_TYPE_NOR_JFFS2_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "rootfstype=jffs2 rw " #elif defined(_NVT_ROOTFS_TYPE_EMMC_) #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "rootfstype=ext4 rw " #else //SD #define CONFIG_BOOTARGS CONFIG_BOOTARGS_COMMON "root=/dev/mmcblk0p2 noinitrd rootfstype=ext3 init=/linuxrc " #endif /* _NVT_ROOTFS_TYPE_ */ /* * Legacy format image header, * all data in network byte order (aka natural aka bigendian). */ #define IH_MAGIC 0x27051956 /* Image Magic Number */ #define IH_NMLEN 32 /* Image Name Length */ typedef struct image_header { unsigned int ih_magic; /* Image Header Magic Number */ unsigned int ih_hcrc; /* Image Header CRC Checksum */ unsigned int ih_time; /* Image Creation Timestamp */ unsigned int ih_size; /* Image Data Size */ unsigned int ih_load; /* Data Load Address */ unsigned int ih_ep; /* Entry Point Address */ unsigned int ih_dcrc; /* Image Data CRC Checksum */ unsigned char ih_os; /* Operating System */ unsigned char ih_arch; /* CPU architecture */ unsigned char ih_type; /* Image Type */ unsigned char ih_comp; /* Compression Type */ unsigned char ih_name[IH_NMLEN]; /* Image Name */ } image_header_t; typedef struct _GZ_BUF { unsigned char *p_begin; unsigned char *p_curr; unsigned char *p_end; } GZ_BUF; typedef struct _UNGZIP_INPUT { unsigned char *in; unsigned char *out; unsigned int insize; unsigned int outsize; GZ_BUF gz_buf; //only for CFG_CORE2_DECOMP_RAMDISK } UNGZIP_INPUT; typedef enum _RAMDISK_METHOD { RAMDISK_METHOD_PARTIAL, ///< partial load and decompress RAMDISK_METHOD_FULL, ///< full loaded the decompress by thread RAMDISK_METHOD_CORE2, ///< for dual core only, use core2 decompress RAMDISK_METHOD_NVTPACK, ///< boot linux from all-in-one on sd card } RAMDISK_METHOD; #if (CFG_INDEP_RAMDISK) #if defined(_NVT_LINUX_COMPRESS_NONE_) static RAMDISK_METHOD ramdisk_methold = RAMDISK_METHOD_FULL; #elif 0 //defined(_NVT_LINUX_SMP_ON_) static RAMDISK_METHOD ramdisk_methold = RAMDISK_METHOD_CORE2; #else // default use partial decompress static RAMDISK_METHOD ramdisk_methold = RAMDISK_METHOD_PARTIAL; #endif #endif //#if (CFG_INDEP_RAMDISK) #if (CFG_INDEP_RAMDISK) static pthread_t handle_unzip_ramdisk; _ALIGNED(64) static UNGZIP_INPUT ungzip_input = {0}; #endif #if POWERON_FAST_BOOT_MSG == ENABLE #define LINUX_BOOT_MSG(fmt, args...) DBG_DUMP(fmt, args) #else #define LINUX_BOOT_MSG(fmt, args...) #endif static char extra_bootarg[CFG_BOOTARG_EXTRA_MAX_LEN] = {'\0'}; static int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name) { int offset; offset = fdt_subnode_offset(fdt, parentoffset, name); if (offset == -FDT_ERR_NOTFOUND) offset = fdt_add_subnode(fdt, parentoffset, name); if (offset < 0) printf("%s: %s: %s\n", __func__, name, fdt_strerror(offset)); return offset; } static int fdt_chosen(LINUXTMP_PARTITION *p_linuxtmp) { int nodeoffset; int err; char *str; /* used to set string properties */ void *fdt = (void *)p_linuxtmp->fdt_addr; #if (((_PACKAGE_DISPLAY_) || (_PACKAGE_BOOTLOGO_)) && (!defined(_disp_off_))) fdt32_t value=0x1; #endif err = fdt_check_header(fdt); if (err < 0) { DBG_DUMP("fdt_chosen: %s\n", fdt_strerror(err)); return err; } /* find or create "/chosen" node. */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return nodeoffset; str = (char *)p_linuxtmp->bootargs_addr; if (str) { err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str) + 1); if (err < 0) { DBG_DUMP("WARNING: could not set bootargs %s.\n", fdt_strerror(err)); return err; } } if(p_linuxtmp->ramfs_addr) { fdt32_t tmp = cpu_to_fdt32(p_linuxtmp->ramfs_addr); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-start", &tmp, sizeof(tmp)); if (err < 0) { DBG_DUMP("WARNING: could not set initrd-start %s.\n", fdt_strerror(err)); return err; } tmp = cpu_to_fdt32(p_linuxtmp->ramfs_addr + p_linuxtmp->ramfs_size); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-end", &tmp, sizeof(tmp)); if (err < 0) { DBG_DUMP("WARNING: could not set initrd-end %s.\n", fdt_strerror(err)); return err; } } #if (((_PACKAGE_DISPLAY_) || (_PACKAGE_BOOTLOGO_)) && (!defined(_disp_off_))) #if HUNTING_CAMERA_MCU == ENABLE if(sf_get_mode_flag()) #endif { nodeoffset = fdt_path_offset((const void*)fdt, "/logo"); if (nodeoffset >= 0) { value = cpu_to_fdt32(value); fdt_setprop(fdt, nodeoffset, "enable", &value, sizeof(value)); } } #endif return 0; } static int fdt_read_mem_node(const char *path, unsigned int *p_addr, unsigned int *p_size) { unsigned char *p_fdt = (unsigned char *)fdt_get_base(); if (p_fdt == NULL) { DBG_ERR("p_fdt is NULL.\n"); return -1; } int len; int nodeoffset; const void *nodep; /* property node pointer */ // get linux space nodeoffset = fdt_path_offset(p_fdt, path); if (nodeoffset < 0) { DBG_ERR("failed to offset for %s = %d \n", path, nodeoffset); return -1; } nodep = fdt_getprop(p_fdt, nodeoffset, "reg", &len); if (len == 0 || nodep == NULL) { DBG_ERR("failed to access reg.\n"); return -1; } else { unsigned int *p_data = (unsigned int *)nodep; *p_addr = be32_to_cpu(p_data[0]); *p_size = be32_to_cpu(p_data[1]); } return 0; } static int fdt_get_info(FDT_INFO *p_info) { int er; if ((er = fdt_read_mem_node(MEM_PATH_LINUX, &p_info->linux_addr, &p_info->linux_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_LINUXTMP, &p_info->linuxtmp_addr, &p_info->linuxtmp_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_FDT, &p_info->fdt_addr, &p_info->fdt_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_RTOS, &p_info->rtos_addr, &p_info->rtos_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_HDAL, &p_info->hdal_addr, &p_info->hdal_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_SHMEM, &p_info->shmem_addr, &p_info->shmem_size)) != 0) { return er; } if ((er = fdt_read_mem_node(MEM_PATH_DRAM, &p_info->dram_addr, &p_info->dram_size)) != 0) { return er; } return 0; } static int create_new_fdt(LINUXTMP_PARTITION *p_linuxtmp) { unsigned char *p_fdt = (unsigned char *)fdt_get_base(); if (p_fdt == NULL) { DBG_ERR("p_fdt is NULL.\n"); return -1; } int new_size = ALIGN_CEIL(fdt_totalsize(p_fdt) + CONFIG_SYS_FDT_PAD, 0x1000); if (p_linuxtmp->tmp2_begin) { p_linuxtmp->fdt_addr = p_linuxtmp->tmp2_curr; p_linuxtmp->fdt_size = new_size; p_linuxtmp->tmp2_curr += new_size; } else { p_linuxtmp->fdt_addr = p_linuxtmp->tmp_curr; p_linuxtmp->fdt_size = new_size; p_linuxtmp->tmp_curr += new_size; } if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } int err = fdt_open_into(p_fdt, (void *)p_linuxtmp->fdt_addr, new_size); if (err != 0) { DBG_ERR("fdt move failed!"); return -1; } return 0; } #if (CFG_INDEP_RAMDISK) static void *gzalloc_core2(void *x, unsigned items, unsigned size) { void *p = ungzip_input.gz_buf.p_curr; size *= items; ungzip_input.gz_buf.p_curr += size; if (ungzip_input.gz_buf.p_curr > ungzip_input.gz_buf.p_end) { printf("gz buff not enough.\r\n"); return NULL; } else { memset(p, 0, size); return p; } } #endif #if (CFG_INDEP_RAMDISK) static void gzfree_core2(void *x, void *addr, unsigned nb) { } #endif #if (CFG_INDEP_RAMDISK) void core2_cb(void) { int err; z_stream stream = {0}; stream.next_in = (z_const Bytef *)ungzip_input.in; stream.avail_in = ungzip_input.insize; stream.next_out = (z_const Bytef *)ungzip_input.out; stream.avail_out = ungzip_input.outsize; stream.zalloc = (alloc_func)gzalloc_core2; stream.zfree = (free_func)gzfree_core2; stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) { printf("Failed to inflateInit\r\n"); inflateEnd(&stream); return; } err = inflate(&stream, Z_NO_FLUSH); inflateEnd(&stream); if (err != Z_STREAM_END) { printf("Failed to inflate\r\n"); return; } // clean cache without invalidate output data unsigned int cache_begin = (unsigned int)ungzip_input.out; unsigned int cache_end = ALIGN_CEIL((unsigned int)ungzip_input.out+ungzip_input.outsize, CACHE_LINE); if ((cache_begin & (CACHE_LINE-1)) != 0) { printf("target addr not cache aligment\r\n"); return; } for (; cache_begin < cache_end; cache_begin+=CACHE_LINE) { __asm__ __volatile__( "mcr p15, 0, %0, c7, c10, 1" : : "r" (cache_begin) ); } // clean cache without invalidate indication byte *(volatile UINT32 *)CFG_CORE2_ENTRY_REG = (UINT32)0; __asm__ __volatile__( "mcr p15, 0, %0, c7, c10, 1" : : "r" (CFG_CORE2_ENTRY_REG) ); } #endif #if defined(_NVT_LINUX_COMPRESS_GZ_) static void *gzalloc_full(void *x, unsigned items, unsigned size) { size *= items; void *p = malloc(size); if (p) { memset(p, 0, size); } return p; } static void gzfree_full(void *x, void *addr, unsigned nb) { free(addr); } #endif #if (CFG_INDEP_RAMDISK) static void *thread_ungzip_ramdisk(void *ptr) { int err; z_stream stream = {0}; stream.next_in = (z_const Bytef *)ungzip_input.in; stream.avail_in = ungzip_input.insize; stream.next_out = (z_const Bytef *)ungzip_input.out; stream.avail_out = ungzip_input.outsize; #if defined(_NVT_LINUX_COMPRESS_GZ_) stream.zalloc = (alloc_func)gzalloc_full; stream.zfree = (free_func)gzfree_full; #endif stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) { DBG_ERR("Failed to inflateInit, err = %d\r\n", err); inflateEnd(&stream); pthread_exit((void *)-1); return NULL; } err = inflate(&stream, Z_NO_FLUSH); inflateEnd(&stream); if (err == Z_STREAM_END) { pthread_exit((void *)0); return NULL; } pthread_exit((void *)-1); return NULL; } #endif #if (CFG_INDEP_RAMDISK) static int load_ramdisk_core2(LINUXTMP_PARTITION *p_linuxtmp) { unsigned int bfc_data = p_linuxtmp->tmp_curr; STORAGE_OBJ *p_strg = EMB_GETSTRGOBJ(STRG_OBJ_FW_ROOTFS); if (p_strg == NULL) { DBG_ERR("failed to get STRG_OBJ_FW_ROOTFS"); return -1; } //load 1st block p_strg->Lock(); p_strg->Open(); if (p_strg->RdSectors((INT8 *)bfc_data, 0, 1) != 0) { DBG_ERR("RdSectors for ramdisk 1st block\r\n"); return -1; } NVTPACK_BFC_HDR *p_hdr = (NVTPACK_BFC_HDR *)bfc_data; if (p_hdr->uiFourCC != MAKEFOURCC('B','C','L','1')) { DBG_ERR("BCL1 header is wrong\r\n"); return -1; } unsigned int bfc_size = be32_to_cpu(p_hdr->uiSizeComp) + sizeof(NVTPACK_BFC_HDR); unsigned int comp_ramfs_addr = bfc_data + sizeof(NVTPACK_BFC_HDR); unsigned int comp_ramfs_size = be32_to_cpu(p_hdr->uiSizeComp); p_linuxtmp->tmp_curr += ALIGN_CEIL_64(bfc_size); p_linuxtmp->ramfs_addr = p_linuxtmp->tmp_curr; p_linuxtmp->ramfs_size = be32_to_cpu(p_hdr->uiSizeUnComp); p_linuxtmp->tmp_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); // load rest of image data unsigned int block_size = 0; p_strg->GetParam(STRG_GET_BEST_ACCESS_SIZE, (UINT32)&block_size, 0); if (block_size == 0) { DBG_ERR("block_size is 0.\n"); return -1; } unsigned int remain_blocks = ALIGN_CEIL(bfc_size - block_size, block_size)/block_size; if (p_strg->RdSectors((INT8 *)(bfc_data+block_size), 1, remain_blocks) != 0) { DBG_ERR("RdSectors for ramdisk\r\n"); return -1; } p_strg->Close(); p_strg->Unlock(); //uncompress ungzip_input.in = (unsigned char*)comp_ramfs_addr; ungzip_input.out = (unsigned char*)p_linuxtmp->ramfs_addr; ungzip_input.insize = comp_ramfs_size; ungzip_input.outsize = p_linuxtmp->ramfs_size; ungzip_input.gz_buf.p_begin = (unsigned char*)malloc(CFG_GZ_WORK_SIZE); ungzip_input.gz_buf.p_curr = ungzip_input.gz_buf.p_begin; ungzip_input.gz_buf.p_end = ungzip_input.gz_buf.p_begin + CFG_GZ_WORK_SIZE; if (ungzip_input.gz_buf.p_begin == NULL) { DBG_ERR("failed to malloc\n"); return -1; } vos_cpu_dcache_sync((VOS_ADDR)&ungzip_input, sizeof(ungzip_input), VOS_DMA_TO_DEVICE); vos_cpu_dcache_sync((VOS_ADDR)p_linuxtmp->ramfs_addr, p_linuxtmp->ramfs_size, VOS_DMA_TO_DEVICE); extern void core2_exec(void); *(volatile UINT32 *)CFG_CORE2_ENTRY_REG = (UINT32)core2_exec; vos_cpu_dcache_sync(CFG_CORE2_ENTRY_REG, 64, VOS_DMA_TO_DEVICE); arm_gic_raise_sgi(8, 2); return 0; } #endif #if (CFG_INDEP_RAMDISK) static int load_ramdisk_partial(LINUXTMP_PARTITION *p_linuxtmp) { FWSRV_CMD cmd = {0}; MEM_RANGE mem_range = {0}; FWSRV_FASTLOAD fastload = {0}; unsigned int comp_ramfs_addr = p_linuxtmp->tmp_curr; unsigned int comp_ramfs_size = CFG_RAMFS_COMP_MAX_SIZE; p_linuxtmp->tmp_curr += comp_ramfs_size; LINUX_BOOT_MSG("linuxtmp used = %lx, comp_ramfs size = %lx\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_begin, comp_ramfs_size); if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } fastload.pStrg = EMB_GETSTRGOBJ(STRG_OBJ_FW_ROOTFS); fastload.MemComp.addr = comp_ramfs_addr; fastload.MemComp.size = comp_ramfs_size; if (p_linuxtmp->tmp2_begin) { fastload.MemUnComp.addr = p_linuxtmp->tmp2_curr; fastload.MemUnComp.size = p_linuxtmp->tmp2_end - p_linuxtmp->tmp2_curr; } else { fastload.MemUnComp.addr = p_linuxtmp->tmp_curr; fastload.MemUnComp.size = p_linuxtmp->tmp_end - p_linuxtmp->tmp_curr; } cmd.Idx = FWSRV_CMD_IDX_FASTLOAD; cmd.In.pData = &fastload; cmd.In.uiNumByte = sizeof(fastload); cmd.Out.pData = &mem_range; cmd.Out.uiNumByte = sizeof(mem_range); cmd.Prop.bExitCmdFinish = TRUE; FWSRV_ER er; if ((er=FwSrv_Open()) != FWSRV_ER_OK) { DBG_ERR("FwSrv_Open failed!\r\n"); return -1; } if ((er=FwSrv_Cmd(&cmd)) != FWSRV_ER_OK) { DBG_ERR("FwSrv_Cmd failed!\r\n"); return -1; } FwSrv_Close(); p_linuxtmp->ramfs_addr = mem_range.addr; p_linuxtmp->ramfs_size = mem_range.size; if (p_linuxtmp->tmp2_begin) { p_linuxtmp->tmp2_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); } else { p_linuxtmp->tmp_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); } LINUX_BOOT_MSG("linuxtmp used = %lx, ramfs uncompressed size = %lx\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_begin, p_linuxtmp->ramfs_size); return 0; } #endif #if (CFG_INDEP_RAMDISK) static int load_ramdisk_full(LINUXTMP_PARTITION *p_linuxtmp) { unsigned int bfc_data = p_linuxtmp->tmp_curr; STORAGE_OBJ *p_strg = EMB_GETSTRGOBJ(STRG_OBJ_FW_ROOTFS); if (p_strg == NULL) { DBG_ERR("failed to get STRG_OBJ_FW_ROOTFS"); return -1; } //load 1st block p_strg->Lock(); p_strg->Open(); if (p_strg->RdSectors((INT8 *)bfc_data, 0, 1) != 0) { DBG_ERR("RdSectors for ramdisk 1st block\r\n"); return -1; } NVTPACK_BFC_HDR *p_hdr = (NVTPACK_BFC_HDR *)bfc_data; if (p_hdr->uiFourCC != MAKEFOURCC('B','C','L','1')) { DBG_ERR("BCL1 header is wrong\r\n"); return -1; } unsigned int bfc_size = be32_to_cpu(p_hdr->uiSizeComp) + sizeof(NVTPACK_BFC_HDR); unsigned int comp_ramfs_addr = bfc_data + sizeof(NVTPACK_BFC_HDR); unsigned int comp_ramfs_size = be32_to_cpu(p_hdr->uiSizeComp); p_linuxtmp->tmp_curr += ALIGN_CEIL_64(bfc_size); if (p_linuxtmp->tmp2_begin) { p_linuxtmp->ramfs_addr = p_linuxtmp->tmp2_curr; p_linuxtmp->ramfs_size = be32_to_cpu(p_hdr->uiSizeUnComp); p_linuxtmp->tmp2_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); if (p_linuxtmp->tmp2_curr >= p_linuxtmp->tmp2_end) { DBG_ERR("rootfs is too large, need reduce %d\n", p_linuxtmp->tmp2_curr - p_linuxtmp->tmp2_end); return -1; } } else { p_linuxtmp->ramfs_addr = p_linuxtmp->tmp_curr; p_linuxtmp->ramfs_size = be32_to_cpu(p_hdr->uiSizeUnComp); p_linuxtmp->tmp_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } } // load rest of image data unsigned int block_size = 0; p_strg->GetParam(STRG_GET_BEST_ACCESS_SIZE, (UINT32)&block_size, 0); if (block_size == 0) { DBG_ERR("block_size is 0.\n"); return -1; } unsigned int remain_blocks = ALIGN_CEIL(bfc_size - block_size, block_size)/block_size; if (p_strg->RdSectors((INT8 *)(bfc_data+block_size), 1, remain_blocks) != 0) { DBG_ERR("RdSectors for ramdisk\r\n"); return -1; } p_strg->Close(); p_strg->Unlock(); //uncompress ungzip_input.in = (unsigned char*)comp_ramfs_addr; ungzip_input.out = (unsigned char*)p_linuxtmp->ramfs_addr; ungzip_input.insize = comp_ramfs_size; ungzip_input.outsize = p_linuxtmp->ramfs_size; int pthread_ret = pthread_create(&handle_unzip_ramdisk, NULL, thread_ungzip_ramdisk , NULL); if (0 != pthread_ret) { DBG_ERR("create thread_ungzip_ramdisk failed, ret %d\r\n", pthread_ret); return -1; } return 0; } #endif static int find_index_nvtpack(LINUXTMP_PARTITION *p_linuxtmp, char *partition_name) { #if (_PACKAGE_FILESYS_ && _PACKAGE_SDCARD_) unsigned char *p_fdt = (unsigned char *)fdt_get_base(); if (p_fdt == NULL) { DBG_ERR("p_fdt is NULL\r\n"); return -1; } // find out nvtpack index to partition_rootfs int i; char *path_fdt = NULL; char *pathes_fdt[] = { "/nor/nvtpack", "/nand/nvtpack", "/mmc@f0510000/nvtpack", }; for (i = 0; i < 3; i++) { int nodeoffset = fdt_path_offset(p_fdt, pathes_fdt[i]); if (nodeoffset >= 0) { path_fdt = pathes_fdt[i]; break; } } if (path_fdt == NULL) { DBG_ERR("unable to find nvtpack in fdt on sd card\r\n"); return -1; } for (i = 0; i < 64; i++) { char path[64] = {0}; sprintf(path, "%s/index/id%d", path_fdt, i); int nodeoffset = fdt_path_offset(p_fdt, path); if (nodeoffset < 0) { continue; } int len = 0; const void *nodep = fdt_getprop(p_fdt, nodeoffset, "partition_name", &len); if (nodep == NULL || len == 0) { continue; } if (strncmp((const char *)nodep, partition_name, len) == 0) { return i; } } return -1; #else DBG_ERR("unsupported\r\n"); return -1; #endif } #if (CFG_INDEP_RAMDISK) static int load_ramdisk_nvtpack(LINUXTMP_PARTITION *p_linuxtmp) { #if (_PACKAGE_FILESYS_ && _PACKAGE_SDCARD_) NVTPACK_ER er; NVTPACK_GET_PARTITION_INPUT np_get_input; NVTPACK_MEM mem_in = {(void *)p_linuxtmp->nvtpack_addr, (unsigned int)p_linuxtmp->nvtpack_size}; int nvtpack_rootfs_index = find_index_nvtpack(p_linuxtmp, "rootfs"); if (nvtpack_rootfs_index== -1) { DBG_ERR("unable to find rootfs-nvtpack-index in fdt on sd card\r\n"); return -1; } NVTPACK_MEM rootfs_mem; np_get_input.id = nvtpack_rootfs_index; np_get_input.mem = mem_in; er = nvtpack_get_partition(&np_get_input, &rootfs_mem); if (er == NVTPACK_ER_NOT_FOUND) { DBG_ERR("unable to find rootfs in all-in-one\r\n"); return -1; } NVTPACK_CHKSUM_HDR *p_sum = (NVTPACK_CHKSUM_HDR *)rootfs_mem.p_data; if (p_sum->uiFourCC != MAKEFOURCC('C','K','S','M')) { DBG_ERR("CKSM header is wrong on sd card\r\n"); return -1; } NVTPACK_BFC_HDR *p_hdr = (NVTPACK_BFC_HDR *)&p_sum[1]; if (p_hdr->uiFourCC != MAKEFOURCC('B','C','L','1')) { DBG_ERR("rootfs BCL1 header is wrong on sd card\r\n"); return -1; } unsigned int comp_ramfs_addr = (unsigned int)&p_hdr[1]; unsigned int comp_ramfs_size = be32_to_cpu(p_hdr->uiSizeComp); p_linuxtmp->ramfs_addr = p_linuxtmp->tmp_curr; p_linuxtmp->ramfs_size = be32_to_cpu(p_hdr->uiSizeUnComp); p_linuxtmp->tmp_curr = p_linuxtmp->ramfs_addr + ALIGN_CEIL_64(p_linuxtmp->ramfs_size); if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } //uncompress ungzip_input.in = (unsigned char*)comp_ramfs_addr; ungzip_input.out = (unsigned char*)p_linuxtmp->ramfs_addr; ungzip_input.insize = comp_ramfs_size; ungzip_input.outsize = p_linuxtmp->ramfs_size; int pthread_ret = pthread_create(&handle_unzip_ramdisk, NULL, thread_ungzip_ramdisk , NULL); if (0 != pthread_ret) { DBG_ERR("create thread_ungzip_ramdisk failed, ret %d\r\n", pthread_ret); return -1; } return 0; #else DBG_ERR("unsupported\r\n"); return -1; #endif } #endif #if (CFG_INDEP_RAMDISK) static int load_ramdisk(LINUXTMP_PARTITION *p_linuxtmp) { switch(ramdisk_methold) { case RAMDISK_METHOD_PARTIAL: return load_ramdisk_partial(p_linuxtmp); case RAMDISK_METHOD_FULL: return load_ramdisk_full(p_linuxtmp); case RAMDISK_METHOD_CORE2: return load_ramdisk_core2(p_linuxtmp); case RAMDISK_METHOD_NVTPACK: return load_ramdisk_nvtpack(p_linuxtmp); default: DBG_ERR("unknown method: %d\r\n", ramdisk_methold); return -1; } } #endif #if (CFG_INDEP_RAMDISK) static int wait_ramdisk(void) { int join_ret; int pthread_ret; switch(ramdisk_methold) { case RAMDISK_METHOD_PARTIAL: return 0; //partial load + parital decompress have finished on load_ramdisk case RAMDISK_METHOD_FULL: case RAMDISK_METHOD_NVTPACK: pthread_ret = pthread_join(handle_unzip_ramdisk, (void *)&join_ret); if (0 != pthread_ret) { DBG_ERR("handle_unzip_ramdisk pthread_join failed, ret %d\r\n", pthread_ret); return -1; } return 0; case RAMDISK_METHOD_CORE2: while(*(volatile UINT32 *)CFG_CORE2_ENTRY_REG) { vos_cpu_dcache_sync(CFG_CORE2_ENTRY_REG, sizeof(UINT32), VOS_DMA_FROM_DEVICE); }; free(ungzip_input.gz_buf.p_begin); return 0; default: DBG_ERR("unknown method: %d\r\n", ramdisk_methold); return -1; } } #endif /* format : "A=B C=D" */ void linuxboot_set_extra_bootarg(char* bootarg) { snprintf(extra_bootarg, CFG_BOOTARG_EXTRA_MAX_LEN, bootarg); } static int make_bootargs(LINUXTMP_PARTITION *p_linuxtmp, unsigned int bootts_begin) { //static char bootargs[] = "root=/dev/ram0 rootfstype=ramfs rdinit=/linuxrc bootts=568047,1720128 resume_addr=0x00007e88 user_debug=0xff"; #if HUNTING_CAMERA_MCU == ENABLE char PowerOnModeStr[32] = {'\0'}; // if(sf_in_update()) // { // if(fastboot_wait_done_timeout(BOOT_INIT_FILESYSOK, FASTBOOT_WAIT_FILESYS_TIMEOUT_MS) != E_OK){ // { // } // } snprintf(PowerOnModeStr, sizeof(PowerOnModeStr), "Mode=%d UpFw=%d mcu=%d sf=%d",sf_get_power_on_mode(), sf_in_update() , sf_in_mcu_update(), sf_battery_level_get()); linuxboot_set_extra_bootarg(PowerOnModeStr); #endif p_linuxtmp->bootargs_addr = p_linuxtmp->tmp_curr; p_linuxtmp->bootargs_size = CFG_BOOTARG_MAX_LEN + CFG_BOOTARG_EXTRA_MAX_LEN; p_linuxtmp->tmp_curr += p_linuxtmp->bootargs_size; if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } memset((char *)p_linuxtmp->bootargs_addr, 0, p_linuxtmp->bootargs_size); snprintf((char *)p_linuxtmp->bootargs_addr, p_linuxtmp->bootargs_size - 1, CONFIG_BOOTARGS "bootts=%u,%u user_debug=0xff %s ", bootts_begin, (unsigned int)hwclock_get_counter(), extra_bootarg); return 0; } static int load_linux_from_flash(LINUXTMP_PARTITION *p_linuxtmp, FDT_INFO *p_fdt_info) { #if defined(_NVT_LINUX_COMPRESS_GZ_) //partital load + partial decompress FWSRV_CMD cmd = {0}; MEM_RANGE mem_range = {0}; FWSRV_FASTLOAD fastload = {0}; p_linuxtmp->lz_linux_addr = p_linuxtmp->tmp_curr; p_linuxtmp->lz_linux_size = CFG_LINUX_COMP_MAX_SIZE; p_linuxtmp->tmp_curr += p_linuxtmp->lz_linux_size; if (p_linuxtmp->tmp_curr >= p_linuxtmp->tmp_end) { DBG_ERR("linuxtmp memory is too small, need more %d\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_end); return -1; } fastload.pStrg = EMB_GETSTRGOBJ(STRG_OBJ_FW_LINUX); fastload.MemComp.addr = p_linuxtmp->lz_linux_addr; fastload.MemComp.size = p_linuxtmp->lz_linux_size; fastload.MemUnComp.addr = p_fdt_info->linux_addr + CFG_LINUX_START_OFFSET; fastload.MemUnComp.size = p_fdt_info->linux_size - CFG_LINUX_START_OFFSET; LINUX_BOOT_MSG("*linuxtmp used size = %lx , linux compressed / uncompressed size = %lx / %lx ******\n", p_linuxtmp->tmp_curr - p_linuxtmp->tmp_begin, fastload.MemComp.size, fastload.MemUnComp.size); if(p_linuxtmp->tmp_curr > p_fdt_info->hdal_addr){ DBG_ERR("linux tmp buffer overflow(curr:%lx), hdal media buffer(%lx) may be corrupted!!\n", p_linuxtmp->tmp_curr, p_fdt_info->hdal_addr); vos_util_delay_ms(100); // add delay to show dbg_fatal message in linux vos_debug_halt(); } cmd.Idx = FWSRV_CMD_IDX_FASTLOAD; cmd.In.pData = &fastload; cmd.In.uiNumByte = sizeof(fastload); cmd.Out.pData = &mem_range; cmd.Out.uiNumByte = sizeof(mem_range); cmd.Prop.bExitCmdFinish = TRUE; FWSRV_ER er; if ((er=FwSrv_Open()) != FWSRV_ER_OK) { DBG_ERR("FwSrv_Open failed!\r\n"); return -1; } if ((er=FwSrv_Cmd(&cmd)) != FWSRV_ER_OK) { DBG_ERR("FwSrv_Cmd failed!\r\n"); return -1; } FwSrv_Close(); return 0; #elif defined(_NVT_LINUX_COMPRESS_NONE_) // only full load unsigned int load_addr = p_fdt_info->linux_addr + CFG_LINUX_START_OFFSET - sizeof(image_header_t) - CFG_MULTI_MKIMAGE_LEN; STORAGE_OBJ *p_strg = EMB_GETSTRGOBJ(STRG_OBJ_FW_LINUX); //load 1st block p_strg->Lock(); p_strg->Open(); if (p_strg->RdSectors((INT8 *)load_addr, 0, 1) != 0) { DBG_ERR("RdSectors for linux 1st block\r\n"); return -1; } image_header_t *p_hdr = (image_header_t *)load_addr; if (be32_to_cpu(p_hdr->ih_magic) != IH_MAGIC) { DBG_ERR("IH_MAGIC is wrong\r\n"); return -1; } //IH_TYPE_MULTI = 4 if (p_hdr->ih_type != 4) { DBG_ERR("ih_type != IH_TYPE_MULTI\r\n"); return -1; } // load rest of image data unsigned int image_size = be32_to_cpu(p_hdr->ih_size) + sizeof(image_header_t); unsigned int block_size = 0; p_strg->GetParam(STRG_GET_BEST_ACCESS_SIZE, (UINT32)&block_size, 0); if (block_size == 0) { DBG_ERR("block_size is 0.\n"); return -1; } unsigned int remain_blocks = ALIGN_CEIL(image_size - block_size, block_size)/block_size; if (p_strg->RdSectors((INT8 *)(load_addr+block_size), 1, remain_blocks) != 0) { DBG_ERR("RdSectors for linux\r\n"); return -1; } p_strg->Close(); p_strg->Unlock(); return 0; #else DBG_ERR("unsupported NVT_LINUX_COMPRESS mode.\r\n"); return -1; #endif } static int load_linux_from_nvtpack(LINUXTMP_PARTITION *p_linuxtmp, FDT_INFO *p_fdt_info) { NVTPACK_ER er; NVTPACK_GET_PARTITION_INPUT np_get_input; NVTPACK_MEM mem_in = {(void *)p_linuxtmp->nvtpack_addr, (unsigned int)p_linuxtmp->nvtpack_size}; int nvtpack_linux_index = find_index_nvtpack(p_linuxtmp, "kernel"); if (nvtpack_linux_index== -1) { DBG_ERR("unable to find linux-nvtpack-index in fdt on sd card\r\n"); return -1; } NVTPACK_MEM linux_mem; np_get_input.id = nvtpack_linux_index; np_get_input.mem = mem_in; er = nvtpack_get_partition(&np_get_input, &linux_mem); if (er == NVTPACK_ER_NOT_FOUND) { DBG_ERR("unable to find linux in all-in-one\r\n"); return -1; } #if defined(_NVT_LINUX_COMPRESS_GZ_) //partital load + partial decompress NVTPACK_BFC_HDR *p_hdr = (NVTPACK_BFC_HDR *)linux_mem.p_data; if (p_hdr->uiFourCC != MAKEFOURCC('B','C','L','1')) { DBG_ERR("linux BCL1 header is wrong on sd card\r\n"); return -1; } int err; z_stream stream = {0}; stream.next_in = (z_const Bytef *)&p_hdr[1]; stream.avail_in = be32_to_cpu(p_hdr->uiSizeComp); stream.next_out = (z_const Bytef *)p_fdt_info->linux_addr + CFG_LINUX_START_OFFSET; stream.avail_out = be32_to_cpu(p_hdr->uiSizeUnComp); stream.zalloc = (alloc_func)gzalloc_full; stream.zfree = (free_func)gzfree_full; stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) { DBG_ERR("Failed to inflateInit, err = %d\r\n", err); inflateEnd(&stream); return -1; } err = inflate(&stream, Z_NO_FLUSH); inflateEnd(&stream); if (err == Z_STREAM_END) { return 0; } return -1; #elif defined(_NVT_LINUX_COMPRESS_NONE_) // only full load unsigned int load_addr = p_fdt_info->linux_addr + CFG_LINUX_START_OFFSET - sizeof(image_header_t) - CFG_MULTI_MKIMAGE_LEN; image_header_t *p_hdr = (image_header_t *)load_addr; if (be32_to_cpu(p_hdr->ih_magic) != IH_MAGIC) { DBG_ERR("IH_MAGIC is wrong\r\n"); return -1; } //IH_TYPE_MULTI = 4 if (p_hdr->ih_type != 4) { DBG_ERR("ih_type != IH_TYPE_MULTI\r\n"); return -1; } // load rest of image data unsigned int image_size = be32_to_cpu(p_hdr->ih_size) + sizeof(image_header_t); memcpy((void *)load_addr, linux_mem.p_data, image_size); return 0; #else DBG_ERR("unsupported NVT_LINUX_COMPRESS mode.\r\n"); return -1; #endif } static int load_linux(LINUXTMP_PARTITION *p_linuxtmp, FDT_INFO *p_fdt_info) { #if 0 SHMINFO *p_shm = (SHMINFO *)p_fdt_info->shmem_addr; if ((p_shm->boot.LdCtrl2 & LDCF_BOOT_CARD) == 0) { return load_linux_from_flash(p_linuxtmp, p_fdt_info); } else { return load_linux_from_nvtpack(p_linuxtmp, p_fdt_info); } #else return load_linux_from_flash(p_linuxtmp, p_fdt_info); #endif } static int check_mem_overlap(FDT_INFO *p_fdt_info) { //rule1: fdt, rtos, bridge must be in order and continuous if (p_fdt_info->fdt_addr + p_fdt_info->fdt_size != p_fdt_info->rtos_addr) { DBG_ERR("in nvt_memory_cfg, the mem of fdt, rtos, bridge must be in order.\r\n"); return -1; } //reule 2: (fdt, rtos, bridge cannot be overlapped with linux) unsigned int mem_begin = p_fdt_info->fdt_addr; unsigned int mem_end = mem_begin + p_fdt_info->fdt_size + p_fdt_info->rtos_size; unsigned int linux_begin = p_fdt_info->linux_addr; unsigned int linux_end = linux_begin + p_fdt_info->linux_size; if (linux_end > mem_begin && linux_begin < mem_end) { DBG_ERR("linux mem is overlapped with (fdt+rtos+bridge)\r\n"); return -1; } //reule 3: (fdt, rtos, bridge cannot be overlapped with hdal) unsigned int hdal_begin = p_fdt_info->hdal_addr; unsigned int hdal_end = hdal_begin + p_fdt_info->hdal_size; if (hdal_end > mem_begin && hdal_begin < mem_end) { DBG_ERR("hdal mem is overlapped with (fdt+rtos+bridge)\r\n"); return -1; } return 0; } #if (_PACKAGE_FILESYS_ && _PACKAGE_SDCARD_) static int load_nvtpack(LINUXTMP_PARTITION *p_linuxtmp) { //polling filesys ready first int n_retry = 5; // 5 sec while (n_retry--) { if (FileSys_WaitFinishEx('A') == FST_STA_OK) { break; } vos_util_delay_ms(1000); } char bint_path[] = "A:\\" _BIN_NAME_T_ ".bin"; char bin_path[] = "A:\\" _BIN_NAME_ ".bin"; char *nvtpack_path = bint_path; FST_FILE h_file = FileSys_OpenFile(bint_path, FST_OPEN_READ); if (h_file == 0) { h_file = FileSys_OpenFile(bin_path, FST_OPEN_READ); nvtpack_path = bin_path; } if (h_file == 0) { DBG_ERR("unable to open %s or %s\r\n", bint_path, bin_path); return -1; } int nvtpack_len = FileSys_GetFileLen(nvtpack_path); unsigned int alloc_size = ALIGN_CEIL_64(nvtpack_len); if (p_linuxtmp->tmp_end-p_linuxtmp->tmp_curr < alloc_size) { DBG_ERR("linuxtmp size too small to alloc nvtpack size %d bytes\r\n", alloc_size); return -1; } p_linuxtmp->nvtpack_size = alloc_size; p_linuxtmp->nvtpack_addr = p_linuxtmp->tmp_end - p_linuxtmp->nvtpack_size; p_linuxtmp->tmp_end = p_linuxtmp->nvtpack_addr; // use the bottom of linuxtmp memory UINT32 fw_size = (UINT32)nvtpack_len; INT32 fst_er = FileSys_ReadFile(h_file, (UINT8 *)p_linuxtmp->nvtpack_addr, &fw_size, 0, NULL); FileSys_CloseFile(h_file); if (fst_er != FST_STA_OK) { DBG_ERR("FW bin read fail\r\n"); return -1; } return 0; } #endif int linuxboot_setup(LINUXBOOT_INFO *p_info) { int er; unsigned int bootts_begin = hwclock_get_counter(); LINUXTMP_PARTITION *p_linuxtmp = &p_info->linuxtmp; FDT_INFO *p_fdt_info = &p_info->fdt_info; if ((er=fdt_get_info(p_fdt_info)) != 0) { return er; } if ((er=check_mem_overlap(p_fdt_info)) != 0) { return er; } p_linuxtmp->tmp_begin = p_fdt_info->linuxtmp_addr; p_linuxtmp->tmp_curr = p_linuxtmp->tmp_begin; p_linuxtmp->tmp_end = p_linuxtmp->tmp_begin + p_fdt_info->linuxtmp_size; if (p_fdt_info->dram_size <= 0x04000000) { DBG_WRN("small dram.\n"); p_linuxtmp->tmp2_begin = p_fdt_info->linux_addr + CFG_LINUX_MAX_CODE_SIZE; p_linuxtmp->tmp2_curr = p_linuxtmp->tmp2_begin; p_linuxtmp->tmp2_end = p_fdt_info->fdt_addr; } #if 0 SHMINFO *p_shm = (SHMINFO *)p_fdt_info->shmem_addr; if (p_shm->boot.LdCtrl2 & LDCF_BOOT_CARD) { #if (_PACKAGE_FILESYS_ && _PACKAGE_SDCARD_) // for boot linux from all-in-one in sd card if ((er=load_nvtpack(p_linuxtmp)) !=0) { return er; } #if (CFG_INDEP_RAMDISK) // force load ramdisk by this method ramdisk_methold = RAMDISK_METHOD_NVTPACK; #endif #else DBG_ERR("for boot from sd, _PACKAGE_FILESYS_ and _PACKAGE_SDCARD_ must enable\r\n"); return -1; #endif } #else (void) load_linux_from_nvtpack; (void) load_nvtpack; #endif // create new fdt for nodes of bootargs and ramdisk if ((er=create_new_fdt(p_linuxtmp)) != 0) { return er; } if(DrvGPIO_GetPhotoMovieModeFromMonitor() == DX_HUNTING_MODE_MOVIE2){ DBG_DUMP("fastboot wait BOOT_FLOW_MOVIE2...\n"); fastboot_wait_done(BOOT_FLOW_MOVIE2); DBG_DUMP("fastboot wait BOOT_FLOW_MOVIE2 ok\n"); } #if (CFG_INDEP_RAMDISK) // load ramdisk into memory and reserved memory if ((er=load_ramdisk(p_linuxtmp)) != 0) { return er; } #endif if ((er=load_linux(p_linuxtmp, p_fdt_info)) != 0) { return er; } #if (CFG_INDEP_RAMDISK) // wait ramdisk decompression thread finish (only for _NVT_LINUX_COMPRESS_NONE_) if ((er=wait_ramdisk()) != 0) { return er; } #endif // make bootargs if ((er=make_bootargs(p_linuxtmp, bootts_begin)) != 0) { return er; } // insert bootargs and ramdisk into fdt if ((er=fdt_chosen(p_linuxtmp)) != 0) { return er; } return 0; } int linuxboot_set_flash_preload(LINUXBOOT_INFO *p_info) { int value = 1; void *fdt = (void *)p_info->linuxtmp.fdt_addr; int nodeoffset = fdt_path_offset(fdt, "/fastboot"); if (nodeoffset < 0) { return nodeoffset; } /* find or create /fastboot/spi-nor node. */ nodeoffset = fdt_find_or_add_subnode(fdt, nodeoffset, "spi-nor"); if (nodeoffset < 0) return nodeoffset; int err = fdt_setprop(fdt, nodeoffset, "preload", &value, sizeof(value)); if (err < 0) { DBG_DUMP("WARNING: could not set spi-nor/preload %s.\n", fdt_strerror(err)); return err; } return 0; } void linuxboot_go(LINUXBOOT_INFO *p_info) { LINUXTMP_PARTITION *p_linuxtmp = &p_info->linuxtmp; FDT_INFO *p_fdt_info = &p_info->fdt_info; unsigned int r2 = p_linuxtmp->fdt_addr; unsigned int kernel_addr = p_fdt_info->linux_addr + CFG_LINUX_START_OFFSET; cpu_disable_interrupt(); cpu_disable_cache(); cpu_disable_mmu(); //alway enter #Mode_SVC before start linux __asm__("cps #0x13"); { void (*kernel_entry)(int zero, int arch, unsigned int params); kernel_entry = (void (*)(int, int, unsigned int))((kernel_addr)); kernel_entry(0, 0, r2); // never returned } }