| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* | |
| * Tests SIE with paging. | |
| * | |
| * Copyright 2023 IBM Corp. | |
| * | |
| * Authors: | |
| * Nico Boehr <nrb@linux.ibm.com> | |
| */ | |
| #include <libcflat.h> | |
| #include <vmalloc.h> | |
| #include <asm/pgtable.h> | |
| #include <mmu.h> | |
| #include <asm/page.h> | |
| #include <asm/interrupt.h> | |
| #include <alloc_page.h> | |
| #include <sclp.h> | |
| #include <sie.h> | |
| #include <snippet.h> | |
| #include <snippet-exit.h> | |
| #include "snippets/c/sie-dat.h" | |
| static struct vm vm; | |
| static pgd_t *guest_root; | |
| static void test_sie_dat(void) | |
| { | |
| uint64_t test_page_gpa, test_page_hpa; | |
| uint8_t *test_page_hva, expected_val; | |
| bool contents_match; | |
| /* guest will tell us the guest physical address of the test buffer */ | |
| sie(&vm); | |
| assert(snippet_is_force_exit_value(&vm)); | |
| test_page_gpa = snippet_get_force_exit_value(&vm); | |
| test_page_hpa = virt_to_pte_phys(guest_root, (void*)test_page_gpa); | |
| test_page_hva = __va(test_page_hpa); | |
| report_info("test buffer gpa=0x%lx hva=%p", test_page_gpa, test_page_hva); | |
| /* guest will now write to the test buffer and we verify the contents */ | |
| sie(&vm); | |
| assert(snippet_is_force_exit(&vm)); | |
| contents_match = true; | |
| for (unsigned int i = 0; i < GUEST_TEST_PAGE_COUNT; i++) { | |
| expected_val = 42 + i; | |
| if (test_page_hva[i * PAGE_SIZE] != expected_val) { | |
| report_fail("page %u mismatch actual_val=%x expected_val=%x", | |
| i, test_page_hva[i], expected_val); | |
| contents_match = false; | |
| } | |
| } | |
| report(contents_match, "test buffer contents match"); | |
| /* the guest will now write to an unmapped address and we check that this causes a segment translation exception */ | |
| report_prefix_push("guest write to unmapped"); | |
| expect_pgm_int(); | |
| sie(&vm); | |
| check_pgm_int_code(PGM_INT_CODE_SEGMENT_TRANSLATION); | |
| report((lowcore.trans_exc_id & PAGE_MASK) == (GUEST_TOTAL_PAGE_COUNT * PAGE_SIZE), "TEID address match"); | |
| report_prefix_pop(); | |
| } | |
| static void setup_guest(void) | |
| { | |
| extern const char SNIPPET_NAME_START(c, sie_dat)[]; | |
| extern const char SNIPPET_NAME_END(c, sie_dat)[]; | |
| uint64_t guest_max_addr; | |
| pgd_t *root; | |
| setup_vm(); | |
| root = (pgd_t *)(stctg(1) & PAGE_MASK); | |
| snippet_setup_guest(&vm, false); | |
| /* allocate a region-1 table */ | |
| guest_root = pgd_alloc_one(); | |
| /* map guest memory 1:1 */ | |
| guest_max_addr = GUEST_TOTAL_PAGE_COUNT * PAGE_SIZE; | |
| for (uint64_t i = 0; i < guest_max_addr; i += PAGE_SIZE) | |
| install_page(guest_root, virt_to_pte_phys(root, vm.guest_mem + i), (void *)i); | |
| /* set up storage limit supression - leave mso and msl intact they are ignored anyways */ | |
| vm.sblk->cpuflags |= CPUSTAT_SM; | |
| /* set up the guest asce */ | |
| vm.save_area.guest.asce = __pa(guest_root) | ASCE_DT_REGION1 | REGION_TABLE_LENGTH; | |
| snippet_init(&vm, SNIPPET_NAME_START(c, sie_dat), | |
| SNIPPET_LEN(c, sie_dat), SNIPPET_UNPACK_OFF); | |
| } | |
| int main(void) | |
| { | |
| report_prefix_push("sie-dat"); | |
| if (!sclp_facilities.has_sief2) { | |
| report_skip("SIEF2 facility unavailable"); | |
| goto done; | |
| } | |
| setup_guest(); | |
| test_sie_dat(); | |
| sie_guest_destroy(&vm); | |
| done: | |
| report_prefix_pop(); | |
| return report_summary(); | |
| } |