Home | History | Annotate | Line # | Download | only in libkvm
      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