1 /* $NetBSD: kvm_m68k_gen.c,v 1.2 2025/12/24 20:37:04 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 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 <sys/cdefs.h> 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: kvm_m68k_gen.c,v 1.2 2025/12/24 20:37:04 andvar Exp $"); 35 #endif /* LIBC_SCCS and not lint */ 36 37 /* 38 * Generic m68k machine dependent routines for kvm. 39 * 40 * Note: This file has to build on ALL m68k machines, 41 * so do NOT include any <machine / *.h> files here. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/types.h> 46 #include <sys/kcore.h> 47 48 #include <unistd.h> 49 #include <limits.h> 50 #include <kvm.h> 51 #include <stdlib.h> 52 53 #include <m68k/cpu.h> 54 #include <m68k/kcore.h> 55 #include <m68k/mmu_51.h> 56 #include <m68k/mmu_30.h> 57 #include <m68k/mmu_40.h> 58 59 #include "kvm_private.h" 60 #include "kvm_m68k.h" 61 62 static int _kvm_gen68k_initvtop(kvm_t *); 63 static void _kvm_gen68k_freevtop(kvm_t *); 64 static int _kvm_gen68k_kvatop(kvm_t *, vaddr_t, paddr_t *); 65 static off_t _kvm_gen68k_pa2off(kvm_t *, u_long); 66 67 struct kvm_ops _kvm_ops_gen68k = { 68 _kvm_gen68k_initvtop, 69 _kvm_gen68k_freevtop, 70 _kvm_gen68k_kvatop, 71 _kvm_gen68k_pa2off 72 }; 73 74 /* 75 * For the table indices, shift first, then mask. The ti_pa_mask 76 * is computed from the number of entries the table indexes, which 77 * in turn defines how precise the address field in each table 78 * entry. 79 */ 80 struct table_index { 81 uint32_t ti_shift; 82 uint32_t ti_mask; 83 uint32_t ti_pa_mask; 84 }; 85 86 #define TABLE_INDEX_COUNT 4 87 88 struct kvm_gen68k_context { 89 uint32_t pg_frame; /* page frame computed from mask */ 90 uint32_t va_mask; /* VA significant bits from IS */ 91 92 struct table_index ti[TABLE_INDEX_COUNT]; 93 }; 94 95 static inline uint32_t 96 table_entry_paddr(uint32_t table_pa, unsigned int idx) 97 { 98 return table_pa + (idx * sizeof(uint32_t)); 99 } 100 101 static int 102 read_table_entry(kvm_t *kd, int level, paddr_t table_pa, unsigned int idx, 103 uint32_t *entry) 104 { 105 uint32_t entry_pa = table_entry_paddr(table_pa, idx); 106 107 if (_kvm_pread(kd, kd->pmfd, entry, sizeof(*entry), 108 _kvm_gen68k_pa2off(kd, entry_pa)) != sizeof(*entry)) { 109 _kvm_err(kd, 0, 110 "failed to read level-%c entry @ pa=0x%08x", 111 'A' + level, entry_pa); 112 return -1; 113 } 114 return 0; 115 } 116 117 static inline unsigned int 118 table_index(const struct kvm_gen68k_context * const ctx, int level, 119 uint32_t va) 120 { 121 return (va >> ctx->ti[level].ti_shift) & ctx->ti[level].ti_mask; 122 } 123 124 static inline uint32_t 125 table_entry_extract_pa(const struct kvm_gen68k_context * const ctx, int level, 126 uint32_t entry) 127 { 128 return entry & ctx->ti[level].ti_pa_mask; 129 } 130 131 static int 132 mmu_tree_walk(kvm_t *kd, uint32_t va, uint32_t *pap) 133 { 134 const cpu_kcore_hdr_t * const h = kd->cpu_data; 135 const struct gen68k_kcore_hdr * const gen = &h->un._gen68k; 136 const struct vmstate * const vmst = kd->vmst; 137 const struct kvm_gen68k_context * const ctx = vmst->private; 138 uint32_t entry, type, table_pa; 139 uint32_t adj_va = va & ctx->va_mask; 140 unsigned int idx; 141 int level; 142 143 /* We only handle SHORT descriptors. */ 144 if (__SHIFTOUT(gen->srp[0], DT51_TYPE) != DT51_SHORT) { 145 _kvm_err(kd, 0, 146 "SRP does not point to a SHORT descriptor"); 147 return -1; 148 } 149 150 table_pa = gen->srp[1]; 151 for (level = 0; level < TABLE_INDEX_COUNT; level++) { 152 idx = table_index(ctx, level, adj_va); 153 if (read_table_entry(kd, level, table_pa, idx, &entry) < 0) { 154 return -1; 155 } 156 type = __SHIFTOUT(entry, DT51_TYPE); 157 switch (type) { 158 case DT51_INVALID: 159 _kvm_err(kd, 0, 160 "INVALID level-%c entry @ table_pa=0x%08x idx=%d", 161 'A' + level, table_pa, idx); 162 return -1; 163 164 case DT51_PAGE: 165 *pap = entry & ctx->pg_frame; 166 return 0; 167 168 case DT51_SHORT: 169 table_pa = table_entry_extract_pa(ctx, level, entry); 170 break; 171 172 case DT51_LONG: 173 default: 174 /* 175 * Our kernel doesn't use DT51_LONG, but if it 176 * did, we could read the value at entry_pa+4 177 * to get the 32-bit Table Physical Address field. 178 */ 179 _kvm_err(kd, 0, 180 "LONG level-%c entry @ table_pa=0x%08x idx=%d", 181 'A' + level, table_pa, idx); 182 return -1; 183 } 184 } 185 _kvm_err(kd, 0, 186 "exhausted %d levels for kva=0x%08x\n", 187 TABLE_INDEX_COUNT, va); 188 return -1; 189 } 190 191 static int 192 _kvm_gen68k_initvtop(kvm_t *kd) 193 { 194 struct vmstate *vmst = kd->vmst; 195 196 struct kvm_gen68k_context *ctx = calloc(1, sizeof(*ctx)); 197 if (ctx == NULL) { 198 goto bad; 199 } 200 201 cpu_kcore_hdr_t *h = kd->cpu_data; 202 struct gen68k_kcore_hdr *gen = &h->un._gen68k; 203 204 vmst->pgshift = __SHIFTOUT(gen->tcr, TCR51_PS); 205 if (vmst->pgshift < 8 || vmst->pgshift > 15) { 206 /* Invalid PS field in TCR. */ 207 goto bad; 208 } 209 vmst->pgofset = (1U << vmst->pgshift) - 1; 210 ctx->pg_frame = ~vmst->pgofset; 211 212 unsigned int ti[TABLE_INDEX_COUNT]; 213 unsigned int is = __SHIFTOUT(gen->tcr, TCR51_IS); 214 ti[0] = __SHIFTOUT(gen->tcr, TCR51_TIA); 215 ti[1] = __SHIFTOUT(gen->tcr, TCR51_TIB); 216 ti[2] = __SHIFTOUT(gen->tcr, TCR51_TIC); 217 ti[3] = __SHIFTOUT(gen->tcr, TCR51_TID); 218 219 /* All of the shifts should add up to 32. */ 220 if (is + ti[0] + ti[1] + ti[2] + ti[3] + vmst->pgshift != 32) { 221 /* This must add up to 32. */ 222 goto bad; 223 } 224 if (is > 15 || ti[0] > 15 || ti[1] > 15 || ti[2] > 15 || ti[3] > 15) { 225 /* None of these values may exceed 15. */ 226 goto bad; 227 } 228 229 /* 230 * The MMU works by successively shifting the address to the 231 * left and using a mux to take the next field from bits 31-N 232 * as indicated by the TCR. The initial shift is intended to 233 * lop off the upper N bits of the address, presumably so that 234 * software can use them for some other purpose (aaah, how fondly 235 * we remember the 24-bit addresses of the 68000/68010). We sort 236 * of work successively the other direction because it's more 237 * convenient for software to do it that way, but we do need to 238 * pre-compute some shifts and masks first. 239 */ 240 241 ctx->va_mask = 0xffffffffU; 242 int curshift = 32; 243 unsigned int count; 244 if (is) { 245 ctx->va_mask = (ctx->va_mask << is) >> is; 246 curshift -= is; 247 } 248 for (int i = 0; i < TABLE_INDEX_COUNT; i++) { 249 /* MMU stops processing once it gets to a TIx == 0. */ 250 if (ti[i] == 0) { 251 break; 252 } 253 curshift -= ti[i]; 254 count = 1U << ti[i]; 255 ctx->ti[i].ti_shift = curshift; 256 ctx->ti[i].ti_mask = (count - 1); 257 /* 258 * XXX Doesn't deal with entries that point to long 259 * XXX descriptors. 260 */ 261 ctx->ti[i].ti_pa_mask = 0xffffffffU & 262 ~((count * sizeof(uint32_t)) - 1); 263 } 264 265 /* At this point, what remains should be pgshift. */ 266 if (curshift != vmst->pgshift) { 267 goto bad; 268 } 269 270 vmst->private = ctx; 271 return 0; 272 273 bad: 274 if (ctx != NULL) { 275 free(ctx); 276 } 277 return -1; 278 } 279 280 static void 281 _kvm_gen68k_freevtop(kvm_t *kd) 282 { 283 struct kvm_gen68k_context *ctx = kd->vmst->private; 284 kd->vmst->private = NULL; 285 286 free(ctx); 287 } 288 289 static int 290 _kvm_gen68k_kvatop(kvm_t *kd, vaddr_t va, paddr_t *pa) 291 { 292 cpu_kcore_hdr_t *h = kd->cpu_data; 293 const struct gen68k_kcore_hdr * const gen = &h->un._gen68k; 294 const struct vmstate * const vmst = kd->vmst; 295 const struct kvm_gen68k_context * const ctx = vmst->private; 296 uint32_t trans_pa; 297 298 if (ISALIVE(kd)) { 299 _kvm_err(kd, 0, "vatop called in live kernel!"); 300 return 0; 301 } 302 303 vaddr_t offset = va & vmst->pgofset; 304 va &= ctx->pg_frame; 305 306 /* 307 * We may be called before address translation is initialized. 308 * This is typically used to find the dump magic number. This 309 * means we do not yet have the kernel page tables available, 310 * so we must to a simple relocation. 311 */ 312 if (va >= h->kernbase && va < gen->relocend) { 313 trans_pa = (va - h->kernbase) + gen->reloc; 314 goto out; 315 } 316 317 if (mmu_tree_walk(kd, va, &trans_pa) < 0) { 318 /* _kvm_err() already set. */ 319 return 0; 320 } 321 out: 322 *pa = trans_pa + offset; 323 return h->page_size - offset; 324 } 325 326 /* 327 * Translate a physical address to a file-offset in the crash dump. 328 */ 329 static off_t 330 _kvm_gen68k_pa2off(kvm_t *kd, u_long pa) 331 { 332 cpu_kcore_hdr_t *h = kd->cpu_data; 333 struct gen68k_kcore_hdr *gen = &h->un._gen68k; 334 phys_ram_seg_t *rsp = gen->ram_segs; 335 off_t off = 0; 336 int i; 337 338 for (i = 0; i < GEN68K_NPHYS_RAM_SEGS && rsp[i].size != 0; i++) { 339 if (pa >= rsp[i].start && 340 pa < (rsp[i].start + rsp[i].size)) { 341 pa -= rsp[i].start; 342 break; 343 } 344 off += rsp[i].size; 345 } 346 return (kd->dump_off + off + pa); 347 } 348