18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Purgatory setup code 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2018 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Philipp Rudo <prudo@linux.vnet.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/linkage.h> 118c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h> 128c2ecf20Sopenharmony_ci#include <asm/page.h> 138c2ecf20Sopenharmony_ci#include <asm/sigp.h> 148c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* The purgatory is the code running between two kernels. It's main purpose 178c2ecf20Sopenharmony_ci * is to verify that the next kernel was not corrupted after load and to 188c2ecf20Sopenharmony_ci * start it. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * If the next kernel is a crash kernel there are some peculiarities to 218c2ecf20Sopenharmony_ci * consider: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * First the purgatory is called twice. Once only to verify the 248c2ecf20Sopenharmony_ci * sha digest. So if the crash kernel got corrupted the old kernel can try 258c2ecf20Sopenharmony_ci * to trigger a stand-alone dumper. And once to actually load the crash kernel. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Second the purgatory also has to swap the crash memory region with its 288c2ecf20Sopenharmony_ci * destination at address 0. As the purgatory is part of crash memory this 298c2ecf20Sopenharmony_ci * requires some finesse. The tactic here is that the purgatory first copies 308c2ecf20Sopenharmony_ci * itself to the end of the destination and then swaps the rest of the 318c2ecf20Sopenharmony_ci * memory running from there. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define bufsz purgatory_end-stack 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci.macro MEMCPY dst,src,len 378c2ecf20Sopenharmony_ci lgr %r0,\dst 388c2ecf20Sopenharmony_ci lgr %r1,\len 398c2ecf20Sopenharmony_ci lgr %r2,\src 408c2ecf20Sopenharmony_ci lgr %r3,\len 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci20: mvcle %r0,%r2,0 438c2ecf20Sopenharmony_ci jo 20b 448c2ecf20Sopenharmony_ci.endm 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci.macro MEMSWAP dst,src,buf,len 478c2ecf20Sopenharmony_ci10: cghi \len,bufsz 488c2ecf20Sopenharmony_ci jh 11f 498c2ecf20Sopenharmony_ci lgr %r4,\len 508c2ecf20Sopenharmony_ci j 12f 518c2ecf20Sopenharmony_ci11: lghi %r4,bufsz 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci12: MEMCPY \buf,\dst,%r4 548c2ecf20Sopenharmony_ci MEMCPY \dst,\src,%r4 558c2ecf20Sopenharmony_ci MEMCPY \src,\buf,%r4 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci agr \dst,%r4 588c2ecf20Sopenharmony_ci agr \src,%r4 598c2ecf20Sopenharmony_ci sgr \len,%r4 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci cghi \len,0 628c2ecf20Sopenharmony_ci jh 10b 638c2ecf20Sopenharmony_ci.endm 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci.macro START_NEXT_KERNEL base subcode 668c2ecf20Sopenharmony_ci lg %r4,kernel_entry-\base(%r13) 678c2ecf20Sopenharmony_ci lg %r5,load_psw_mask-\base(%r13) 688c2ecf20Sopenharmony_ci ogr %r4,%r5 698c2ecf20Sopenharmony_ci stg %r4,0(%r0) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci xgr %r0,%r0 728c2ecf20Sopenharmony_ci lghi %r1,\subcode 738c2ecf20Sopenharmony_ci diag %r0,%r1,0x308 748c2ecf20Sopenharmony_ci.endm 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci.text 778c2ecf20Sopenharmony_ci.align PAGE_SIZE 788c2ecf20Sopenharmony_ciENTRY(purgatory_start) 798c2ecf20Sopenharmony_ci /* The purgatory might be called after a diag308 so better set 808c2ecf20Sopenharmony_ci * architecture and addressing mode. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci lhi %r1,1 838c2ecf20Sopenharmony_ci sigp %r1,%r0,SIGP_SET_ARCHITECTURE 848c2ecf20Sopenharmony_ci sam64 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci larl %r5,gprregs 878c2ecf20Sopenharmony_ci stmg %r6,%r15,0(%r5) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci basr %r13,0 908c2ecf20Sopenharmony_ci.base_crash: 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Setup stack */ 938c2ecf20Sopenharmony_ci larl %r15,purgatory_end-STACK_FRAME_OVERHEAD 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* If the next kernel is KEXEC_TYPE_CRASH the purgatory is called 968c2ecf20Sopenharmony_ci * directly with a flag passed in %r2 whether the purgatory shall do 978c2ecf20Sopenharmony_ci * checksum verification only (%r2 = 0 -> verification only). 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Check now and preserve over C function call by storing in 1008c2ecf20Sopenharmony_ci * %r10 whith 1018c2ecf20Sopenharmony_ci * 1 -> checksum verification only 1028c2ecf20Sopenharmony_ci * 0 -> load new kernel 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci lghi %r10,0 1058c2ecf20Sopenharmony_ci lg %r11,kernel_type-.base_crash(%r13) 1068c2ecf20Sopenharmony_ci cghi %r11,1 /* KEXEC_TYPE_CRASH */ 1078c2ecf20Sopenharmony_ci jne .do_checksum_verification 1088c2ecf20Sopenharmony_ci cghi %r2,0 /* checksum verification only */ 1098c2ecf20Sopenharmony_ci jne .do_checksum_verification 1108c2ecf20Sopenharmony_ci lghi %r10,1 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci.do_checksum_verification: 1138c2ecf20Sopenharmony_ci brasl %r14,verify_sha256_digest 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci cghi %r10,1 /* checksum verification only */ 1168c2ecf20Sopenharmony_ci je .return_old_kernel 1178c2ecf20Sopenharmony_ci cghi %r2,0 /* checksum match */ 1188c2ecf20Sopenharmony_ci jne .disabled_wait 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* If the next kernel is a crash kernel the purgatory has to swap 1218c2ecf20Sopenharmony_ci * the mem regions first. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci cghi %r11,1 /* KEXEC_TYPE_CRASH */ 1248c2ecf20Sopenharmony_ci je .start_crash_kernel 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* start normal kernel */ 1278c2ecf20Sopenharmony_ci START_NEXT_KERNEL .base_crash 0 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci.return_old_kernel: 1308c2ecf20Sopenharmony_ci lmg %r6,%r15,gprregs-.base_crash(%r13) 1318c2ecf20Sopenharmony_ci br %r14 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci.disabled_wait: 1348c2ecf20Sopenharmony_ci lpswe disabled_wait_psw-.base_crash(%r13) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci.start_crash_kernel: 1378c2ecf20Sopenharmony_ci /* Location of purgatory_start in crash memory */ 1388c2ecf20Sopenharmony_ci lgr %r8,%r13 1398c2ecf20Sopenharmony_ci aghi %r8,-(.base_crash-purgatory_start) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Destination for this code i.e. end of memory to be swapped. */ 1428c2ecf20Sopenharmony_ci lg %r9,crash_size-.base_crash(%r13) 1438c2ecf20Sopenharmony_ci aghi %r9,-(purgatory_end-purgatory_start) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Destination in crash memory, i.e. same as r9 but in crash memory. */ 1468c2ecf20Sopenharmony_ci lg %r10,crash_start-.base_crash(%r13) 1478c2ecf20Sopenharmony_ci agr %r10,%r9 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Buffer location (in crash memory) and size. As the purgatory is 1508c2ecf20Sopenharmony_ci * behind the point of no return it can re-use the stack as buffer. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci lghi %r11,bufsz 1538c2ecf20Sopenharmony_ci larl %r12,stack 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci MEMCPY %r12,%r9,%r11 /* dst -> (crash) buf */ 1568c2ecf20Sopenharmony_ci MEMCPY %r9,%r8,%r11 /* self -> dst */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Jump to new location. */ 1598c2ecf20Sopenharmony_ci lgr %r7,%r9 1608c2ecf20Sopenharmony_ci aghi %r7,.jump_to_dst-purgatory_start 1618c2ecf20Sopenharmony_ci br %r7 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci.jump_to_dst: 1648c2ecf20Sopenharmony_ci basr %r13,0 1658c2ecf20Sopenharmony_ci.base_dst: 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* clear buffer */ 1688c2ecf20Sopenharmony_ci MEMCPY %r12,%r10,%r11 /* (crash) buf -> (crash) dst */ 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Load new buffer location after jump */ 1718c2ecf20Sopenharmony_ci larl %r7,stack 1728c2ecf20Sopenharmony_ci aghi %r10,stack-purgatory_start 1738c2ecf20Sopenharmony_ci MEMCPY %r10,%r7,%r11 /* (new) buf -> (crash) buf */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Now the code is set up to run from its designated location. Start 1768c2ecf20Sopenharmony_ci * swapping the rest of crash memory now. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * The registers will be used as follow: 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * %r0-%r4 reserved for macros defined above 1818c2ecf20Sopenharmony_ci * %r5-%r6 tmp registers 1828c2ecf20Sopenharmony_ci * %r7 pointer to current struct sha region 1838c2ecf20Sopenharmony_ci * %r8 index to iterate over all sha regions 1848c2ecf20Sopenharmony_ci * %r9 pointer in crash memory 1858c2ecf20Sopenharmony_ci * %r10 pointer in old kernel 1868c2ecf20Sopenharmony_ci * %r11 total size (still) to be moved 1878c2ecf20Sopenharmony_ci * %r12 pointer to buffer 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci lgr %r12,%r7 1908c2ecf20Sopenharmony_ci lgr %r11,%r9 1918c2ecf20Sopenharmony_ci lghi %r10,0 1928c2ecf20Sopenharmony_ci lg %r9,crash_start-.base_dst(%r13) 1938c2ecf20Sopenharmony_ci lghi %r8,16 /* KEXEC_SEGMENTS_MAX */ 1948c2ecf20Sopenharmony_ci larl %r7,purgatory_sha_regions 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci j .loop_first 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Loop over all purgatory_sha_regions. */ 1998c2ecf20Sopenharmony_ci.loop_next: 2008c2ecf20Sopenharmony_ci aghi %r8,-1 2018c2ecf20Sopenharmony_ci cghi %r8,0 2028c2ecf20Sopenharmony_ci je .loop_out 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci aghi %r7,__KEXEC_SHA_REGION_SIZE 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci.loop_first: 2078c2ecf20Sopenharmony_ci lg %r5,__KEXEC_SHA_REGION_START(%r7) 2088c2ecf20Sopenharmony_ci cghi %r5,0 2098c2ecf20Sopenharmony_ci je .loop_next 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Copy [end last sha region, start current sha region) */ 2128c2ecf20Sopenharmony_ci /* Note: kexec_sha_region->start points in crash memory */ 2138c2ecf20Sopenharmony_ci sgr %r5,%r9 2148c2ecf20Sopenharmony_ci MEMCPY %r9,%r10,%r5 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci agr %r9,%r5 2178c2ecf20Sopenharmony_ci agr %r10,%r5 2188c2ecf20Sopenharmony_ci sgr %r11,%r5 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Swap sha region */ 2218c2ecf20Sopenharmony_ci lg %r6,__KEXEC_SHA_REGION_LEN(%r7) 2228c2ecf20Sopenharmony_ci MEMSWAP %r9,%r10,%r12,%r6 2238c2ecf20Sopenharmony_ci sg %r11,__KEXEC_SHA_REGION_LEN(%r7) 2248c2ecf20Sopenharmony_ci j .loop_next 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci.loop_out: 2278c2ecf20Sopenharmony_ci /* Copy rest of crash memory */ 2288c2ecf20Sopenharmony_ci MEMCPY %r9,%r10,%r11 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* start crash kernel */ 2318c2ecf20Sopenharmony_ci START_NEXT_KERNEL .base_dst 1 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciload_psw_mask: 2358c2ecf20Sopenharmony_ci .long 0x00080000,0x80000000 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci .align 8 2388c2ecf20Sopenharmony_cidisabled_wait_psw: 2398c2ecf20Sopenharmony_ci .quad 0x0002000180000000 2408c2ecf20Sopenharmony_ci .quad 0x0000000000000000 + .do_checksum_verification 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cigprregs: 2438c2ecf20Sopenharmony_ci .rept 10 2448c2ecf20Sopenharmony_ci .quad 0 2458c2ecf20Sopenharmony_ci .endr 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* Macro to define a global variable with name and size (in bytes) to be 2488c2ecf20Sopenharmony_ci * shared with C code. 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * Add the .size and .type attribute to satisfy checks on the Elf_Sym during 2518c2ecf20Sopenharmony_ci * purgatory load. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci.macro GLOBAL_VARIABLE name,size 2548c2ecf20Sopenharmony_ci\name: 2558c2ecf20Sopenharmony_ci .global \name 2568c2ecf20Sopenharmony_ci .size \name,\size 2578c2ecf20Sopenharmony_ci .type \name,object 2588c2ecf20Sopenharmony_ci .skip \size,0 2598c2ecf20Sopenharmony_ci.endm 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ciGLOBAL_VARIABLE purgatory_sha256_digest,32 2628c2ecf20Sopenharmony_ciGLOBAL_VARIABLE purgatory_sha_regions,16*__KEXEC_SHA_REGION_SIZE 2638c2ecf20Sopenharmony_ciGLOBAL_VARIABLE kernel_entry,8 2648c2ecf20Sopenharmony_ciGLOBAL_VARIABLE kernel_type,8 2658c2ecf20Sopenharmony_ciGLOBAL_VARIABLE crash_start,8 2668c2ecf20Sopenharmony_ciGLOBAL_VARIABLE crash_size,8 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci .align PAGE_SIZE 2698c2ecf20Sopenharmony_cistack: 2708c2ecf20Sopenharmony_ci /* The buffer to move this code must be as big as the code. */ 2718c2ecf20Sopenharmony_ci .skip stack-purgatory_start 2728c2ecf20Sopenharmony_ci .align PAGE_SIZE 2738c2ecf20Sopenharmony_cipurgatory_end: 274