xref: /kvm-unit-tests/lib/s390x/sie.c (revision dfc1fec2fbde04ad607e1aed560cf7059350c70f)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Library for managing various aspects of guests
4  *
5  * Copyright (c) 2021 IBM Corp
6  *
7  * Authors:
8  *  Janosch Frank <frankja@linux.ibm.com>
9  */
10 
11 #include <asm/barrier.h>
12 #include <bitops.h>
13 #include <libcflat.h>
14 #include <sie.h>
15 #include <asm/page.h>
16 #include <asm/interrupt.h>
17 #include <libcflat.h>
18 #include <alloc_page.h>
19 #include <vmalloc.h>
20 #include <sclp.h>
21 
22 void sie_expect_validity(struct vm *vm)
23 {
24 	vm->validity_expected = true;
25 }
26 
27 uint16_t sie_get_validity(struct vm *vm)
28 {
29 	/*
30 	 * 0xffff will never be returned by SIE, so we can indicate a
31 	 * missing validity via this value.
32 	 */
33 	if (vm->sblk->icptcode != ICPT_VALIDITY)
34 		return 0xffff;
35 
36 	return vm->sblk->ipb >> 16;
37 }
38 
39 void sie_check_validity(struct vm *vm, uint16_t vir_exp)
40 {
41 	uint16_t vir = sie_get_validity(vm);
42 
43 	report(vir_exp == vir, "VALIDITY: %x", vir);
44 }
45 
46 void sie_handle_validity(struct vm *vm)
47 {
48 	if (vm->sblk->icptcode != ICPT_VALIDITY)
49 		return;
50 
51 	if (!vm->validity_expected)
52 		report_abort("VALIDITY: %x", sie_get_validity(vm));
53 	vm->validity_expected = false;
54 }
55 
56 void sie(struct vm *vm)
57 {
58 	uint64_t old_cr13;
59 
60 	/* When a pgm int code is set, we'll never enter SIE below. */
61 	assert(!read_pgm_int_code());
62 
63 	if (vm->sblk->sdf == 2)
64 		memcpy(vm->sblk->pv_grregs, vm->save_area.guest.grs,
65 		       sizeof(vm->save_area.guest.grs));
66 
67 	/* Reset icptcode so we don't trip over it below */
68 	vm->sblk->icptcode = 0;
69 
70 	/*
71 	 * Set up home address space to match primary space. Instead of running
72 	 * in home space all the time, we switch every time in sie() because:
73 	 * - tests that depend on running in primary space mode don't need to be
74 	 *   touched
75 	 * - it avoids regressions in tests
76 	 * - switching every time makes it easier to extend this in the future,
77 	 *   for example to allow tests to run in whatever space they want
78 	 */
79 	old_cr13 = stctg(13);
80 	lctlg(13, stctg(1));
81 
82 	/* switch to home space so guest tables can be different from host */
83 	psw_mask_set_bits(PSW_MASK_HOME);
84 
85 	/* also handle all interruptions in home space while in SIE */
86 	irq_set_dat_mode(true, AS_HOME);
87 
88 	/* leave SIE when we have an intercept or an interrupt so the test can react to it */
89 	while (vm->sblk->icptcode == 0 && !read_pgm_int_code()) {
90 		sie64a(vm->sblk, &vm->save_area);
91 		sie_handle_validity(vm);
92 	}
93 	vm->save_area.guest.grs[14] = vm->sblk->gg14;
94 	vm->save_area.guest.grs[15] = vm->sblk->gg15;
95 
96 	irq_set_dat_mode(true, AS_PRIM);
97 	psw_mask_clear_bits(PSW_MASK_HOME);
98 
99 	/* restore the old CR 13 */
100 	lctlg(13, old_cr13);
101 
102 	if (vm->sblk->sdf == 2)
103 		memcpy(vm->save_area.guest.grs, vm->sblk->pv_grregs,
104 		       sizeof(vm->save_area.guest.grs));
105 }
106 
107 void sie_guest_sca_create(struct vm *vm)
108 {
109 	vm->sca = (struct esca_block *)alloc_page();
110 
111 	/* Let's start out with one page of ESCA for now */
112 	vm->sblk->scaoh = ((uint64_t)vm->sca >> 32);
113 	vm->sblk->scaol = (uint64_t)vm->sca & ~0x3fU;
114 	vm->sblk->ecb2 |= ECB2_ESCA;
115 
116 	/* Enable SIGP sense running interpretation */
117 	vm->sblk->ecb |= ECB_SRSI;
118 
119 	/* We assume that cpu 0 is always part of the vm */
120 	vm->sca->mcn[0] = BIT(63);
121 	vm->sca->cpu[0].sda = (uint64_t)vm->sblk;
122 }
123 
124 /* Initializes the struct vm members like the SIE control block. */
125 void sie_guest_create(struct vm *vm, uint64_t guest_mem, uint64_t guest_mem_len)
126 {
127 	vm->sblk = alloc_page();
128 	memset(vm->sblk, 0, PAGE_SIZE);
129 	vm->sblk->cpuflags = CPUSTAT_ZARCH | CPUSTAT_RUNNING;
130 	vm->sblk->ihcpu = 0xffff;
131 	vm->sblk->prefix = 0;
132 
133 	/* Guest memory chunks are always 1MB */
134 	assert(!(guest_mem_len & ~HPAGE_MASK));
135 	vm->guest_mem = (uint8_t *)guest_mem;
136 	/* For non-PV guests we re-use the host's ASCE for ease of use */
137 	vm->save_area.guest.asce = stctg(1);
138 	/* Currently MSO/MSL is the easiest option */
139 	vm->sblk->mso = (uint64_t)guest_mem;
140 	vm->sblk->msl = (uint64_t)guest_mem + ((guest_mem_len - 1) & HPAGE_MASK);
141 
142 	/* CRYCB needs to be in the first 2GB */
143 	vm->crycb = alloc_pages_flags(0, AREA_DMA31);
144 	vm->sblk->crycbd = (uint32_t)(uintptr_t)vm->crycb;
145 }
146 
147 /**
148  * sie_guest_alloc() - Allocate memory for a guest and map it in virtual address
149  * space such that it is properly aligned.
150  * @guest_size: the desired size of the guest in bytes.
151  */
152 uint8_t *sie_guest_alloc(uint64_t guest_size)
153 {
154 	static unsigned long guest_counter = 1;
155 	u8 *guest_phys, *guest_virt;
156 	unsigned long i;
157 	pgd_t *root;
158 
159 	setup_vm();
160 	root = (pgd_t *)(stctg(1) & PAGE_MASK);
161 
162 	/*
163 	 * Start of guest memory in host virtual space needs to be aligned to
164 	 * 2GB for some environments. It also can't be at 2GB since the memory
165 	 * allocator stores its page_states metadata there.
166 	 * Thus we use the next multiple of 4GB after the end of physical
167 	 * mapping. This also leaves space after end of physical memory so the
168 	 * page immediately after physical memory is guaranteed not to be
169 	 * present.
170 	 */
171 	guest_virt = (uint8_t *)ALIGN(get_ram_size() + guest_counter * 4UL * SZ_1G, SZ_2G);
172 	guest_counter++;
173 
174 	guest_phys = alloc_pages(get_order(guest_size) - 12);
175 	/*
176 	 * Establish a new mapping of the guest memory so it can be 2GB aligned
177 	 * without actually requiring 2GB physical memory.
178 	 */
179 	for (i = 0; i < guest_size; i += PAGE_SIZE) {
180 		install_page(root, __pa(guest_phys + i), guest_virt + i);
181 	}
182 	memset(guest_virt, 0, guest_size);
183 
184 	return guest_virt;
185 }
186 
187 /* Frees the memory that was gathered on initialization */
188 void sie_guest_destroy(struct vm *vm)
189 {
190 	free_page(vm->crycb);
191 	free_page(vm->sblk);
192 	if (vm->sblk->ecb2 & ECB2_ESCA)
193 		free_page(vm->sca);
194 }
195