xref: /freebsd/lib/libkvm/tests/kvm_open2_test.c (revision 8a7df6c58d549bd2eaa5cad30b2ef93c3eca3c18)
1 /*-
2  * Copyright (c) 2017 Enji Cooper <ngie@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/endian.h>
28 #include <elf.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <kvm.h>
32 #include <limits.h>
33 #include <paths.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include <atf-c.h>
41 
42 #include "../../../sys/powerpc/include/minidump.h"
43 #include "kvm_test_common.h"
44 
45 #define	PPC64_DMAP_BASE		0xc000000000000000ULL
46 #define	PPC64_KERN_BASE		0xc008000000000000ULL
47 #define	RADIX_ROOT_SIZE		(1UL << 16)
48 #define	RADIX_ROOT_ENTRIES	(RADIX_ROOT_SIZE / sizeof(uint64_t))
49 #define	RADIX_VALID		0x8000000000000000ULL
50 #define	RADIX_LEAF		0x4000000000000000ULL
51 #define	RADIX_READ		0x4
52 #define	RADIX_WRITE		0x2
53 
54 static int
55 unresolved_symbol(const char *name __unused, kvaddr_t *value __unused)
56 {
57 
58 	return (-1);
59 }
60 
61 static void
62 create_powerpc64le_kernel(const char *path)
63 {
64 	Elf64_Ehdr ehdr;
65 	ssize_t n;
66 	int fd;
67 
68 	memset(&ehdr, 0, sizeof(ehdr));
69 	memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
70 	ehdr.e_ident[EI_CLASS] = ELFCLASS64;
71 	ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
72 	ehdr.e_ident[EI_VERSION] = EV_CURRENT;
73 	ehdr.e_type = htole16(ET_DYN);
74 	ehdr.e_machine = htole16(EM_PPC64);
75 	ehdr.e_version = htole32(EV_CURRENT);
76 	ehdr.e_ehsize = htole16(sizeof(ehdr));
77 
78 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
79 	ATF_REQUIRE(fd != -1);
80 	n = write(fd, &ehdr, sizeof(ehdr));
81 	ATF_REQUIRE_EQ(n, (ssize_t)sizeof(ehdr));
82 	ATF_REQUIRE(close(fd) == 0);
83 }
84 
85 static void
86 create_invalid_powerpc64_minidump(const char *path)
87 {
88 	char buf[PAGE_SIZE];
89 	ssize_t n;
90 	int fd;
91 
92 	memset(buf, 0, sizeof(buf));
93 	memcpy(buf, "minidump FreeBSD/powerpc64",
94 	    sizeof("minidump FreeBSD/powerpc64"));
95 
96 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
97 	ATF_REQUIRE(fd != -1);
98 	n = write(fd, buf, sizeof(buf));
99 	ATF_REQUIRE_EQ(n, (ssize_t)sizeof(buf));
100 	ATF_REQUIRE(close(fd) == 0);
101 }
102 
103 static void
104 write_all(int fd, const void *buf, size_t len)
105 {
106 	const char *p;
107 	ssize_t n;
108 
109 	p = buf;
110 	while (len != 0) {
111 		n = write(fd, p, len);
112 		ATF_REQUIRE(n > 0);
113 		p += n;
114 		len -= n;
115 	}
116 }
117 
118 static void
119 create_powerpc64le_radix_minidump(const char *path)
120 {
121 	struct minidumphdr hdr;
122 	uint64_t entry, *dump_avail, *root;
123 	uint8_t *page;
124 	size_t root_index;
125 	int fd, i;
126 
127 	dump_avail = calloc(1, PAGE_SIZE);
128 	root = calloc(1, RADIX_ROOT_SIZE);
129 	page = calloc(1, PAGE_SIZE);
130 	ATF_REQUIRE(dump_avail != NULL);
131 	ATF_REQUIRE(root != NULL);
132 	ATF_REQUIRE(page != NULL);
133 
134 	memset(&hdr, 0, sizeof(hdr));
135 	strlcpy(hdr.magic, MINIDUMP_MAGIC, sizeof(hdr.magic));
136 	strlcpy(hdr.mmu_name, "mmu_radix", sizeof(hdr.mmu_name));
137 	hdr.version = htole32(MINIDUMP_VERSION);
138 	hdr.bitmapsize = htole32(PAGE_SIZE);
139 	hdr.pmapsize = htole32(RADIX_ROOT_SIZE);
140 	hdr.kernbase = htole64(PPC64_KERN_BASE);
141 	hdr.kernend = htole64(PPC64_KERN_BASE + PAGE_SIZE);
142 	hdr.dmapbase = htole64(PPC64_DMAP_BASE);
143 	hdr.dmapend = htole64(PPC64_DMAP_BASE + 0x10000);
144 	hdr.startkernel = htole64(PPC64_DMAP_BASE + 0x4000);
145 	hdr.endkernel = htole64(PPC64_DMAP_BASE + 0x5000);
146 	hdr.dumpavailsize = htole32(PAGE_SIZE);
147 
148 	dump_avail[0] = htole64(0);
149 	dump_avail[1] = htole64(0x10000);
150 
151 	root_index = (PPC64_KERN_BASE >> 39) & (RADIX_ROOT_ENTRIES - 1);
152 	root[root_index] = htobe64(RADIX_VALID | 0x1000 | 9);
153 
154 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
155 	ATF_REQUIRE(fd != -1);
156 	memset(page, 0, PAGE_SIZE);
157 	memcpy(page, &hdr, sizeof(hdr));
158 	write_all(fd, page, PAGE_SIZE);
159 	write_all(fd, dump_avail, PAGE_SIZE);
160 	memset(page, 0, PAGE_SIZE);
161 	for (i = 1; i <= 4; i++)
162 		page[i / NBBY] |= 1U << (i % NBBY);
163 	write_all(fd, page, PAGE_SIZE);
164 	write_all(fd, root, RADIX_ROOT_SIZE);
165 
166 	/* L2, L3, and PTE pages, followed by the mapped data page. */
167 	for (i = 1; i <= 3; i++) {
168 		memset(page, 0, PAGE_SIZE);
169 		if (i != 3)
170 			entry = htobe64(RADIX_VALID | ((i + 1) * 0x1000) |
171 			    9);
172 		else
173 			entry = htobe64(RADIX_VALID | RADIX_LEAF |
174 			    RADIX_READ | RADIX_WRITE | 0x4000);
175 		memcpy(page, &entry, sizeof(entry));
176 		write_all(fd, page, PAGE_SIZE);
177 	}
178 	memset(page, 0, PAGE_SIZE);
179 	memcpy(page, "radix minidump", sizeof("radix minidump"));
180 	write_all(fd, page, PAGE_SIZE);
181 	ATF_REQUIRE(close(fd) == 0);
182 
183 	free(page);
184 	free(root);
185 	free(dump_avail);
186 }
187 
188 ATF_TC_WITHOUT_HEAD(kvm_open2_negative_test_nonexistent_corefile);
189 ATF_TC_BODY(kvm_open2_negative_test_nonexistent_corefile, tc)
190 {
191 
192 	errbuf_clear();
193 	ATF_CHECK(kvm_open2(NULL, "/nonexistent", O_RDONLY, NULL, NULL) == NULL);
194 	ATF_CHECK(!errbuf_has_error(errbuf));
195 	errbuf_clear();
196 	ATF_CHECK(kvm_open2(NULL, "/nonexistent", O_RDONLY,
197 	    errbuf, NULL) == NULL);
198 	ATF_CHECK(errbuf_has_error(errbuf));
199 }
200 
201 ATF_TC_WITHOUT_HEAD(kvm_open2_negative_test_nonexistent_execfile);
202 ATF_TC_BODY(kvm_open2_negative_test_nonexistent_execfile, tc)
203 {
204 
205 	errbuf_clear();
206 	ATF_CHECK(kvm_open2("/nonexistent", _PATH_DEVZERO, O_RDONLY,
207 	    NULL, NULL) == NULL);
208 	ATF_CHECK(strlen(errbuf) == 0);
209 	errbuf_clear();
210 	ATF_CHECK(kvm_open2("/nonexistent", _PATH_DEVZERO, O_RDONLY,
211 	    errbuf, NULL) == NULL);
212 	ATF_CHECK(errbuf_has_error(errbuf));
213 }
214 
215 ATF_TC(kvm_open2_negative_test_invalid_corefile);
216 ATF_TC_HEAD(kvm_open2_negative_test_invalid_corefile, tc)
217 {
218 
219 	atf_tc_set_md_var(tc, "require.user", "root");
220 }
221 
222 ATF_TC_BODY(kvm_open2_negative_test_invalid_corefile, tc)
223 {
224 	kvm_t *kd;
225 
226 	errbuf_clear();
227 	atf_utils_create_file("some-file", "this is a text file");
228 	kd = kvm_open2(NULL, "some-file", O_RDONLY, errbuf, NULL);
229 	ATF_CHECK(errbuf_has_error(errbuf));
230 	ATF_REQUIRE_MSG(kd == NULL, "kvm_open2 succeeded");
231 }
232 
233 ATF_TC(kvm_open2_negative_test_invalid_execfile);
234 ATF_TC_HEAD(kvm_open2_negative_test_invalid_execfile, tc)
235 {
236 
237 	atf_tc_set_md_var(tc, "require.user", "root");
238 }
239 
240 ATF_TC_BODY(kvm_open2_negative_test_invalid_execfile, tc)
241 {
242 	kvm_t *kd;
243 
244 	errbuf_clear();
245 	atf_utils_create_file("some-file", "this is a text file");
246 	kd = kvm_open2("some-file", "/bin/sh", O_RDONLY, errbuf, NULL);
247 	ATF_CHECK(errbuf_has_error(errbuf));
248 	ATF_REQUIRE_MSG(kd == NULL, "kvm_open2 succeeded unexpectedly");
249 }
250 
251 ATF_TC_WITHOUT_HEAD(kvm_open2_powerpc64le_minidump_probe);
252 ATF_TC_BODY(kvm_open2_powerpc64le_minidump_probe, tc)
253 {
254 	kvm_t *kd;
255 
256 	create_powerpc64le_kernel("kernel");
257 	create_invalid_powerpc64_minidump("vmcore");
258 	errbuf_clear();
259 	kd = kvm_open2("kernel", "vmcore", O_RDONLY, errbuf,
260 	    unresolved_symbol);
261 	ATF_REQUIRE_MSG(kd == NULL, "kvm_open2 succeeded unexpectedly");
262 	ATF_CHECK_MATCH("wrong minidump version", errbuf);
263 }
264 
265 ATF_TC_WITHOUT_HEAD(kvm_open2_powerpc64le_radix_minidump);
266 ATF_TC_BODY(kvm_open2_powerpc64le_radix_minidump, tc)
267 {
268 	char buf[sizeof("radix minidump")];
269 	kvm_t *kd;
270 
271 	create_powerpc64le_kernel("kernel-radix");
272 	create_powerpc64le_radix_minidump("vmcore-radix");
273 	errbuf_clear();
274 	kd = kvm_open2("kernel-radix", "vmcore-radix", O_RDONLY, errbuf,
275 	    unresolved_symbol);
276 	ATF_REQUIRE_MSG(kd != NULL, "kvm_open2 failed: %s", errbuf);
277 	ATF_REQUIRE_EQ(kvm_read2(kd, PPC64_KERN_BASE, buf, sizeof(buf)),
278 	    (ssize_t)sizeof(buf));
279 	ATF_CHECK_STREQ(buf, "radix minidump");
280 	memset(buf, 0, sizeof(buf));
281 	ATF_REQUIRE_EQ(kvm_read2(kd, PPC64_DMAP_BASE + 0x4000, buf,
282 	    sizeof(buf)), (ssize_t)sizeof(buf));
283 	ATF_CHECK_STREQ(buf, "radix minidump");
284 	ATF_REQUIRE(kvm_close(kd) == 0);
285 }
286 
287 ATF_TP_ADD_TCS(tp)
288 {
289 
290 	ATF_TP_ADD_TC(tp, kvm_open2_negative_test_invalid_corefile);
291 	ATF_TP_ADD_TC(tp, kvm_open2_negative_test_invalid_execfile);
292 	ATF_TP_ADD_TC(tp, kvm_open2_negative_test_nonexistent_corefile);
293 	ATF_TP_ADD_TC(tp, kvm_open2_negative_test_nonexistent_execfile);
294 	ATF_TP_ADD_TC(tp, kvm_open2_powerpc64le_minidump_probe);
295 	ATF_TP_ADD_TC(tp, kvm_open2_powerpc64le_radix_minidump);
296 
297 	return (atf_no_error());
298 }
299