Ignore -p
[kvm-minippc.git] / main.c
diff --git a/main.c b/main.c
index 99c1041dbb2b182fc386d7e835ce901eac189a7b..86181fb7a7d738fe240977ffd0b5f7ce0da316d0 100644 (file)
--- a/main.c
+++ b/main.c
@@ -16,6 +16,7 @@
 
 #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,
 };
@@ -154,7 +160,7 @@ static void parsesprs(const char name[], struct kvm_regs *regs) {
        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, ':');
@@ -180,11 +186,121 @@ static void load(const char 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[] = {
@@ -198,7 +314,7 @@ int main(int argc, char **argv) {
                { "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;
@@ -209,13 +325,21 @@ int main(int argc, char **argv) {
        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(&regs, 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);
@@ -247,7 +371,7 @@ int main(int argc, char **argv) {
                                parsesprs(optarg, &regs);
                        break;
                        case 'l':
-                               load(optarg);
+                               load(optarg, ram);
                        break;
                        case 'd':
                                if (num_dumps >= MAXDUMPS) {
@@ -271,6 +395,8 @@ int main(int argc, char **argv) {
                                        return 1;
                                }
                        break;
+                       case 'p': // ignored
+                       break;
                        case 'h':
                        default:
                                help(argv[0]);
@@ -282,24 +408,29 @@ int main(int argc, char **argv) {
                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");
@@ -325,14 +456,15 @@ int main(int argc, char **argv) {
                .userspace_addr = (uint64_t) ram,
                .flags = 0
        };
-       ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
+       if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region) == -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, &region);
+       if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region) == -1)
+               abort();
 
        vcpu = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
        const unsigned vcpulen = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
@@ -341,11 +473,14 @@ int main(int argc, char **argv) {
 
        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, &regs) == -1)
                abort();
+       setfpregs(vcpu, &fpregs);
 
        const struct kvm_guest_debug dbg = {
                .control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP
@@ -354,15 +489,59 @@ int main(int argc, char **argv) {
                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, &regs) == -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;
 }