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