License, options
[kvm-minippc.git] / main.c
1 /* Bare metal PPC KVM app, for libre-soc simulator comparison purposes
2 Copyright (C) 2021 Lauri Kasanen
3
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.
7
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.
12
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/>.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <linux/kvm.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #define MAXDUMPS 10
33
34 static void nukenewline(char buf[]) {
35 unsigned i;
36 for (i = 0; buf[i]; i++) {
37 if (buf[i] == '\n') {
38 buf[i] = '\0';
39 break;
40 }
41 }
42 }
43
44 static void help(const char argv0[]) {
45
46 printf("Usage: %s [args] -i file.bin\n\n"
47
48 "-i --binary file Raw, bare metal executable\n"
49 "-g --intregs file Text file setting up GPRs\n"
50 "-f --fpregs file Text file setting up FPRs\n"
51 "-s --spregs file Text file setting up SPRs (unnecessary if only LR is needed)\n"
52 "-l --load file:addr Load this binary to RAM\n"
53 "-d --dump file:addr:len Save this RAM area after running\n"
54 "-t --trace file Save a full trace to this file\n"
55 "-h --help This help\n\n"
56
57 "Load and dump may be given multiple times. GPR/FPR are numbered,\n"
58 "SPRs are named.\n", argv0);
59
60 exit(0);
61 }
62
63 static void parseregs(const char name[], const uint8_t gpr) {
64
65 FILE *f = fopen(name, "r");
66 if (!f) {
67 printf("Can't open %s\n", name);
68 exit(1);
69 }
70
71 char buf[256];
72
73 while (fgets(buf, 256, f)) {
74 if (buf[0] == '#')
75 continue;
76 nukenewline(buf);
77
78 const uint8_t reg = strtol(buf, NULL, 0);
79 const char *ptr = strchr(buf + 1, ' ');
80 if (!ptr) {
81 printf("Invalid line '%s'\n", buf);
82 continue;
83 }
84
85 const uint64_t val = strtol(ptr, NULL, 0);
86
87 // TODO apply reg
88 if (gpr) {
89 } else {
90 }
91 }
92
93 fclose(f);
94 }
95
96 static void parsesprs(const char name[]) {
97
98 FILE *f = fopen(name, "r");
99 if (!f) {
100 printf("Can't open %s\n", name);
101 exit(1);
102 }
103
104 char buf[256];
105
106 while (fgets(buf, 256, f)) {
107 if (buf[0] == '#')
108 continue;
109 nukenewline(buf);
110
111 uint8_t reg = 0xff;
112
113 #define check(a) if (!strncasecmp(buf, a, sizeof(a) - 1))
114
115 check("LR:") {
116 // TODO reg = ;
117 }
118
119 #undef check
120
121 if (reg == 0xff) {
122 printf("Unknown (unimplemented?) SPR register '%s'\n",
123 buf);
124 continue;
125 }
126
127 const char *ptr = strchr(buf + 1, ' ');
128 if (!ptr) {
129 printf("Invalid line '%s'\n", buf);
130 continue;
131 }
132
133 const uint64_t val = strtol(ptr, NULL, 0);
134
135 // TODO apply reg
136 }
137
138 fclose(f);
139 }
140
141 static void load(const char arg[]) {
142
143 char name[PATH_MAX];
144 const char *ptr = strchr(arg, ':');
145 if (!ptr) {
146 printf("Invalid load\n");
147 exit(1);
148 }
149
150 const unsigned namelen = ptr - arg;
151
152 strncpy(name, arg, namelen);
153 name[namelen] = '\0';
154
155 const uint64_t addr = strtol(ptr + 1, NULL, 0);
156
157 FILE *f = fopen(name, "r");
158 if (!f) {
159 printf("Can't open %s\n", name);
160 exit(1);
161 }
162
163 fseek(f, 0, SEEK_END);
164 const unsigned len = ftell(f);
165 rewind(f);
166
167 // TODO copy it up
168
169 fclose(f);
170 }
171
172 int main(int argc, char **argv) {
173
174 const struct option longopts[] = {
175 { "binary", 1, NULL, 'i' },
176 { "intregs", 1, NULL, 'g' },
177 { "fpregs", 1, NULL, 'f' },
178 { "spregs", 1, NULL, 's' },
179 { "load", 1, NULL, 'l' },
180 { "dump", 1, NULL, 'd' },
181 { "trace", 1, NULL, 't' },
182 { "help", 0, NULL, 'h' },
183 { NULL, 0, NULL, 0 }
184 };
185 const char opts[] = "i:g:f:s:l:d:t:h";
186
187 int kvm;
188 FILE *binary = NULL, *trace = NULL;
189 char dumps[MAXDUMPS][PATH_MAX];
190 unsigned num_dumps = 0;
191
192 while (1) {
193 const int c = getopt_long(argc, argv, opts, longopts, NULL);
194 if (c == -1)
195 break;
196
197 switch (c) {
198 case 'i':
199 if (binary) {
200 printf("Only one binary allowed\n");
201 return 1;
202 }
203
204 binary = fopen(optarg, "r");
205 if (!binary) {
206 printf("Failed to open %s\n", optarg);
207 return 1;
208 }
209 break;
210 case 'g':
211 parseregs(optarg, 1);
212 break;
213 case 'f':
214 parseregs(optarg, 0);
215 break;
216 case 's':
217 parsesprs(optarg);
218 break;
219 case 'l':
220 load(optarg);
221 break;
222 case 'd':
223 if (num_dumps >= MAXDUMPS) {
224 printf("Too many dumps\n");
225 return 1;
226 }
227
228 strncpy(dumps[num_dumps], optarg, PATH_MAX);
229 dumps[num_dumps][PATH_MAX - 1] = '\0';
230 num_dumps++;
231 break;
232 case 't':
233 if (trace) {
234 printf("Only one trace allowed\n");
235 return 1;
236 }
237
238 trace = fopen(optarg, "w");
239 if (!trace) {
240 printf("Failed to open %s\n", optarg);
241 return 1;
242 }
243 break;
244 case 'h':
245 default:
246 help(argv[0]);
247 break;
248 }
249 }
250
251 if (!binary) {
252 help(argv[0]);
253 }
254
255 fseek(binary, 0, SEEK_END);
256 const unsigned binlen = ftell(binary);
257 rewind(binary);
258
259 printf("Loading binary %u bytes\n", binlen); // TODO
260
261 kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
262 if (kvm < 0) {
263 printf("Failed to open kvm, perhaps you lack permissions?\n");
264 return 1;
265 }
266
267 int ret;
268 ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
269 if (ret == -1 || ret != 12) abort();
270
271 close(kvm);
272 return 0;
273 }