1213d110cSClaudio Imbrenda /* SPDX-License-Identifier: GPL-2.0-only */ 2213d110cSClaudio Imbrenda /* 3213d110cSClaudio Imbrenda * Move Page instruction tests 4213d110cSClaudio Imbrenda * 5213d110cSClaudio Imbrenda * Copyright (c) 2021 IBM Corp 6213d110cSClaudio Imbrenda * 7213d110cSClaudio Imbrenda * Authors: 8213d110cSClaudio Imbrenda * Claudio Imbrenda <imbrenda@linux.ibm.com> 9213d110cSClaudio Imbrenda */ 10213d110cSClaudio Imbrenda #include <libcflat.h> 11213d110cSClaudio Imbrenda #include <asm/asm-offsets.h> 12213d110cSClaudio Imbrenda #include <asm-generic/barrier.h> 13213d110cSClaudio Imbrenda #include <asm/interrupt.h> 14213d110cSClaudio Imbrenda #include <asm/pgtable.h> 15213d110cSClaudio Imbrenda #include <mmu.h> 16213d110cSClaudio Imbrenda #include <asm/page.h> 17213d110cSClaudio Imbrenda #include <asm/facility.h> 18213d110cSClaudio Imbrenda #include <asm/mem.h> 19213d110cSClaudio Imbrenda #include <asm/sigp.h> 20213d110cSClaudio Imbrenda #include <smp.h> 21213d110cSClaudio Imbrenda #include <alloc_page.h> 22213d110cSClaudio Imbrenda #include <bitops.h> 23*f489dfa5SClaudio Imbrenda #include <hardware.h> 24213d110cSClaudio Imbrenda 25213d110cSClaudio Imbrenda /* Used to build the appropriate test values for register 0 */ 26213d110cSClaudio Imbrenda #define KFC(x) ((x) << 10) 27213d110cSClaudio Imbrenda #define CCO 0x100 28213d110cSClaudio Imbrenda 29213d110cSClaudio Imbrenda /* How much memory to allocate for the test */ 30213d110cSClaudio Imbrenda #define MEM_ORDER 12 31213d110cSClaudio Imbrenda /* How many iterations to perform in the loops */ 32213d110cSClaudio Imbrenda #define ITER 8 33213d110cSClaudio Imbrenda 34213d110cSClaudio Imbrenda /* Used to generate the simple pattern */ 35213d110cSClaudio Imbrenda #define MAGIC 42 36213d110cSClaudio Imbrenda 37213d110cSClaudio Imbrenda static uint8_t source[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 38213d110cSClaudio Imbrenda static uint8_t buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 39f65b8467SClaudio Imbrenda static struct lowcore * const lc; 40213d110cSClaudio Imbrenda 41213d110cSClaudio Imbrenda /* Keep track of fresh memory */ 42213d110cSClaudio Imbrenda static uint8_t *fresh; 43213d110cSClaudio Imbrenda 44213d110cSClaudio Imbrenda static inline int mvpg(unsigned long r0, void *dest, void *src) 45213d110cSClaudio Imbrenda { 46213d110cSClaudio Imbrenda register unsigned long reg0 asm ("0") = r0; 47213d110cSClaudio Imbrenda int cc; 48213d110cSClaudio Imbrenda 49213d110cSClaudio Imbrenda asm volatile(" mvpg %1,%2\n" 50213d110cSClaudio Imbrenda " ipm %0\n" 51213d110cSClaudio Imbrenda " srl %0,28" 52213d110cSClaudio Imbrenda : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0) 53213d110cSClaudio Imbrenda : "memory", "cc"); 54213d110cSClaudio Imbrenda return cc; 55213d110cSClaudio Imbrenda } 56213d110cSClaudio Imbrenda 57213d110cSClaudio Imbrenda /* 58213d110cSClaudio Imbrenda * Initialize a page with a simple pattern 59213d110cSClaudio Imbrenda */ 60213d110cSClaudio Imbrenda static void init_page(uint8_t *p) 61213d110cSClaudio Imbrenda { 62213d110cSClaudio Imbrenda int i; 63213d110cSClaudio Imbrenda 64213d110cSClaudio Imbrenda for (i = 0; i < PAGE_SIZE; i++) 65213d110cSClaudio Imbrenda p[i] = i + MAGIC; 66213d110cSClaudio Imbrenda } 67213d110cSClaudio Imbrenda 68213d110cSClaudio Imbrenda /* 69213d110cSClaudio Imbrenda * Check if the given page contains the simple pattern 70213d110cSClaudio Imbrenda */ 71213d110cSClaudio Imbrenda static int page_ok(const uint8_t *p) 72213d110cSClaudio Imbrenda { 73213d110cSClaudio Imbrenda int i; 74213d110cSClaudio Imbrenda 75213d110cSClaudio Imbrenda for (i = 0; i < PAGE_SIZE; i++) 76213d110cSClaudio Imbrenda if (p[i] != (uint8_t)(i + MAGIC)) 77213d110cSClaudio Imbrenda return 0; 78213d110cSClaudio Imbrenda return 1; 79213d110cSClaudio Imbrenda } 80213d110cSClaudio Imbrenda 81f65b8467SClaudio Imbrenda /* 82f65b8467SClaudio Imbrenda * Check that the Operand Access Identification matches with the values of 83f65b8467SClaudio Imbrenda * the r1 and r2 fields in the instruction format. The r1 and r2 fields are 84f65b8467SClaudio Imbrenda * in the last byte of the instruction, and the Program Old PSW will point 85f65b8467SClaudio Imbrenda * to the beginning of the instruction after the one that caused the fault 86f65b8467SClaudio Imbrenda * (the fixup code in the interrupt handler takes care of that for 87f65b8467SClaudio Imbrenda * nullifying instructions). Therefore it is enough to compare the byte 88f65b8467SClaudio Imbrenda * before the one contained in the Program Old PSW with the value of the 89f65b8467SClaudio Imbrenda * Operand Access Identification. 90f65b8467SClaudio Imbrenda */ 91f65b8467SClaudio Imbrenda static inline bool check_oai(void) 92f65b8467SClaudio Imbrenda { 93f65b8467SClaudio Imbrenda return *(uint8_t *)(lc->pgm_old_psw.addr - 1) == lc->op_acc_id; 94f65b8467SClaudio Imbrenda } 95f65b8467SClaudio Imbrenda 96213d110cSClaudio Imbrenda static void test_exceptions(void) 97213d110cSClaudio Imbrenda { 98213d110cSClaudio Imbrenda int i, expected; 99213d110cSClaudio Imbrenda 100213d110cSClaudio Imbrenda report_prefix_push("exceptions"); 101213d110cSClaudio Imbrenda 102213d110cSClaudio Imbrenda /* 103213d110cSClaudio Imbrenda * Key Function Control values 4 and 5 are allowed only in supervisor 104213d110cSClaudio Imbrenda * state, and even then, only if the move-page-and-set-key facility 105213d110cSClaudio Imbrenda * is present (STFLE bit 149) 106213d110cSClaudio Imbrenda */ 107213d110cSClaudio Imbrenda report_prefix_push("privileged"); 108213d110cSClaudio Imbrenda if (test_facility(149)) { 109213d110cSClaudio Imbrenda expected = PGM_INT_CODE_PRIVILEGED_OPERATION; 110213d110cSClaudio Imbrenda for (i = 4; i < 6; i++) { 111213d110cSClaudio Imbrenda expect_pgm_int(); 112213d110cSClaudio Imbrenda enter_pstate(); 113213d110cSClaudio Imbrenda mvpg(KFC(i), buffer, source); 114213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value %d", i); 115213d110cSClaudio Imbrenda } 116213d110cSClaudio Imbrenda } else { 117213d110cSClaudio Imbrenda report_skip("Key Function Control value %d", 4); 118213d110cSClaudio Imbrenda report_skip("Key Function Control value %d", 5); 119213d110cSClaudio Imbrenda i = 4; 120213d110cSClaudio Imbrenda } 121213d110cSClaudio Imbrenda report_prefix_pop(); 122213d110cSClaudio Imbrenda 123213d110cSClaudio Imbrenda /* 124213d110cSClaudio Imbrenda * Invalid values of the Key Function Control, or setting the 125213d110cSClaudio Imbrenda * reserved bits, should result in a specification exception 126213d110cSClaudio Imbrenda */ 127213d110cSClaudio Imbrenda report_prefix_push("specification"); 128213d110cSClaudio Imbrenda expected = PGM_INT_CODE_SPECIFICATION; 129213d110cSClaudio Imbrenda expect_pgm_int(); 130213d110cSClaudio Imbrenda mvpg(KFC(3), buffer, source); 131213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value 3"); 132213d110cSClaudio Imbrenda for (; i < 32; i++) { 133213d110cSClaudio Imbrenda expect_pgm_int(); 134213d110cSClaudio Imbrenda mvpg(KFC(i), buffer, source); 135213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Key Function Control value %d", i); 136213d110cSClaudio Imbrenda } 137213d110cSClaudio Imbrenda report_prefix_pop(); 138213d110cSClaudio Imbrenda 139213d110cSClaudio Imbrenda /* Operands outside memory result in addressing exceptions, as usual */ 140213d110cSClaudio Imbrenda report_prefix_push("addressing"); 141213d110cSClaudio Imbrenda expected = PGM_INT_CODE_ADDRESSING; 142213d110cSClaudio Imbrenda expect_pgm_int(); 143213d110cSClaudio Imbrenda mvpg(0, buffer, (void *)PAGE_MASK); 144213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "Second operand outside memory"); 145213d110cSClaudio Imbrenda 146213d110cSClaudio Imbrenda expect_pgm_int(); 147213d110cSClaudio Imbrenda mvpg(0, (void *)PAGE_MASK, source); 148213d110cSClaudio Imbrenda report(clear_pgm_int() == expected, "First operand outside memory"); 149213d110cSClaudio Imbrenda report_prefix_pop(); 150213d110cSClaudio Imbrenda 151213d110cSClaudio Imbrenda report_prefix_pop(); 152213d110cSClaudio Imbrenda } 153213d110cSClaudio Imbrenda 154213d110cSClaudio Imbrenda static void test_success(void) 155213d110cSClaudio Imbrenda { 156213d110cSClaudio Imbrenda int cc; 157213d110cSClaudio Imbrenda 158213d110cSClaudio Imbrenda report_prefix_push("success"); 159213d110cSClaudio Imbrenda /* Test successful scenarios, both in supervisor and problem state */ 160213d110cSClaudio Imbrenda cc = mvpg(0, buffer, source); 161213d110cSClaudio Imbrenda report(page_ok(buffer) && !cc, "Supervisor state MVPG successful"); 162213d110cSClaudio Imbrenda memset(buffer, 0xff, PAGE_SIZE); 163213d110cSClaudio Imbrenda 164213d110cSClaudio Imbrenda enter_pstate(); 165213d110cSClaudio Imbrenda cc = mvpg(0, buffer, source); 166213d110cSClaudio Imbrenda leave_pstate(); 167213d110cSClaudio Imbrenda report(page_ok(buffer) && !cc, "Problem state MVPG successful"); 168213d110cSClaudio Imbrenda 169213d110cSClaudio Imbrenda report_prefix_pop(); 170213d110cSClaudio Imbrenda } 171213d110cSClaudio Imbrenda 172213d110cSClaudio Imbrenda static void test_small_loop(const void *string) 173213d110cSClaudio Imbrenda { 174213d110cSClaudio Imbrenda uint8_t *dest; 175213d110cSClaudio Imbrenda int i, cc; 176213d110cSClaudio Imbrenda 177213d110cSClaudio Imbrenda /* Looping over cold and warm pages helps catch VSIE bugs */ 178213d110cSClaudio Imbrenda report_prefix_push(string); 179213d110cSClaudio Imbrenda dest = fresh; 180213d110cSClaudio Imbrenda for (i = 0; i < ITER; i++) { 181213d110cSClaudio Imbrenda cc = mvpg(0, fresh, source); 182213d110cSClaudio Imbrenda report(page_ok(fresh) && !cc, "cold: %p, %p", source, fresh); 183213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 184213d110cSClaudio Imbrenda } 185213d110cSClaudio Imbrenda 186213d110cSClaudio Imbrenda for (i = 0; i < ITER; i++) { 187213d110cSClaudio Imbrenda memset(dest, 0, PAGE_SIZE); 188213d110cSClaudio Imbrenda cc = mvpg(0, dest, source); 189213d110cSClaudio Imbrenda report(page_ok(dest) && !cc, "warm: %p, %p", source, dest); 190213d110cSClaudio Imbrenda dest += PAGE_SIZE; 191213d110cSClaudio Imbrenda } 192213d110cSClaudio Imbrenda report_prefix_pop(); 193213d110cSClaudio Imbrenda } 194213d110cSClaudio Imbrenda 195213d110cSClaudio Imbrenda static void test_mmu_prot(void) 196213d110cSClaudio Imbrenda { 197213d110cSClaudio Imbrenda int cc; 198213d110cSClaudio Imbrenda 199213d110cSClaudio Imbrenda report_prefix_push("protection"); 200213d110cSClaudio Imbrenda report_prefix_push("cco=0"); 201213d110cSClaudio Imbrenda 202213d110cSClaudio Imbrenda /* MVPG should still succeed when the source is read-only */ 203213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_P); 204213d110cSClaudio Imbrenda cc = mvpg(0, fresh, source); 205213d110cSClaudio Imbrenda report(page_ok(fresh) && !cc, "source read only"); 206213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_P); 207213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 208213d110cSClaudio Imbrenda 209213d110cSClaudio Imbrenda /* 210213d110cSClaudio Imbrenda * When the source or destination are invalid, a page translation 211213d110cSClaudio Imbrenda * exception should be raised; when the destination is read-only, 212213d110cSClaudio Imbrenda * a protection exception should be raised. 213213d110cSClaudio Imbrenda */ 214213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_P); 215213d110cSClaudio Imbrenda expect_pgm_int(); 216213d110cSClaudio Imbrenda mvpg(0, fresh, source); 217213d110cSClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 218213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 219213d110cSClaudio Imbrenda 220f65b8467SClaudio Imbrenda report_prefix_push("source invalid"); 221213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_I); 222f65b8467SClaudio Imbrenda lc->op_acc_id = 0; 223213d110cSClaudio Imbrenda expect_pgm_int(); 224213d110cSClaudio Imbrenda mvpg(0, fresh, source); 225f65b8467SClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception"); 226213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_I); 227f65b8467SClaudio Imbrenda report(check_oai(), "operand access ident"); 228f65b8467SClaudio Imbrenda report_prefix_pop(); 229213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 230213d110cSClaudio Imbrenda 231f65b8467SClaudio Imbrenda report_prefix_push("destination invalid"); 232213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I); 233f65b8467SClaudio Imbrenda lc->op_acc_id = 0; 234213d110cSClaudio Imbrenda expect_pgm_int(); 235213d110cSClaudio Imbrenda mvpg(0, fresh, source); 236f65b8467SClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION, "exception"); 237f65b8467SClaudio Imbrenda report(check_oai(), "operand access ident"); 238f65b8467SClaudio Imbrenda report_prefix_pop(); 239213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 240213d110cSClaudio Imbrenda 241213d110cSClaudio Imbrenda report_prefix_pop(); 242213d110cSClaudio Imbrenda report_prefix_push("cco=1"); 243213d110cSClaudio Imbrenda /* 244213d110cSClaudio Imbrenda * Setting the CCO bit should suppress page translation exceptions, 245213d110cSClaudio Imbrenda * but not protection exceptions. 246213d110cSClaudio Imbrenda */ 247213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_P); 248213d110cSClaudio Imbrenda expect_pgm_int(); 249213d110cSClaudio Imbrenda mvpg(CCO, fresh, source); 250213d110cSClaudio Imbrenda report(clear_pgm_int() == PGM_INT_CODE_PROTECTION, "destination read only"); 251213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 252213d110cSClaudio Imbrenda 253b7f0f9a2SClaudio Imbrenda /* Known issue in TCG: CCO flag is not honoured */ 254*f489dfa5SClaudio Imbrenda if (host_is_tcg()) { 255b7f0f9a2SClaudio Imbrenda report_prefix_push("TCG"); 256b7f0f9a2SClaudio Imbrenda report_skip("destination invalid"); 257b7f0f9a2SClaudio Imbrenda report_skip("source invalid"); 258b7f0f9a2SClaudio Imbrenda report_skip("source and destination invalid"); 259b7f0f9a2SClaudio Imbrenda report_prefix_pop(); 260b7f0f9a2SClaudio Imbrenda } else { 261213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I); 262213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source); 263213d110cSClaudio Imbrenda report(cc == 1, "destination invalid"); 264213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 265213d110cSClaudio Imbrenda 266213d110cSClaudio Imbrenda protect_page(source, PAGE_ENTRY_I); 267213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source); 268213d110cSClaudio Imbrenda report(cc == 2, "source invalid"); 269213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 270213d110cSClaudio Imbrenda 271213d110cSClaudio Imbrenda protect_page(fresh, PAGE_ENTRY_I); 272213d110cSClaudio Imbrenda cc = mvpg(CCO, fresh, source); 273213d110cSClaudio Imbrenda report(cc == 2, "source and destination invalid"); 274213d110cSClaudio Imbrenda fresh += PAGE_SIZE; 275b7f0f9a2SClaudio Imbrenda } 276213d110cSClaudio Imbrenda 277213d110cSClaudio Imbrenda unprotect_page(source, PAGE_ENTRY_I); 278213d110cSClaudio Imbrenda report_prefix_pop(); 279213d110cSClaudio Imbrenda report_prefix_pop(); 280213d110cSClaudio Imbrenda } 281213d110cSClaudio Imbrenda 282213d110cSClaudio Imbrenda int main(void) 283213d110cSClaudio Imbrenda { 284213d110cSClaudio Imbrenda report_prefix_push("mvpg"); 285213d110cSClaudio Imbrenda 286213d110cSClaudio Imbrenda init_page(source); 287213d110cSClaudio Imbrenda fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO | FLAG_FRESH); 288213d110cSClaudio Imbrenda assert(fresh); 289213d110cSClaudio Imbrenda 290213d110cSClaudio Imbrenda test_exceptions(); 291213d110cSClaudio Imbrenda test_success(); 292213d110cSClaudio Imbrenda test_small_loop("nommu"); 293213d110cSClaudio Imbrenda 294213d110cSClaudio Imbrenda setup_vm(); 295213d110cSClaudio Imbrenda 296213d110cSClaudio Imbrenda test_small_loop("mmu"); 297213d110cSClaudio Imbrenda test_mmu_prot(); 298213d110cSClaudio Imbrenda 299213d110cSClaudio Imbrenda report_prefix_pop(); 300213d110cSClaudio Imbrenda return report_summary(); 301213d110cSClaudio Imbrenda } 302