Changeset View
Changeset View
Standalone View
Standalone View
lib/libkvm/kvm_sparc64.c
Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
#endif /* LIBC_SCCS and not lint */ | #endif /* LIBC_SCCS and not lint */ | ||||
/* | /* | ||||
* sparc64 machine dependent routines for kvm. | * sparc64 machine dependent routines for kvm. | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/user.h> | #include <kvm.h> | ||||
#include <sys/proc.h> | #include <limits.h> | ||||
#include <sys/stat.h> | #include <stdint.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <nlist.h> | |||||
#include <kvm.h> | |||||
#include <vm/vm.h> | #include "../../sys/sparc64/include/kerneldump.h" | ||||
#include <vm/vm_param.h> | |||||
#include <machine/kerneldump.h> | |||||
#include <machine/tte.h> | |||||
#include <machine/tlb.h> | |||||
#include <machine/tsb.h> | |||||
#include <limits.h> | |||||
#include "kvm_private.h" | #include "kvm_private.h" | ||||
#include "kvm_sparc64.h" | |||||
#ifndef btop | |||||
#define btop(x) (sparc64_btop(x)) | |||||
#define ptob(x) (sparc64_ptob(x)) | |||||
#endif | |||||
struct vmstate { | struct vmstate { | ||||
off_t vm_tsb_off; | off_t vm_tsb_off; | ||||
vm_size_t vm_tsb_mask; | uint64_t vm_tsb_mask; | ||||
int vm_nregions; | int vm_nregions; | ||||
struct sparc64_dump_reg *vm_regions; | struct sparc64_dump_reg *vm_regions; | ||||
}; | }; | ||||
void | static int | ||||
_kvm_freevtop(kvm_t *kd) | _sparc64_probe(kvm_t *kd) | ||||
{ | { | ||||
if (kd->vmst != 0) { | |||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_SPARCV9)); | |||||
} | |||||
static void | |||||
_sparc64_freevtop(kvm_t *kd) | |||||
{ | |||||
free(kd->vmst->vm_regions); | free(kd->vmst->vm_regions); | ||||
free(kd->vmst); | free(kd->vmst); | ||||
kd->vmst = NULL; | |||||
} | } | ||||
} | |||||
static int | static int | ||||
_kvm_read_phys(kvm_t *kd, off_t pos, void *buf, size_t size) | _sparc64_read_phys(kvm_t *kd, off_t pos, void *buf, size_t size) | ||||
{ | { | ||||
/* XXX This has to be a raw file read, kvm_read is virtual. */ | /* XXX This has to be a raw file read, kvm_read is virtual. */ | ||||
if (lseek(kd->pmfd, pos, SEEK_SET) == -1) { | if (pread(kd->pmfd, buf, size, pos) != (ssize_t)size) { | ||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: lseek"); | _kvm_syserr(kd, kd->program, "_sparc64_read_phys: pread"); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (read(kd->pmfd, buf, size) != (ssize_t)size) { | |||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: read"); | |||||
return (0); | |||||
} | |||||
return (1); | return (1); | ||||
} | } | ||||
static int | static int | ||||
_kvm_reg_cmp(const void *a, const void *b) | _sparc64_reg_cmp(const void *a, const void *b) | ||||
{ | { | ||||
const struct sparc64_dump_reg *ra, *rb; | const struct sparc64_dump_reg *ra, *rb; | ||||
ra = a; | ra = a; | ||||
rb = b; | rb = b; | ||||
if (ra->dr_pa < rb->dr_pa) | if (ra->dr_pa < rb->dr_pa) | ||||
return (-1); | return (-1); | ||||
else if (ra->dr_pa >= rb->dr_pa + rb->dr_size) | else if (ra->dr_pa >= rb->dr_pa + rb->dr_size) | ||||
return (1); | return (1); | ||||
else | else | ||||
return (0); | return (0); | ||||
} | } | ||||
#define KVM_OFF_NOTFOUND 0 | #define KVM_OFF_NOTFOUND 0 | ||||
static off_t | static off_t | ||||
_kvm_find_off(struct vmstate *vm, vm_offset_t pa, vm_size_t size) | _sparc64_find_off(struct vmstate *vm, uint64_t pa, uint64_t size) | ||||
{ | { | ||||
struct sparc64_dump_reg *reg, key; | struct sparc64_dump_reg *reg, key; | ||||
vm_offset_t o; | vm_offset_t o; | ||||
key.dr_pa = pa; | key.dr_pa = pa; | ||||
reg = bsearch(&key, vm->vm_regions, vm->vm_nregions, | reg = bsearch(&key, vm->vm_regions, vm->vm_nregions, | ||||
sizeof(*vm->vm_regions), _kvm_reg_cmp); | sizeof(*vm->vm_regions), _sparc64_reg_cmp); | ||||
if (reg == NULL) | if (reg == NULL) | ||||
return (KVM_OFF_NOTFOUND); | return (KVM_OFF_NOTFOUND); | ||||
o = pa - reg->dr_pa; | o = pa - reg->dr_pa; | ||||
if (o + size > reg->dr_size) | if (o + size > reg->dr_size) | ||||
return (KVM_OFF_NOTFOUND); | return (KVM_OFF_NOTFOUND); | ||||
return (reg->dr_offs + o); | return (reg->dr_offs + o); | ||||
} | } | ||||
int | static int | ||||
_kvm_initvtop(kvm_t *kd) | _sparc64_initvtop(kvm_t *kd) | ||||
{ | { | ||||
struct sparc64_dump_hdr hdr; | struct sparc64_dump_hdr hdr; | ||||
struct sparc64_dump_reg *regs; | struct sparc64_dump_reg *regs; | ||||
struct vmstate *vm; | struct vmstate *vm; | ||||
size_t regsz; | size_t regsz; | ||||
vm_offset_t pa; | uint64_t pa; | ||||
int i; | |||||
vm = (struct vmstate *)_kvm_malloc(kd, sizeof(*vm)); | vm = (struct vmstate *)_kvm_malloc(kd, sizeof(*vm)); | ||||
if (vm == NULL) { | if (vm == NULL) { | ||||
_kvm_err(kd, kd->program, "cannot allocate vm"); | _kvm_err(kd, kd->program, "cannot allocate vm"); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
kd->vmst = vm; | kd->vmst = vm; | ||||
if (!_kvm_read_phys(kd, 0, &hdr, sizeof(hdr))) | if (!_sparc64_read_phys(kd, 0, &hdr, sizeof(hdr))) | ||||
goto fail_vm; | goto fail_vm; | ||||
hdr.dh_hdr_size = be64toh(hdr.dh_hdr_size); | |||||
hdr.dh_tsb_pa = be64toh(hdr.dh_tsb_pa); | |||||
hdr.dh_tsb_size = be64toh(hdr.dh_tsb_size); | |||||
hdr.dh_tsb_mask = be64toh(hdr.dh_tsb_mask); | |||||
hdr.dh_nregions = be32toh(hdr.dh_nregions); | |||||
pa = hdr.dh_tsb_pa; | pa = hdr.dh_tsb_pa; | ||||
regsz = hdr.dh_nregions * sizeof(*regs); | regsz = hdr.dh_nregions * sizeof(*regs); | ||||
regs = _kvm_malloc(kd, regsz); | regs = _kvm_malloc(kd, regsz); | ||||
if (regs == NULL) { | if (regs == NULL) { | ||||
_kvm_err(kd, kd->program, "cannot allocate regions"); | _kvm_err(kd, kd->program, "cannot allocate regions"); | ||||
goto fail_vm; | goto fail_vm; | ||||
} | } | ||||
if (!_kvm_read_phys(kd, sizeof(hdr), regs, regsz)) | if (!_sparc64_read_phys(kd, sizeof(hdr), regs, regsz)) | ||||
goto fail_regs; | goto fail_regs; | ||||
qsort(regs, hdr.dh_nregions, sizeof(*regs), _kvm_reg_cmp); | for (i = 0; i < hdr.dh_nregions; i++) { | ||||
regs[i].dr_pa = be64toh(regs[i].dr_pa); | |||||
regs[i].dr_size = be64toh(regs[i].dr_size); | |||||
regs[i].dr_offs = be64toh(regs[i].dr_offs); | |||||
} | |||||
qsort(regs, hdr.dh_nregions, sizeof(*regs), _sparc64_reg_cmp); | |||||
vm->vm_tsb_mask = hdr.dh_tsb_mask; | vm->vm_tsb_mask = hdr.dh_tsb_mask; | ||||
vm->vm_regions = regs; | vm->vm_regions = regs; | ||||
vm->vm_nregions = hdr.dh_nregions; | vm->vm_nregions = hdr.dh_nregions; | ||||
vm->vm_tsb_off = _kvm_find_off(vm, hdr.dh_tsb_pa, hdr.dh_tsb_size); | vm->vm_tsb_off = _sparc64_find_off(vm, hdr.dh_tsb_pa, hdr.dh_tsb_size); | ||||
if (vm->vm_tsb_off == KVM_OFF_NOTFOUND) { | if (vm->vm_tsb_off == KVM_OFF_NOTFOUND) { | ||||
_kvm_err(kd, kd->program, "tsb not found in dump"); | _kvm_err(kd, kd->program, "tsb not found in dump"); | ||||
goto fail_regs; | goto fail_regs; | ||||
} | } | ||||
return (0); | return (0); | ||||
fail_regs: | fail_regs: | ||||
free(regs); | free(regs); | ||||
fail_vm: | fail_vm: | ||||
free(vm); | free(vm); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
int | static int | ||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa) | _sparc64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa) | ||||
{ | { | ||||
struct tte tte; | struct sparc64_tte tte; | ||||
off_t tte_off; | off_t tte_off; | ||||
u_long vpn; | kvaddr_t vpn; | ||||
off_t pa_off; | off_t pa_off; | ||||
u_long pg_off; | kvaddr_t pg_off; | ||||
int rest; | int rest; | ||||
pg_off = va & PAGE_MASK; | pg_off = va & SPARC64_PAGE_MASK; | ||||
if (va >= VM_MIN_DIRECT_ADDRESS) | if (va >= SPARC64_MIN_DIRECT_ADDRESS) | ||||
pa_off = TLB_DIRECT_TO_PHYS(va) & ~PAGE_MASK; | pa_off = SPARC64_DIRECT_TO_PHYS(va) & ~SPARC64_PAGE_MASK; | ||||
else { | else { | ||||
vpn = btop(va); | vpn = va >> SPARC64_PAGE_SHIFT; | ||||
tte_off = kd->vmst->vm_tsb_off + | tte_off = kd->vmst->vm_tsb_off + | ||||
((vpn & kd->vmst->vm_tsb_mask) << TTE_SHIFT); | ((vpn & kd->vmst->vm_tsb_mask) << SPARC64_TTE_SHIFT); | ||||
if (!_kvm_read_phys(kd, tte_off, &tte, sizeof(tte))) | if (!_sparc64_read_phys(kd, tte_off, &tte, sizeof(tte))) | ||||
goto invalid; | goto invalid; | ||||
if (!tte_match(&tte, va)) | tte.tte_vpn = be64toh(tte.tte_vpn); | ||||
tte.tte_data = be64toh(tte.tte_data); | |||||
if (!sparc64_tte_match(&tte, va)) | |||||
goto invalid; | goto invalid; | ||||
pa_off = TTE_GET_PA(&tte); | pa_off = SPARC64_TTE_GET_PA(&tte); | ||||
} | } | ||||
rest = PAGE_SIZE - pg_off; | rest = SPARC64_PAGE_SIZE - pg_off; | ||||
pa_off = _kvm_find_off(kd->vmst, pa_off, rest); | pa_off = _sparc64_find_off(kd->vmst, pa_off, rest); | ||||
if (pa_off == KVM_OFF_NOTFOUND) | if (pa_off == KVM_OFF_NOTFOUND) | ||||
goto invalid; | goto invalid; | ||||
*pa = pa_off + pg_off; | *pa = pa_off + pg_off; | ||||
return (rest); | return (rest); | ||||
invalid: | invalid: | ||||
_kvm_err(kd, 0, "invalid address (%lx)", va); | _kvm_err(kd, 0, "invalid address (%jx)", (uintmax_t)va); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
_sparc64_native(kvm_t *kd) | |||||
{ | |||||
#ifdef __sparc64__ | |||||
return (1); | |||||
#else | |||||
return (0); | |||||
#endif | |||||
} | |||||
struct kvm_arch kvm_sparc64 = { | |||||
.ka_probe = _sparc64_probe, | |||||
.ka_initvtop = _sparc64_initvtop, | |||||
.ka_freevtop = _sparc64_freevtop, | |||||
.ka_kvatop = _sparc64_kvatop, | |||||
.ka_native = _sparc64_native, | |||||
}; | |||||
KVM_ARCH(kvm_sparc64); |