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

163 lines
3.6 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_PPC
#include <sys/syspage.h>
#include <ppc/cpu.h>
#include <ppc/context.h>
/*
Define a structure used for communication to a GDB client
*/
typedef struct {
uint32_t gpr[32];
double fpr[32];
uint32_t pc;
uint32_t ps;
uint32_t cnd;
uint32_t lr;
uint32_t cnt;
uint32_t xer;
uint32_t mq;
} ppc_gdb_regs;
static int
ppc_init(int elf_cpu) {
if(elf_cpu != EM_PPC) return 0;
switch(note->cpu_info) {
case PPC_FAMILY_booke:
case PPC_FAMILY_900:
cpu->pgtbl_size = 0x2000;
break;
}
return 1;
}
#define ONE_TO_ONE_SIZE (256*1024*1024)
static int
ppc_v2p(uintptr_t vaddr, paddr64_t *paddrp, unsigned *lenp) {
uint32_t l2vaddr;
paddr64_t paddr;
unsigned offset;
unsigned pgsize;
unsigned l1shift;
unsigned l2mask;
unsigned pte_size;
union {
struct {
uint32_t paddr;
uint32_t flags;
} _bke;
uint32_t _400;
uint32_t _600;
uint32_t _800;
uint64_t _900;
} pte;
if(vaddr < ONE_TO_ONE_SIZE) {
*paddrp = vaddr;
*lenp = ONE_TO_ONE_SIZE - *paddrp;
return 1;
}
// consult the page table...
if(cpu->pgtbl_size == 0x2000) {
l1shift = 21;
l2mask = 0x1ff;
pte_size = 8;
} else {
l1shift = 22;
l2mask = 0x3ff;
pte_size = 4;
}
l2vaddr = endian_native32(((uintptr_t *)pgtbl)[vaddr >> l1shift]) & ~0x3ff;
if(l2vaddr == 0) return 0;
core_read_vaddr(l2vaddr + ((vaddr >> 12) & l2mask)*pte_size, &pte, pte_size);
pgsize = 0x1000;
switch(note->cpu_info) {
case PPC_FAMILY_booke:
pte._bke.paddr = endian_native32(pte._bke.paddr);
paddr = (pte._bke.paddr & ~0xfff) | ((paddr64_t)(pte._bke.paddr & 0xfff) << 32);
pte._bke.flags = endian_native32(pte._bke.flags);
pgsize = 0x400 << (((pte._bke.flags >> 8) & 0xf) * 2);
break;
case PPC_FAMILY_400:
case PPC_FAMILY_800:
pte._400 = endian_native32(pte._400);
if(!(pte._400 & 0x3ff)) return 0;
paddr = pte._400 & ~0x3ff;
break;
case PPC_FAMILY_600:
pte._600 = endian_native32(pte._600);
if(!(pte._600 & 0x3ff)) return 0;
paddr = ((paddr64_t)((pte._600) & ~(0x3ff)))
| ((paddr64_t)(pte._600 & 0x00000e00) << 24)
| ((paddr64_t)(pte._600 & 0x4) << 30);
break;
case PPC_FAMILY_900:
paddr = endian_native64(pte._900) & ~(paddr64_t)0xfff;
break;
default:
if(debug_flag > 1) {
fprintf(stderr, "Unknown PPC family\n");
}
return 0;
}
offset = vaddr & (pgsize-1);
*paddrp = paddr | offset;
*lenp = 0x1000 - offset;
return 1;
}
static void
ppc_cvt_regset(const void *in, void *out) {
const PPC_CPU_REGISTERS *ctx = in;
ppc_gdb_regs *gdb = out;
memcpy(gdb->gpr, ctx->gpr, sizeof(gdb->gpr));
memset(gdb->fpr, 0, sizeof(gdb->fpr));
gdb->pc = ctx->iar;
gdb->ps = ctx->msr;
gdb->cnd = ctx->cr;
gdb->lr = ctx->lr;
gdb->cnt = ctx->ctr;
gdb->xer = ctx->xer;
gdb->mq = ctx->u.mq;
}
struct cpuinfo cpu_ppc = {
ppc_init,
ppc_v2p,
ppc_cvt_regset,
0x1000,
sizeof(ppc_gdb_regs),
sizeof(uint32_t),
};