From 6a6c6083fa69575334282d0c8f5dd688a2282188 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Wed, 16 Mar 2022 15:39:18 +0000 Subject: x1000: bootloader: fix Linux self-extracting kernel boot Basically, there's longstanding bug in Linux with self-extracting kernels on MIPS which just happened to manifest now on the M3K as a hang on boot. The fix is applied to the M3K and Q1 since they both use this type of kernel image. Change-Id: I17d2bad6eebd677cd6d2e0bf146450c71fcf1229 --- bootloader/x1000/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'bootloader/x1000/boot.c') diff --git a/bootloader/x1000/boot.c b/bootloader/x1000/boot.c index 153c2277aa..d6dfd4a193 100644 --- a/bootloader/x1000/boot.c +++ b/bootloader/x1000/boot.c @@ -148,6 +148,35 @@ void boot_linux(void) * Be careful when modifying this code. */ +#if defined(FIIO_M3K) || defined(SHANLING_Q1) +uint32_t saved_kernel_entry __attribute__((section(".idata"))); +void kernel_thunk(long, long, long, long) __attribute__((section(".icode"))); + +void kernel_thunk(long a0, long a1, long a2, long a3) +{ + /* cache flush */ + commit_discard_idcache(); + + /* now we can jump to the kernel */ + typedef void(*entry_fn)(long, long, long, long); + entry_fn fn = (entry_fn)saved_kernel_entry; + fn(a0, a1, a2, a3); + while(1); +} + +static void patch_stub_call(void* patch_addr) +{ + uint32_t* code = patch_addr; + uint32_t stub_addr = (uint32_t)(void*)kernel_thunk; + + /* generate call to stub */ + code[0] = 0x3c190000 | (stub_addr >> 16); /* lui t9, stub_hi */ + code[1] = 0x37390000 | (stub_addr & 0xffff); /* ori t9, t9, stub_lo */ + code[2] = 0x0320f809; /* jalr t9 */ + code[3] = 0x00000000; /* nop */ +} +#endif + static __attribute__((unused)) void boot_of_helper(uint32_t addr, uint32_t flash_size, const char* args) { @@ -157,6 +186,19 @@ void boot_of_helper(uint32_t addr, uint32_t flash_size, const char* args) if(handle < 0) return; +#if defined(FIIO_M3K) || defined(SHANLING_Q1) + /* Fix for targets that use self-extracting kernel images */ + void* jump_addr = core_get_data(handle); + uint32_t entry_addr = mips_linux_stub_get_entry(&jump_addr, img_length); + if(entry_addr >= 0xa0000000 || entry_addr < 0x80000000) { + splash2(5*HZ, "Kernel patch failed", "Please send bugreport"); + return; + } + + saved_kernel_entry = entry_addr; + patch_stub_call(jump_addr); +#endif + gui_shutdown(); x1000_dualboot_load_pdma_fw(); -- cgit v1.2.3