Files
QNX/utils/k/kdserver/cpu_mips.c
2025-08-20 19:02:58 +08:00

150 lines
4.0 KiB
C

/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
#include "kdserver.h"
#define SYSPAGE_TARGET_MIPS
#include <sys/syspage.h>
#include <mips/cpu.h>
#include <mips/vm.h>
#include <mips/context.h>
/*
Define a structure used for communication to a GDB client
*/
typedef struct {
int regs[32]; /* CPU registers */
int sr; /* status register */
int lo; /* LO */
int hi; /* HI */
int bad; /* BadVaddr */
int cause; /* Cause */
int pc; /* EPC */
int fp; /* Psuedo frame pointer */
} mips_gdb_regs;
typedef struct {
uint32_t lo;
uint32_t pm;
} pte_t;
static unsigned pfn_topshift;
static int
mips_init(int elf_cpu) {
if(elf_cpu != EM_MIPS) return 0;
pfn_topshift = note->cpu_info & MIPS_CPU_FLAG_PFNTOPSHIFT_MASK;
return 1;
}
static int
mips_v2p(uintptr_t vaddr, paddr64_t *paddrp, unsigned *lenp) {
uint32_t l2vaddr;
pte_t pte;
paddr64_t paddr;
unsigned offset;
unsigned pgsize;
pte_t **page_table = pgtbl;
if(MIPS_IS_KSEG0(vaddr)) {
*paddrp = MIPS_KSEG0_TO_PHYS(vaddr);
*lenp = MIPS_R4K_K0SIZE - *paddrp;
return 1;
}
// consult the page table...
l2vaddr = endian_native32((uintptr_t)page_table[L1IDX(vaddr)]);
if(l2vaddr == 0) return 0;
core_read_paddr(MIPS_KSEG0_TO_PHYS(l2vaddr) + L2IDX(vaddr)*sizeof(pte_t), &pte, sizeof(pte));
pte.lo = endian_native32(pte.lo);
pte.pm = endian_native32(pte.pm);
if(note->cpu_info & MIPS_CPU_FLAG_NO_WIRED) {
// was an R3K
if(!(pte.lo & MIPS3K_TLB_VALID)) return 0;
paddr = pte.lo & MIPS3K_TLB_LO_PFNMASK;
pgsize = 0x1000;
} else {
if(!(pte.lo & MIPS_TLB_VALID)) return 0;
paddr = ((paddr64_t)(pte.lo & MIPS_TLB_LO_PFNMASK)) << pfn_topshift;
pgsize = ((pte.pm | 0x1fff) + 1) >> 1;
}
offset = vaddr & (pgsize-1);
*paddrp = paddr | offset;
*lenp = pgsize - offset;
return 1;
}
static void
mips_cvt_regset(const void *in, void *out) {
const MIPS_CPU_REGISTERS *ctx = in;
mips_gdb_regs *gdb = out;
unsigned i;
int big_endian;
#if defined(__BIGENDIAN__)
#define NATIVE_BIG 1
#elif defined(__LITTLEENDIAN__)
#define NATIVE_BIG 0
#else
#error ENDIAN not defined
#endif
if(endian_native32(1) != 1) {
// cross endian
big_endian = !NATIVE_BIG;
} else {
big_endian = NATIVE_BIG;
}
// Playing games. The MIPS_CREG macro assumes that we're running
// on the actual system as far as endianness goes. We're going
// to undef MIPS_REGS_LOW_WORD (which is the dependent piece)
// and redefine it so that it works properly for the endianness
// of the target system, not the host that kdserver is running on.
#undef MIPS_REGS_LOW_WORD
#define MIPS_REGS_LOW_WORD big_endian
for(i=0; i < 32; ++i) {
gdb->regs[i] = ctx->regs[MIPS_CREG(i)];
}
gdb->sr = ctx->regs[MIPS_CREG(MIPS_REG_SREG)];
gdb->lo = ctx->regs[MIPS_CREG(MIPS_REG_LO)];
gdb->hi = ctx->regs[MIPS_CREG(MIPS_REG_HI)];
gdb->bad = ctx->regs[MIPS_CREG(MIPS_REG_BADVADDR)];
gdb->cause = 0;
gdb->pc = ctx->regs[MIPS_CREG(MIPS_REG_EPC)];
gdb->fp = ctx->regs[MIPS_CREG(MIPS_REG_SP)];
}
struct cpuinfo cpu_mips = {
mips_init,
mips_v2p,
mips_cvt_regset,
0x2000,
sizeof(mips_gdb_regs),
sizeof(uint32_t),
};