h_mem_assist.c revision 1.10
1/*	$NetBSD: h_mem_assist.c,v 1.10 2019/05/11 07:31:57 maxv Exp $	*/
2
3/*
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Maxime Villard.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <stdint.h>
35#include <stdbool.h>
36#include <unistd.h>
37#include <string.h>
38#include <err.h>
39#include <errno.h>
40#include <sys/types.h>
41#include <sys/mman.h>
42#include <machine/segments.h>
43#include <machine/psl.h>
44#include <machine/pte.h>
45#include <x86/specialreg.h>
46
47#include <nvmm.h>
48
49#define PAGE_SIZE 4096
50
51static uint8_t mmiobuf[PAGE_SIZE];
52static uint8_t *instbuf;
53
54static void
55init_seg(struct nvmm_x64_state_seg *seg, int type, int sel)
56{
57	seg->selector = sel;
58	seg->attrib.type = type;
59	seg->attrib.s = (type & 0b10000) != 0;
60	seg->attrib.dpl = 0;
61	seg->attrib.p = 1;
62	seg->attrib.avl = 1;
63	seg->attrib.l = 1;
64	seg->attrib.def = 0;
65	seg->attrib.g = 1;
66	seg->limit = 0x0000FFFF;
67	seg->base = 0x00000000;
68}
69
70static void
71reset_machine(struct nvmm_machine *mach)
72{
73	struct nvmm_x64_state state;
74
75	memset(&state, 0, sizeof(state));
76
77	/* Default. */
78	state.gprs[NVMM_X64_GPR_RFLAGS] = PSL_MBO;
79	init_seg(&state.segs[NVMM_X64_SEG_CS], SDT_MEMERA, GSEL(GCODE_SEL, SEL_KPL));
80	init_seg(&state.segs[NVMM_X64_SEG_SS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL));
81	init_seg(&state.segs[NVMM_X64_SEG_DS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL));
82	init_seg(&state.segs[NVMM_X64_SEG_ES], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL));
83	init_seg(&state.segs[NVMM_X64_SEG_FS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL));
84	init_seg(&state.segs[NVMM_X64_SEG_GS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL));
85
86	/* Blank. */
87	init_seg(&state.segs[NVMM_X64_SEG_GDT], 0, 0);
88	init_seg(&state.segs[NVMM_X64_SEG_IDT], 0, 0);
89	init_seg(&state.segs[NVMM_X64_SEG_LDT], SDT_SYSLDT, 0);
90	init_seg(&state.segs[NVMM_X64_SEG_TR], SDT_SYS386BSY, 0);
91
92	/* Protected mode enabled. */
93	state.crs[NVMM_X64_CR_CR0] = CR0_PG|CR0_PE|CR0_NE|CR0_TS|CR0_MP|CR0_WP|CR0_AM;
94
95	/* 64bit mode enabled. */
96	state.crs[NVMM_X64_CR_CR4] = CR4_PAE;
97	state.msrs[NVMM_X64_MSR_EFER] = EFER_LME | EFER_SCE | EFER_LMA;
98
99	/* Stolen from x86/pmap.c */
100#define	PATENTRY(n, type)	(type << ((n) * 8))
101#define	PAT_UC		0x0ULL
102#define	PAT_WC		0x1ULL
103#define	PAT_WT		0x4ULL
104#define	PAT_WP		0x5ULL
105#define	PAT_WB		0x6ULL
106#define	PAT_UCMINUS	0x7ULL
107	state.msrs[NVMM_X64_MSR_PAT] =
108	    PATENTRY(0, PAT_WB) | PATENTRY(1, PAT_WT) |
109	    PATENTRY(2, PAT_UCMINUS) | PATENTRY(3, PAT_UC) |
110	    PATENTRY(4, PAT_WB) | PATENTRY(5, PAT_WT) |
111	    PATENTRY(6, PAT_UCMINUS) | PATENTRY(7, PAT_UC);
112
113	/* Page tables. */
114	state.crs[NVMM_X64_CR_CR3] = 0x3000;
115
116	state.gprs[NVMM_X64_GPR_RIP] = 0x2000;
117
118	if (nvmm_vcpu_setstate(mach, 0, &state, NVMM_X64_STATE_ALL) == -1)
119		err(errno, "nvmm_vcpu_setstate");
120}
121
122static void
123map_pages(struct nvmm_machine *mach)
124{
125	pt_entry_t *L4, *L3, *L2, *L1;
126	int ret;
127
128	instbuf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
129	    -1, 0);
130	if (instbuf == MAP_FAILED)
131		err(errno, "mmap");
132
133	if (nvmm_hva_map(mach, (uintptr_t)instbuf, PAGE_SIZE) == -1)
134		err(errno, "nvmm_hva_map");
135	ret = nvmm_gpa_map(mach, (uintptr_t)instbuf, 0x2000, PAGE_SIZE,
136	    PROT_READ|PROT_EXEC);
137	if (ret == -1)
138		err(errno, "nvmm_gpa_map");
139
140	L4 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
141	    -1, 0);
142	if (L4 == MAP_FAILED)
143		err(errno, "mmap");
144	L3 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
145	    -1, 0);
146	if (L3 == MAP_FAILED)
147		err(errno, "mmap");
148	L2 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
149	    -1, 0);
150	if (L2 == MAP_FAILED)
151		err(errno, "mmap");
152	L1 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
153	    -1, 0);
154	if (L1 == MAP_FAILED)
155		err(errno, "mmap");
156
157	if (nvmm_hva_map(mach, (uintptr_t)L4, PAGE_SIZE) == -1)
158		err(errno, "nvmm_hva_map");
159	if (nvmm_hva_map(mach, (uintptr_t)L3, PAGE_SIZE) == -1)
160		err(errno, "nvmm_hva_map");
161	if (nvmm_hva_map(mach, (uintptr_t)L2, PAGE_SIZE) == -1)
162		err(errno, "nvmm_hva_map");
163	if (nvmm_hva_map(mach, (uintptr_t)L1, PAGE_SIZE) == -1)
164		err(errno, "nvmm_hva_map");
165
166	ret = nvmm_gpa_map(mach, (uintptr_t)L4, 0x3000, PAGE_SIZE,
167	    PROT_READ|PROT_WRITE);
168	if (ret == -1)
169		err(errno, "nvmm_gpa_map");
170	ret = nvmm_gpa_map(mach, (uintptr_t)L3, 0x4000, PAGE_SIZE,
171	    PROT_READ|PROT_WRITE);
172	if (ret == -1)
173		err(errno, "nvmm_gpa_map");
174	ret = nvmm_gpa_map(mach, (uintptr_t)L2, 0x5000, PAGE_SIZE,
175	    PROT_READ|PROT_WRITE);
176	if (ret == -1)
177		err(errno, "nvmm_gpa_map");
178	ret = nvmm_gpa_map(mach, (uintptr_t)L1, 0x6000, PAGE_SIZE,
179	    PROT_READ|PROT_WRITE);
180	if (ret == -1)
181		err(errno, "nvmm_gpa_map");
182
183	memset(L4, 0, PAGE_SIZE);
184	memset(L3, 0, PAGE_SIZE);
185	memset(L2, 0, PAGE_SIZE);
186	memset(L1, 0, PAGE_SIZE);
187
188	L4[0] = PTE_P | PTE_W | 0x4000;
189	L3[0] = PTE_P | PTE_W | 0x5000;
190	L2[0] = PTE_P | PTE_W | 0x6000;
191	L1[0x2000 / PAGE_SIZE] = PTE_P | PTE_W | 0x2000;
192	L1[0x1000 / PAGE_SIZE] = PTE_P | PTE_W | 0x1000;
193}
194
195/* -------------------------------------------------------------------------- */
196
197static void
198mem_callback(struct nvmm_mem *mem)
199{
200	size_t off;
201
202	if (mem->gpa < 0x1000 || mem->gpa + mem->size > 0x1000 + PAGE_SIZE) {
203		printf("Out of page\n");
204		exit(-1);
205	}
206
207	off = mem->gpa - 0x1000;
208
209	printf("-> gpa = %p\n", (void *)mem->gpa);
210
211	if (mem->write) {
212		memcpy(mmiobuf + off, mem->data, mem->size);
213	} else {
214		memcpy(mem->data, mmiobuf + off, mem->size);
215	}
216}
217
218static int
219handle_memory(struct nvmm_machine *mach, struct nvmm_exit *exit)
220{
221	int ret;
222
223	ret = nvmm_assist_mem(mach, 0, exit);
224	if (ret == -1) {
225		err(errno, "nvmm_assist_mem");
226	}
227
228	return 0;
229}
230
231static void
232run_machine(struct nvmm_machine *mach)
233{
234	struct nvmm_exit exit;
235
236	while (1) {
237		if (nvmm_vcpu_run(mach, 0, &exit) == -1)
238			err(errno, "nvmm_vcpu_run");
239
240		switch (exit.reason) {
241		case NVMM_EXIT_NONE:
242			break;
243
244		case NVMM_EXIT_MSR:
245			/* Stop here. */
246			return;
247
248		case NVMM_EXIT_MEMORY:
249			handle_memory(mach, &exit);
250			break;
251
252		case NVMM_EXIT_SHUTDOWN:
253			printf("Shutting down!\n");
254			return;
255
256		default:
257			printf("Invalid!\n");
258			return;
259		}
260	}
261}
262
263/* -------------------------------------------------------------------------- */
264
265struct test {
266	const char *name;
267	uint8_t *code_begin;
268	uint8_t *code_end;
269	uint64_t wanted;
270};
271
272static void
273run_test(struct nvmm_machine *mach, const struct test *test)
274{
275	uint64_t *res;
276	size_t size;
277
278	size = (size_t)test->code_end - (size_t)test->code_begin;
279
280	reset_machine(mach);
281
282	memset(mmiobuf, 0, PAGE_SIZE);
283	memcpy(instbuf, test->code_begin, size);
284
285	run_machine(mach);
286
287	res = (uint64_t *)mmiobuf;
288	if (*res == test->wanted) {
289		printf("Test '%s' passed\n", test->name);
290	} else {
291		printf("Test '%s' failed, wanted 0x%lx, got 0x%lx\n", test->name,
292		    test->wanted, *res);
293	}
294}
295
296/* -------------------------------------------------------------------------- */
297
298extern uint8_t test1_begin, test1_end;
299extern uint8_t test2_begin, test2_end;
300extern uint8_t test3_begin, test3_end;
301extern uint8_t test4_begin, test4_end;
302extern uint8_t test5_begin, test5_end;
303extern uint8_t test6_begin, test6_end;
304extern uint8_t test7_begin, test7_end;
305extern uint8_t test8_begin, test8_end;
306extern uint8_t test9_begin, test9_end;
307extern uint8_t test10_begin, test10_end;
308extern uint8_t test11_begin, test11_end;
309extern uint8_t test12_begin, test12_end;
310extern uint8_t test13_begin, test13_end;
311extern uint8_t test14_begin, test14_end;
312
313static const struct test tests[] = {
314	{ "test1 - MOV", &test1_begin, &test1_end, 0x3004 },
315	{ "test2 - OR",  &test2_begin, &test2_end, 0x16FF },
316	{ "test3 - AND", &test3_begin, &test3_end, 0x1FC0 },
317	{ "test4 - XOR", &test4_begin, &test4_end, 0x10CF },
318	{ "test5 - Address Sizes", &test5_begin, &test5_end, 0x1F00 },
319	{ "test6 - DMO", &test6_begin, &test6_end, 0xFFAB },
320	{ "test7 - STOS", &test7_begin, &test7_end, 0x00123456 },
321	{ "test8 - LODS", &test8_begin, &test8_end, 0x12345678 },
322	{ "test9 - MOVS", &test9_begin, &test9_end, 0x12345678 },
323	{ "test10 - MOVZXB", &test10_begin, &test10_end, 0x00000078 },
324	{ "test11 - MOVZXW", &test11_begin, &test11_end, 0x00005678 },
325	{ "test12 - CMP", &test12_begin, &test12_end, 0x00000001 },
326	{ "test13 - SUB", &test13_begin, &test13_end, 0x0000000F0000A0FF },
327	{ "test14 - TEST", &test14_begin, &test14_end, 0x00000001 },
328	{ NULL, NULL, NULL, -1 }
329};
330
331static struct nvmm_callbacks callbacks = {
332	.io = NULL,
333	.mem = mem_callback
334};
335
336/*
337 * 0x1000: MMIO address, unmapped
338 * 0x2000: Instructions, mapped
339 * 0x3000: L4
340 * 0x4000: L3
341 * 0x5000: L2
342 * 0x6000: L1
343 */
344int main(int argc, char *argv[])
345{
346	struct nvmm_machine mach;
347	size_t i;
348
349	if (nvmm_machine_create(&mach) == -1)
350		err(errno, "nvmm_machine_create");
351	if (nvmm_vcpu_create(&mach, 0) == -1)
352		err(errno, "nvmm_vcpu_create");
353	nvmm_machine_configure(&mach, NVMM_MACH_CONF_CALLBACKS, &callbacks);
354	map_pages(&mach);
355
356	for (i = 0; tests[i].name != NULL; i++) {
357		run_test(&mach, &tests[i]);
358	}
359
360	return 0;
361}
362