#define _GNU_SOURCE
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/types.h>
#include <unistd.h>
+#define PAGE_SIZE (64 * 1024) // assumption
#define MAXDUMPS 10
#define RAMSIZE (64 * 1024 * 1024)
#define PROGSTART 0x20000000
+#define MSR_64 (1UL<<63)
+#define MSR_FP (1UL<<13)
+#define MSR_LE (1UL<<0)
+
enum {
SPR_LR,
};
fclose(f);
}
-static void load(const char arg[]) {
+static void load(const char arg[], uint8_t *ram) {
char name[PATH_MAX];
const char *ptr = strchr(arg, ':');
const unsigned len = ftell(f);
rewind(f);
- // TODO copy it up
+ if (addr + len >= RAMSIZE) {
+ printf("Tried to use too much RAM\n");
+ exit(1);
+ }
+
+ printf("Loading %s to 0x%lx, %u bytes\n", name, addr, len);
+
+ if (fread(&ram[addr], len, 1, f) != 1)
+ abort();
+
+ fclose(f);
+}
+
+static void dump(const char src[], const uint8_t ram[]) {
+ const char *ptr = strchr(src, ':');
+ if (!ptr) {
+ printf("Invalid dump\n");
+ exit(1);
+ }
+
+ char name[PATH_MAX];
+ memcpy(name, src, ptr - src);
+ name[ptr - src] = '\0';
+
+ ptr++;
+ const uint32_t addr = strtol(ptr, NULL, 0);
+
+ ptr = strchr(ptr, ':');
+ if (!ptr) {
+ printf("Invalid dump\n");
+ exit(1);
+ }
+
+ ptr++;
+ const uint32_t len = strtol(ptr, NULL, 0);
+
+ //printf("Saving to %s, from %x, len %u\n", name, addr, len);
+
+ FILE *f = fopen(name, "w");
+ if (!f) {
+ printf("Can't open %s\n", name);
+ exit(1);
+ }
+
+ if (fwrite(&ram[addr], len, 1, f) != 1)
+ abort();
fclose(f);
}
+static void setfpregs(const int vcpu, struct kvm_fpu *fpregs) {
+ // KVM_[SG]ET_FPU isn't supported on PPC, we have to move individual regs...
+ unsigned i;
+ for (i = 0; i < 32; i++) {
+ struct kvm_one_reg r = {
+ .id = KVM_REG_PPC_FPR(i),
+ .addr = (uint64_t) &fpregs->fpr[i]
+ };
+
+ if (ioctl(vcpu, KVM_SET_ONE_REG, &r) != 0)
+ abort();
+ }
+}
+
+static void getfpregs(const int vcpu, struct kvm_fpu *fpregs) {
+ // KVM_[SG]ET_FPU isn't supported on PPC, we have to move individual regs...
+ unsigned i;
+ for (i = 0; i < 32; i++) {
+ struct kvm_one_reg r = {
+ .id = KVM_REG_PPC_FPR(i),
+ .addr = (uint64_t) &fpregs->fpr[i]
+ };
+
+ if (ioctl(vcpu, KVM_GET_ONE_REG, &r) != 0)
+ abort();
+ }
+}
+
+static void disassemble(const char binpath[], char *disasm, const unsigned len) {
+
+ char buf[PATH_MAX];
+ snprintf(buf, PATH_MAX,
+ "objdump -b binary -m powerpc --no-show-raw-insn -D %s",
+ binpath);
+ buf[PATH_MAX - 1] = '\0';
+
+ FILE *f = popen(buf, "r");
+ if (!f) {
+ printf("Disassembly failed, trace won't have disas\n");
+ return;
+ }
+
+ while (fgets(buf, PATH_MAX, f)) {
+ if (buf[0] != ' ')
+ continue;
+ const char *ptr = strchr(buf, ':');
+ if (!ptr)
+ continue;
+ nukenewline(buf);
+
+ const unsigned addr = strtol(buf, NULL, 16) / 4 * 32;
+ if (addr / 32 + 1 >= len)
+ abort();
+
+ ptr++;
+ while (isspace(*ptr))
+ ptr++;
+
+ strncpy(&disasm[addr], ptr, 32);
+ disasm[addr + 31] = '\0';
+ }
+
+ pclose(f);
+}
+
int main(int argc, char **argv) {
const struct option longopts[] = {
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
- const char opts[] = "i:g:f:s:l:d:t:h";
+ const char opts[] = "i:g:f:s:l:d:t:hp:";
struct kvm_run *run;
struct kvm_regs regs;
char dumps[MAXDUMPS][PATH_MAX];
unsigned num_dumps = 0;
uint8_t *ram, *progmem;
- const char *binpath;
+ const char *binpath = NULL;
+ const char *disasm = NULL;
+
+ // Yes, we're frugal
+ if (posix_memalign((void **) &ram, 64 * 1024, RAMSIZE))
+ abort();
+ memset(ram, 0, RAMSIZE);
memset(®s, 0, sizeof(struct kvm_regs));
memset(&fpregs, 0, sizeof(struct kvm_fpu));
regs.lr = -1;
regs.pc = PROGSTART;
+ regs.msr = MSR_64 | MSR_FP | MSR_LE;
+ regs.gpr[1] = 0x8000; // Default stack pointer at 32kb, 20kb free space before vecs
while (1) {
const int c = getopt_long(argc, argv, opts, longopts, NULL);
parsesprs(optarg, ®s);
break;
case 'l':
- load(optarg);
+ load(optarg, ram);
break;
case 'd':
if (num_dumps >= MAXDUMPS) {
return 1;
}
break;
+ case 'p': // ignored
+ break;
case 'h':
default:
help(argv[0]);
help(argv[0]);
}
- // Yes, we're frugal
- if (posix_memalign((void **) &ram, 64 * 1024, RAMSIZE))
- abort();
- memset(ram, 0, RAMSIZE);
-
fseek(binary, 0, SEEK_END);
const unsigned binlen = ftell(binary);
rewind(binary);
printf("Loading binary %u bytes\n", binlen);
- if (posix_memalign((void **) &progmem, 64 * 1024, binlen))
+ const unsigned progmemlen = (binlen + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+
+ if (posix_memalign((void **) &progmem, 64 * 1024, progmemlen))
abort();
if (fread(progmem, binlen, 1, binary) != 1)
abort();
fclose(binary);
+
+ if (trace) {
+ const unsigned disaslen = binlen / 4 + 64;
+ disasm = calloc(disaslen, 32);
+
+ disassemble(binpath, (char *) disasm, disaslen);
+ }
+
kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
if (kvm < 0) {
printf("Failed to open kvm, perhaps you lack permissions?\n");
.userspace_addr = (uint64_t) ram,
.flags = 0
};
- ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion);
+ if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion) == -1)
+ abort();
region.slot = 1;
region.guest_phys_addr = PROGSTART;
- region.memory_size = binlen;
+ region.memory_size = progmemlen;
region.userspace_addr = (uint64_t) progmem;
- region.flags = KVM_MEM_READONLY;
- ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion);
+ if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion) == -1)
+ abort();
vcpu = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
const unsigned vcpulen = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
if (ioctl(vcpu, KVM_GET_SREGS, &sregs) == -1)
abort();
+ // It defaults to host cpu on POWER5+, which is what we want.
+ // http://pearpc.sourceforge.net/pvr.html
if (ioctl(vcpu, KVM_SET_SREGS, &sregs) == -1)
abort();
if (ioctl(vcpu, KVM_SET_REGS, ®s) == -1)
abort();
+ setfpregs(vcpu, &fpregs);
const struct kvm_guest_debug dbg = {
.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP
abort();
// Runtime
+ uint64_t pc = PROGSTART;
+ unsigned i;
while (1) {
if (ioctl(vcpu, KVM_RUN, NULL) == -1)
abort();
- printf("Exited because %u\n", run->exit_reason);
+
+ if (ioctl(vcpu, KVM_GET_REGS, ®s) == -1)
+ abort();
+ //printf("PC %lx LR %lx\n", regs.pc, regs.lr);
+
+ if (run->exit_reason == KVM_EXIT_DEBUG) {
+ if (trace) {
+ getfpregs(vcpu, &fpregs);
+
+ fprintf(trace, "%lx: %s\n", pc, &disasm[(pc - PROGSTART) / 4 * 32]);
+ for (i = 0; i < 32; i++) {
+ if (i % 8 == 0)
+ fprintf(trace, "GPR: ");
+ fprintf(trace, "%08lx", regs.gpr[i]);
+ if (i % 8 == 7)
+ fprintf(trace, "\n");
+ else
+ fprintf(trace, " ");
+ }
+ for (i = 0; i < 32; i++) {
+ if (i % 8 == 0)
+ fprintf(trace, "FPR: ");
+ fprintf(trace, "%08lx", fpregs.fpr[i]);
+ if (i % 8 == 7)
+ fprintf(trace, "\n");
+ else
+ fprintf(trace, " ");
+ }
+ }
+ } else if (regs.pc < PROGSTART || regs.pc > PROGSTART + binlen) {
+ // Normal exit by blr
+ break;
+ } else {
+ printf("Unexpected exit because %u\n", run->exit_reason);
+ break;
+ }
+
+ pc = regs.pc;
+ }
+
+ for (i = 0; i < num_dumps; i++) {
+ dump(dumps[i], ram);
}
close(kvm);
free(progmem);
free(ram);
free((char *) binpath);
+ free((char *) disasm);
return 0;
}