1 /* Bare metal PPC KVM app, for libre-soc simulator comparison purposes
2 Copyright (C) 2021 Lauri Kasanen
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, version 3 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/kvm.h>
28 #include <sys/ioctl.h>
31 #include <sys/types.h>
35 #define RAMSIZE (64 * 1024 * 1024)
36 #define PROGSTART 0x20000000
42 static void nukenewline(char buf
[]) {
44 for (i
= 0; buf
[i
]; i
++) {
52 static void help(const char argv0
[]) {
54 printf("Usage: %s [args] -i file.bin\n\n"
56 "-i --binary file Raw, bare metal executable\n"
57 "-g --intregs file Text file setting up GPRs\n"
58 "-f --fpregs file Text file setting up FPRs\n"
59 "-s --spregs file Text file setting up SPRs (unnecessary if only LR is needed)\n"
60 "-l --load file:addr Load this binary to RAM\n"
61 "-d --dump file:addr:len Save this RAM area after running\n"
62 "-t --trace file Save a full trace to this file\n"
63 "-h --help This help\n\n"
65 "Load and dump may be given multiple times. GPR/FPR are numbered,\n"
66 "SPRs are named.\n", argv0
);
71 static void parseregs(const char name
[], const uint8_t gpr
, struct kvm_regs
*regs
,
72 struct kvm_fpu
*fpregs
) {
74 FILE *f
= fopen(name
, "r");
76 printf("Can't open %s\n", name
);
82 while (fgets(buf
, 256, f
)) {
87 const uint8_t reg
= strtol(buf
, NULL
, 0);
88 const char *ptr
= strchr(buf
+ 1, ' ');
90 printf("Invalid line '%s'\n", buf
);
94 const uint64_t val
= strtol(ptr
, NULL
, 0);
100 fpregs
->fpr
[reg
] = val
;
107 static void parsesprs(const char name
[], struct kvm_regs
*regs
) {
109 FILE *f
= fopen(name
, "r");
111 printf("Can't open %s\n", name
);
117 while (fgets(buf
, 256, f
)) {
124 #define check(a) if (!strncasecmp(buf, a, sizeof(a) - 1))
133 printf("Unknown (unimplemented?) SPR register '%s'\n",
138 const char *ptr
= strchr(buf
+ 1, ' ');
140 printf("Invalid line '%s'\n", buf
);
144 const uint64_t val
= strtol(ptr
, NULL
, 0);
157 static void load(const char arg
[], uint8_t *ram
) {
160 const char *ptr
= strchr(arg
, ':');
162 printf("Invalid load\n");
166 const unsigned namelen
= ptr
- arg
;
168 strncpy(name
, arg
, namelen
);
169 name
[namelen
] = '\0';
171 const uint64_t addr
= strtol(ptr
+ 1, NULL
, 0);
173 FILE *f
= fopen(name
, "r");
175 printf("Can't open %s\n", name
);
179 fseek(f
, 0, SEEK_END
);
180 const unsigned len
= ftell(f
);
183 if (addr
+ len
>= RAMSIZE
) {
184 printf("Tried to use too much RAM\n");
188 printf("Loading %s to 0x%lx, %u bytes\n", name
, addr
, len
);
190 if (fread(&ram
[addr
], len
, 1, f
) != 1)
196 int main(int argc
, char **argv
) {
198 const struct option longopts
[] = {
199 { "binary", 1, NULL
, 'i' },
200 { "intregs", 1, NULL
, 'g' },
201 { "fpregs", 1, NULL
, 'f' },
202 { "spregs", 1, NULL
, 's' },
203 { "load", 1, NULL
, 'l' },
204 { "dump", 1, NULL
, 'd' },
205 { "trace", 1, NULL
, 't' },
206 { "help", 0, NULL
, 'h' },
209 const char opts
[] = "i:g:f:s:l:d:t:h";
212 struct kvm_regs regs
;
213 struct kvm_sregs sregs
;
214 struct kvm_fpu fpregs
;
216 FILE *binary
= NULL
, *trace
= NULL
;
217 char dumps
[MAXDUMPS
][PATH_MAX
];
218 unsigned num_dumps
= 0;
219 uint8_t *ram
, *progmem
;
223 if (posix_memalign((void **) &ram
, 64 * 1024, RAMSIZE
))
225 memset(ram
, 0, RAMSIZE
);
227 memset(®s
, 0, sizeof(struct kvm_regs
));
228 memset(&fpregs
, 0, sizeof(struct kvm_fpu
));
234 const int c
= getopt_long(argc
, argv
, opts
, longopts
, NULL
);
241 printf("Only one binary allowed\n");
245 binary
= fopen(optarg
, "r");
247 printf("Failed to open %s\n", optarg
);
251 binpath
= strdup(optarg
);
254 parseregs(optarg
, 1, ®s
, &fpregs
);
257 parseregs(optarg
, 0, ®s
, &fpregs
);
260 parsesprs(optarg
, ®s
);
266 if (num_dumps
>= MAXDUMPS
) {
267 printf("Too many dumps\n");
271 strncpy(dumps
[num_dumps
], optarg
, PATH_MAX
);
272 dumps
[num_dumps
][PATH_MAX
- 1] = '\0';
277 printf("Only one trace allowed\n");
281 trace
= fopen(optarg
, "w");
283 printf("Failed to open %s\n", optarg
);
298 fseek(binary
, 0, SEEK_END
);
299 const unsigned binlen
= ftell(binary
);
302 printf("Loading binary %u bytes\n", binlen
);
304 if (posix_memalign((void **) &progmem
, 64 * 1024, binlen
))
307 if (fread(progmem
, binlen
, 1, binary
) != 1)
311 kvm
= open("/dev/kvm", O_RDWR
| O_CLOEXEC
);
313 printf("Failed to open kvm, perhaps you lack permissions?\n");
318 ret
= ioctl(kvm
, KVM_GET_API_VERSION
, NULL
);
319 if (ret
== -1 || ret
!= 12) abort();
321 ret
= ioctl(kvm
, KVM_CHECK_EXTENSION
, KVM_CAP_PPC_GUEST_DEBUG_SSTEP
);
322 if (ret
== -1 || !ret
) {
323 printf("This system lacks single-stepping!\n");
327 vmfd
= ioctl(kvm
, KVM_CREATE_VM
, (unsigned long)0);
329 struct kvm_userspace_memory_region region
= {
331 .guest_phys_addr
= 0,
332 .memory_size
= RAMSIZE
,
333 .userspace_addr
= (uint64_t) ram
,
336 ioctl(vmfd
, KVM_SET_USER_MEMORY_REGION
, ®ion
);
339 region
.guest_phys_addr
= PROGSTART
;
340 region
.memory_size
= binlen
;
341 region
.userspace_addr
= (uint64_t) progmem
;
342 region
.flags
= KVM_MEM_READONLY
;
343 ioctl(vmfd
, KVM_SET_USER_MEMORY_REGION
, ®ion
);
345 vcpu
= ioctl(vmfd
, KVM_CREATE_VCPU
, (unsigned long)0);
346 const unsigned vcpulen
= ioctl(kvm
, KVM_GET_VCPU_MMAP_SIZE
, NULL
);
348 run
= mmap(NULL
, vcpulen
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, vcpu
, 0);
350 if (ioctl(vcpu
, KVM_GET_SREGS
, &sregs
) == -1)
352 if (ioctl(vcpu
, KVM_SET_SREGS
, &sregs
) == -1)
355 if (ioctl(vcpu
, KVM_SET_REGS
, ®s
) == -1)
358 const struct kvm_guest_debug dbg
= {
359 .control
= KVM_GUESTDBG_ENABLE
| KVM_GUESTDBG_SINGLESTEP
361 if (ioctl(vcpu
, KVM_SET_GUEST_DEBUG
, &dbg
) == -1)
366 if (ioctl(vcpu
, KVM_RUN
, NULL
) == -1)
368 printf("Exited because %u\n", run
->exit_reason
);
374 free((char *) binpath
);