1*4882a593Smuzhiyun/* SPDX-License-Identifier: GPL-2.0 */ 2*4882a593Smuzhiyun/* 3*4882a593Smuzhiyun * Purgatory setup code 4*4882a593Smuzhiyun * 5*4882a593Smuzhiyun * Copyright IBM Corp. 2018 6*4882a593Smuzhiyun * 7*4882a593Smuzhiyun * Author(s): Philipp Rudo <prudo@linux.vnet.ibm.com> 8*4882a593Smuzhiyun */ 9*4882a593Smuzhiyun 10*4882a593Smuzhiyun#include <linux/linkage.h> 11*4882a593Smuzhiyun#include <asm/asm-offsets.h> 12*4882a593Smuzhiyun#include <asm/page.h> 13*4882a593Smuzhiyun#include <asm/sigp.h> 14*4882a593Smuzhiyun#include <asm/ptrace.h> 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun/* The purgatory is the code running between two kernels. It's main purpose 17*4882a593Smuzhiyun * is to verify that the next kernel was not corrupted after load and to 18*4882a593Smuzhiyun * start it. 19*4882a593Smuzhiyun * 20*4882a593Smuzhiyun * If the next kernel is a crash kernel there are some peculiarities to 21*4882a593Smuzhiyun * consider: 22*4882a593Smuzhiyun * 23*4882a593Smuzhiyun * First the purgatory is called twice. Once only to verify the 24*4882a593Smuzhiyun * sha digest. So if the crash kernel got corrupted the old kernel can try 25*4882a593Smuzhiyun * to trigger a stand-alone dumper. And once to actually load the crash kernel. 26*4882a593Smuzhiyun * 27*4882a593Smuzhiyun * Second the purgatory also has to swap the crash memory region with its 28*4882a593Smuzhiyun * destination at address 0. As the purgatory is part of crash memory this 29*4882a593Smuzhiyun * requires some finesse. The tactic here is that the purgatory first copies 30*4882a593Smuzhiyun * itself to the end of the destination and then swaps the rest of the 31*4882a593Smuzhiyun * memory running from there. 32*4882a593Smuzhiyun */ 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun#define bufsz purgatory_end-stack 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun.macro MEMCPY dst,src,len 37*4882a593Smuzhiyun lgr %r0,\dst 38*4882a593Smuzhiyun lgr %r1,\len 39*4882a593Smuzhiyun lgr %r2,\src 40*4882a593Smuzhiyun lgr %r3,\len 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun20: mvcle %r0,%r2,0 43*4882a593Smuzhiyun jo 20b 44*4882a593Smuzhiyun.endm 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun.macro MEMSWAP dst,src,buf,len 47*4882a593Smuzhiyun10: cghi \len,bufsz 48*4882a593Smuzhiyun jh 11f 49*4882a593Smuzhiyun lgr %r4,\len 50*4882a593Smuzhiyun j 12f 51*4882a593Smuzhiyun11: lghi %r4,bufsz 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun12: MEMCPY \buf,\dst,%r4 54*4882a593Smuzhiyun MEMCPY \dst,\src,%r4 55*4882a593Smuzhiyun MEMCPY \src,\buf,%r4 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun agr \dst,%r4 58*4882a593Smuzhiyun agr \src,%r4 59*4882a593Smuzhiyun sgr \len,%r4 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun cghi \len,0 62*4882a593Smuzhiyun jh 10b 63*4882a593Smuzhiyun.endm 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun.macro START_NEXT_KERNEL base subcode 66*4882a593Smuzhiyun lg %r4,kernel_entry-\base(%r13) 67*4882a593Smuzhiyun lg %r5,load_psw_mask-\base(%r13) 68*4882a593Smuzhiyun ogr %r4,%r5 69*4882a593Smuzhiyun stg %r4,0(%r0) 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun xgr %r0,%r0 72*4882a593Smuzhiyun lghi %r1,\subcode 73*4882a593Smuzhiyun diag %r0,%r1,0x308 74*4882a593Smuzhiyun.endm 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun.text 77*4882a593Smuzhiyun.align PAGE_SIZE 78*4882a593SmuzhiyunENTRY(purgatory_start) 79*4882a593Smuzhiyun /* The purgatory might be called after a diag308 so better set 80*4882a593Smuzhiyun * architecture and addressing mode. 81*4882a593Smuzhiyun */ 82*4882a593Smuzhiyun lhi %r1,1 83*4882a593Smuzhiyun sigp %r1,%r0,SIGP_SET_ARCHITECTURE 84*4882a593Smuzhiyun sam64 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun larl %r5,gprregs 87*4882a593Smuzhiyun stmg %r6,%r15,0(%r5) 88*4882a593Smuzhiyun 89*4882a593Smuzhiyun basr %r13,0 90*4882a593Smuzhiyun.base_crash: 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun /* Setup stack */ 93*4882a593Smuzhiyun larl %r15,purgatory_end-STACK_FRAME_OVERHEAD 94*4882a593Smuzhiyun 95*4882a593Smuzhiyun /* If the next kernel is KEXEC_TYPE_CRASH the purgatory is called 96*4882a593Smuzhiyun * directly with a flag passed in %r2 whether the purgatory shall do 97*4882a593Smuzhiyun * checksum verification only (%r2 = 0 -> verification only). 98*4882a593Smuzhiyun * 99*4882a593Smuzhiyun * Check now and preserve over C function call by storing in 100*4882a593Smuzhiyun * %r10 whith 101*4882a593Smuzhiyun * 1 -> checksum verification only 102*4882a593Smuzhiyun * 0 -> load new kernel 103*4882a593Smuzhiyun */ 104*4882a593Smuzhiyun lghi %r10,0 105*4882a593Smuzhiyun lg %r11,kernel_type-.base_crash(%r13) 106*4882a593Smuzhiyun cghi %r11,1 /* KEXEC_TYPE_CRASH */ 107*4882a593Smuzhiyun jne .do_checksum_verification 108*4882a593Smuzhiyun cghi %r2,0 /* checksum verification only */ 109*4882a593Smuzhiyun jne .do_checksum_verification 110*4882a593Smuzhiyun lghi %r10,1 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun.do_checksum_verification: 113*4882a593Smuzhiyun brasl %r14,verify_sha256_digest 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun cghi %r10,1 /* checksum verification only */ 116*4882a593Smuzhiyun je .return_old_kernel 117*4882a593Smuzhiyun cghi %r2,0 /* checksum match */ 118*4882a593Smuzhiyun jne .disabled_wait 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun /* If the next kernel is a crash kernel the purgatory has to swap 121*4882a593Smuzhiyun * the mem regions first. 122*4882a593Smuzhiyun */ 123*4882a593Smuzhiyun cghi %r11,1 /* KEXEC_TYPE_CRASH */ 124*4882a593Smuzhiyun je .start_crash_kernel 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun /* start normal kernel */ 127*4882a593Smuzhiyun START_NEXT_KERNEL .base_crash 0 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun.return_old_kernel: 130*4882a593Smuzhiyun lmg %r6,%r15,gprregs-.base_crash(%r13) 131*4882a593Smuzhiyun br %r14 132*4882a593Smuzhiyun 133*4882a593Smuzhiyun.disabled_wait: 134*4882a593Smuzhiyun lpswe disabled_wait_psw-.base_crash(%r13) 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun.start_crash_kernel: 137*4882a593Smuzhiyun /* Location of purgatory_start in crash memory */ 138*4882a593Smuzhiyun lgr %r8,%r13 139*4882a593Smuzhiyun aghi %r8,-(.base_crash-purgatory_start) 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun /* Destination for this code i.e. end of memory to be swapped. */ 142*4882a593Smuzhiyun lg %r9,crash_size-.base_crash(%r13) 143*4882a593Smuzhiyun aghi %r9,-(purgatory_end-purgatory_start) 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun /* Destination in crash memory, i.e. same as r9 but in crash memory. */ 146*4882a593Smuzhiyun lg %r10,crash_start-.base_crash(%r13) 147*4882a593Smuzhiyun agr %r10,%r9 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun /* Buffer location (in crash memory) and size. As the purgatory is 150*4882a593Smuzhiyun * behind the point of no return it can re-use the stack as buffer. 151*4882a593Smuzhiyun */ 152*4882a593Smuzhiyun lghi %r11,bufsz 153*4882a593Smuzhiyun larl %r12,stack 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun MEMCPY %r12,%r9,%r11 /* dst -> (crash) buf */ 156*4882a593Smuzhiyun MEMCPY %r9,%r8,%r11 /* self -> dst */ 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun /* Jump to new location. */ 159*4882a593Smuzhiyun lgr %r7,%r9 160*4882a593Smuzhiyun aghi %r7,.jump_to_dst-purgatory_start 161*4882a593Smuzhiyun br %r7 162*4882a593Smuzhiyun 163*4882a593Smuzhiyun.jump_to_dst: 164*4882a593Smuzhiyun basr %r13,0 165*4882a593Smuzhiyun.base_dst: 166*4882a593Smuzhiyun 167*4882a593Smuzhiyun /* clear buffer */ 168*4882a593Smuzhiyun MEMCPY %r12,%r10,%r11 /* (crash) buf -> (crash) dst */ 169*4882a593Smuzhiyun 170*4882a593Smuzhiyun /* Load new buffer location after jump */ 171*4882a593Smuzhiyun larl %r7,stack 172*4882a593Smuzhiyun aghi %r10,stack-purgatory_start 173*4882a593Smuzhiyun MEMCPY %r10,%r7,%r11 /* (new) buf -> (crash) buf */ 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun /* Now the code is set up to run from its designated location. Start 176*4882a593Smuzhiyun * swapping the rest of crash memory now. 177*4882a593Smuzhiyun * 178*4882a593Smuzhiyun * The registers will be used as follow: 179*4882a593Smuzhiyun * 180*4882a593Smuzhiyun * %r0-%r4 reserved for macros defined above 181*4882a593Smuzhiyun * %r5-%r6 tmp registers 182*4882a593Smuzhiyun * %r7 pointer to current struct sha region 183*4882a593Smuzhiyun * %r8 index to iterate over all sha regions 184*4882a593Smuzhiyun * %r9 pointer in crash memory 185*4882a593Smuzhiyun * %r10 pointer in old kernel 186*4882a593Smuzhiyun * %r11 total size (still) to be moved 187*4882a593Smuzhiyun * %r12 pointer to buffer 188*4882a593Smuzhiyun */ 189*4882a593Smuzhiyun lgr %r12,%r7 190*4882a593Smuzhiyun lgr %r11,%r9 191*4882a593Smuzhiyun lghi %r10,0 192*4882a593Smuzhiyun lg %r9,crash_start-.base_dst(%r13) 193*4882a593Smuzhiyun lghi %r8,16 /* KEXEC_SEGMENTS_MAX */ 194*4882a593Smuzhiyun larl %r7,purgatory_sha_regions 195*4882a593Smuzhiyun 196*4882a593Smuzhiyun j .loop_first 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun /* Loop over all purgatory_sha_regions. */ 199*4882a593Smuzhiyun.loop_next: 200*4882a593Smuzhiyun aghi %r8,-1 201*4882a593Smuzhiyun cghi %r8,0 202*4882a593Smuzhiyun je .loop_out 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun aghi %r7,__KEXEC_SHA_REGION_SIZE 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun.loop_first: 207*4882a593Smuzhiyun lg %r5,__KEXEC_SHA_REGION_START(%r7) 208*4882a593Smuzhiyun cghi %r5,0 209*4882a593Smuzhiyun je .loop_next 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun /* Copy [end last sha region, start current sha region) */ 212*4882a593Smuzhiyun /* Note: kexec_sha_region->start points in crash memory */ 213*4882a593Smuzhiyun sgr %r5,%r9 214*4882a593Smuzhiyun MEMCPY %r9,%r10,%r5 215*4882a593Smuzhiyun 216*4882a593Smuzhiyun agr %r9,%r5 217*4882a593Smuzhiyun agr %r10,%r5 218*4882a593Smuzhiyun sgr %r11,%r5 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun /* Swap sha region */ 221*4882a593Smuzhiyun lg %r6,__KEXEC_SHA_REGION_LEN(%r7) 222*4882a593Smuzhiyun MEMSWAP %r9,%r10,%r12,%r6 223*4882a593Smuzhiyun sg %r11,__KEXEC_SHA_REGION_LEN(%r7) 224*4882a593Smuzhiyun j .loop_next 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun.loop_out: 227*4882a593Smuzhiyun /* Copy rest of crash memory */ 228*4882a593Smuzhiyun MEMCPY %r9,%r10,%r11 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun /* start crash kernel */ 231*4882a593Smuzhiyun START_NEXT_KERNEL .base_dst 1 232*4882a593Smuzhiyun 233*4882a593Smuzhiyun 234*4882a593Smuzhiyunload_psw_mask: 235*4882a593Smuzhiyun .long 0x00080000,0x80000000 236*4882a593Smuzhiyun 237*4882a593Smuzhiyun .align 8 238*4882a593Smuzhiyundisabled_wait_psw: 239*4882a593Smuzhiyun .quad 0x0002000180000000 240*4882a593Smuzhiyun .quad 0x0000000000000000 + .do_checksum_verification 241*4882a593Smuzhiyun 242*4882a593Smuzhiyungprregs: 243*4882a593Smuzhiyun .rept 10 244*4882a593Smuzhiyun .quad 0 245*4882a593Smuzhiyun .endr 246*4882a593Smuzhiyun 247*4882a593Smuzhiyun/* Macro to define a global variable with name and size (in bytes) to be 248*4882a593Smuzhiyun * shared with C code. 249*4882a593Smuzhiyun * 250*4882a593Smuzhiyun * Add the .size and .type attribute to satisfy checks on the Elf_Sym during 251*4882a593Smuzhiyun * purgatory load. 252*4882a593Smuzhiyun */ 253*4882a593Smuzhiyun.macro GLOBAL_VARIABLE name,size 254*4882a593Smuzhiyun\name: 255*4882a593Smuzhiyun .global \name 256*4882a593Smuzhiyun .size \name,\size 257*4882a593Smuzhiyun .type \name,object 258*4882a593Smuzhiyun .skip \size,0 259*4882a593Smuzhiyun.endm 260*4882a593Smuzhiyun 261*4882a593SmuzhiyunGLOBAL_VARIABLE purgatory_sha256_digest,32 262*4882a593SmuzhiyunGLOBAL_VARIABLE purgatory_sha_regions,16*__KEXEC_SHA_REGION_SIZE 263*4882a593SmuzhiyunGLOBAL_VARIABLE kernel_entry,8 264*4882a593SmuzhiyunGLOBAL_VARIABLE kernel_type,8 265*4882a593SmuzhiyunGLOBAL_VARIABLE crash_start,8 266*4882a593SmuzhiyunGLOBAL_VARIABLE crash_size,8 267*4882a593Smuzhiyun 268*4882a593Smuzhiyun .align PAGE_SIZE 269*4882a593Smuzhiyunstack: 270*4882a593Smuzhiyun /* The buffer to move this code must be as big as the code. */ 271*4882a593Smuzhiyun .skip stack-purgatory_start 272*4882a593Smuzhiyun .align PAGE_SIZE 273*4882a593Smuzhiyunpurgatory_end: 274