1 /*
2 * Copyright (c) 2008-2012 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <ctype.h>
10 #include <lk/debug.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <lk/list.h>
14 #include <string.h>
15 #include <arch/ops.h>
16 #include <platform.h>
17 #include <platform/debug.h>
18 #include <kernel/thread.h>
19 #include <arch.h>
20
21 #include <lk/console_cmd.h>
22
23 #if WITH_KERNEL_VM
24 #include <kernel/vm.h>
25 #endif
26
27 static int cmd_display_mem(int argc, const console_cmd_args *argv);
28 static int cmd_modify_mem(int argc, const console_cmd_args *argv);
29 static int cmd_fill_mem(int argc, const console_cmd_args *argv);
30 static int cmd_reset(int argc, const console_cmd_args *argv);
31 static int cmd_memtest(int argc, const console_cmd_args *argv);
32 static int cmd_copy_mem(int argc, const console_cmd_args *argv);
33 static int cmd_chain(int argc, const console_cmd_args *argv);
34 static int cmd_sleep(int argc, const console_cmd_args *argv);
35 static int cmd_time(int argc, const console_cmd_args *argv);
36 static int cmd_timeh(int argc, const console_cmd_args *argv);
37 static int cmd_crash(int argc, const console_cmd_args *argv);
38 static int cmd_panic(int argc, const console_cmd_args *argv);
39 static int cmd_stackstomp(int argc, const console_cmd_args *argv);
40
41 STATIC_COMMAND_START
42 #if LK_DEBUGLEVEL > 0
43 STATIC_COMMAND_MASKED("dw", "display memory in words", &cmd_display_mem, CMD_AVAIL_ALWAYS)
44 STATIC_COMMAND_MASKED("dh", "display memory in halfwords", &cmd_display_mem, CMD_AVAIL_ALWAYS)
45 STATIC_COMMAND_MASKED("db", "display memory in bytes", &cmd_display_mem, CMD_AVAIL_ALWAYS)
46 STATIC_COMMAND_MASKED("mw", "modify word of memory", &cmd_modify_mem, CMD_AVAIL_ALWAYS)
47 STATIC_COMMAND_MASKED("mh", "modify halfword of memory", &cmd_modify_mem, CMD_AVAIL_ALWAYS)
48 STATIC_COMMAND_MASKED("mb", "modify byte of memory", &cmd_modify_mem, CMD_AVAIL_ALWAYS)
49 STATIC_COMMAND_MASKED("fw", "fill range of memory by word", &cmd_fill_mem, CMD_AVAIL_ALWAYS)
50 STATIC_COMMAND_MASKED("fh", "fill range of memory by halfword", &cmd_fill_mem, CMD_AVAIL_ALWAYS)
51 STATIC_COMMAND_MASKED("fb", "fill range of memory by byte", &cmd_fill_mem, CMD_AVAIL_ALWAYS)
52 STATIC_COMMAND_MASKED("mc", "copy a range of memory", &cmd_copy_mem, CMD_AVAIL_ALWAYS)
53 STATIC_COMMAND("crash", "intentionally crash", &cmd_crash)
54 STATIC_COMMAND("panic", "intentionally panic", &cmd_panic)
55 STATIC_COMMAND("stackstomp", "intentionally overrun the stack", &cmd_stackstomp)
56 #endif
57 #if LK_DEBUGLEVEL > 1
58 STATIC_COMMAND("mtest", "simple memory test", &cmd_memtest)
59 #endif
60 STATIC_COMMAND("chain", "chain load another binary", &cmd_chain)
61 STATIC_COMMAND("sleep", "sleep number of seconds", &cmd_sleep)
62 STATIC_COMMAND("sleepm", "sleep number of milliseconds", &cmd_sleep)
63 STATIC_COMMAND("time", "print current time", &cmd_time)
64 STATIC_COMMAND("timeh", "print current time hires", &cmd_timeh)
65 STATIC_COMMAND_END(mem);
66
67 #define EXIT_IF_NOT_MAPPED(address) \
68 do { \
69 if (vaddr_to_paddr((void *)address) == 0) { \
70 printf("ERROR: address 0x%lx is unmapped\n", address); \
71 return -1; \
72 } \
73 } while (0)
74
75 #if WITH_KERNEL_VM
76 /*
77 * Checks if the address requested is mapped. Mapping is checked at page level,
78 * in the address range make sure all the pages are mapped.
79 * Adjust stop address to last mapped address in the range.
80 */
check_address_mapped(unsigned long start,unsigned long * stop)81 static int check_address_mapped(unsigned long start, unsigned long *stop) {
82 unsigned long page_curr;
83 if (stop == NULL) {
84 return -1;
85 }
86 page_curr = ROUNDDOWN(start, PAGE_SIZE);
87
88 /* if the first page itself unmapped, return error */
89 EXIT_IF_NOT_MAPPED(page_curr);
90
91 /* check rest of the pages */
92 page_curr += PAGE_SIZE;
93 while (page_curr < *stop) {
94 if (vaddr_to_paddr((void *)page_curr) == 0) {
95 /* there is an unmpaeed page in the range, adjust stop */
96 *stop = page_curr; /* don't access beyond this */
97 printf("INFO: access truncated to 0x%lx as no further mapping\n", *stop);
98 /* this ain't an error, the requested len is not mapped */
99 return 0;
100 }
101 page_curr += PAGE_SIZE;
102 }
103 return 0;
104 }
105 #endif
106
cmd_display_mem(int argc,const console_cmd_args * argv)107 static int cmd_display_mem(int argc, const console_cmd_args *argv) {
108 /* save the last address and len so we can continue where we left off */
109 static unsigned long address;
110 static size_t len;
111
112 if (argc < 3 && len == 0) {
113 printf("not enough arguments\n");
114 #if WITH_KERNEL_VM
115 printf("%s [-l] [-b] [-p] [address] [length]\n", argv[0].str);
116 #else
117 printf("%s [-l] [-b] [address] [length]\n", argv[0].str);
118 #endif
119 printf(" -l little endian\n"
120 " -b big endian\n");
121 #if WITH_KERNEL_VM
122 printf(" -p physical address\n");
123 #endif
124 return -1;
125 }
126
127 uint32_t size;
128 if (strcmp(argv[0].str, "dw") == 0) {
129 size = 4;
130 } else if (strcmp(argv[0].str, "dh") == 0) {
131 size = 2;
132 } else {
133 size = 1;
134 }
135
136 uint byte_order = BYTE_ORDER;
137 int argindex = 1;
138 bool read_address = false;
139 #if WITH_KERNEL_VM
140 bool phy_addr = false;
141 #endif
142 while (argc > argindex) {
143 if (!strcmp(argv[argindex].str, "-l")) {
144 byte_order = LITTLE_ENDIAN;
145 } else if (!strcmp(argv[argindex].str, "-b")) {
146 byte_order = BIG_ENDIAN;
147 #if WITH_KERNEL_VM
148 } else if (!strcmp(argv[argindex].str, "-p")) {
149 phy_addr = true;
150 #endif
151 } else if (!read_address) {
152 address = argv[argindex].u;
153 read_address = true;
154 } else {
155 len = argv[argindex].u;
156 }
157
158 argindex++;
159 }
160
161 #if WITH_KERNEL_VM
162 if (phy_addr == true) {
163 address = (unsigned long)paddr_to_kvaddr(address);
164 }
165 #endif
166 unsigned long stop = address + len;
167 int count = 0;
168
169 if ((address & (size - 1)) != 0) {
170 printf("unaligned address, cannot display\n");
171 return -1;
172 }
173
174 #if WITH_KERNEL_VM
175 if (check_address_mapped(address, &stop))
176 return -1;
177 #endif
178
179 for ( ; address < stop; address += size) {
180 if (count == 0)
181 printf("0x%08lx: ", address);
182 switch (size) {
183 case 4: {
184 uint32_t val = (byte_order != BYTE_ORDER) ?
185 SWAP_32(*(uint32_t *)address) :
186 *(uint32_t *)address;
187 printf("%08x ", val);
188 break;
189 }
190 case 2: {
191 uint16_t val = (byte_order != BYTE_ORDER) ?
192 SWAP_16(*(uint16_t *)address) :
193 *(uint16_t *)address;
194 printf("%04hx ", val);
195 break;
196 }
197 case 1:
198 printf("%02hhx ", *(uint8_t *)address);
199 break;
200 }
201 count += size;
202 if (count == 16) {
203 printf("\n");
204 count = 0;
205 }
206 }
207
208 if (count != 0)
209 printf("\n");
210
211 return 0;
212 }
213
cmd_modify_mem(int argc,const console_cmd_args * argv)214 static int cmd_modify_mem(int argc, const console_cmd_args *argv) {
215 uint32_t size;
216 unsigned long address = 0;
217 unsigned int val = 0;
218
219 if (argc < 3) {
220 printf("not enough arguments\n");
221 #if WITH_KERNEL_VM
222 printf("%s [-p] <address> <val>\n"
223 " -p physical address\n", argv[0].str);
224 #else
225 printf("%s <address> <val>\n", argv[0].str);
226 #endif
227 return -1;
228 }
229
230 if (strcmp(argv[0].str, "mw") == 0) {
231 size = 4;
232 } else if (strcmp(argv[0].str, "mh") == 0) {
233 size = 2;
234 } else {
235 size = 1;
236 }
237
238 int argindex = 1;
239 bool read_address = false;
240 #if WITH_KERNEL_VM
241 bool phy_addr = false;
242 #endif
243
244 while (argc > argindex) {
245 #if WITH_KERNEL_VM
246 if (!strcmp(argv[argindex].str, "-p")) {
247 phy_addr = true;
248 argindex++;
249 continue;
250 }
251 #endif
252 if (!read_address) {
253 address = argv[argindex].u;
254 read_address = true;
255 } else {
256 val = argv[argindex].u;
257 }
258
259 argindex++;
260 }
261
262 #if WITH_KERNEL_VM
263 if (phy_addr == true) {
264 address = (unsigned long)paddr_to_kvaddr(address);
265 }
266 #endif
267 if ((address & (size - 1)) != 0) {
268 printf("unaligned address, cannot modify\n");
269 return -1;
270 }
271
272 #if WITH_KERNEL_VM
273 /* preflight the page start address to see if it's mapped */
274 EXIT_IF_NOT_MAPPED(ROUNDDOWN(address, PAGE_SIZE));
275 #endif
276
277 switch (size) {
278 case 4:
279 *(uint32_t *)address = (uint32_t)val;
280 break;
281 case 2:
282 *(uint16_t *)address = (uint16_t)val;
283 break;
284 case 1:
285 *(uint8_t *)address = (uint8_t)val;
286 break;
287 }
288
289 return 0;
290 }
291
cmd_fill_mem(int argc,const console_cmd_args * argv)292 static int cmd_fill_mem(int argc, const console_cmd_args *argv) {
293 uint32_t size;
294
295 if (argc < 4) {
296 printf("not enough arguments\n");
297 printf("%s <address> <len> <val>\n", argv[0].str);
298 return -1;
299 }
300
301 if (strcmp(argv[0].str, "fw") == 0) {
302 size = 4;
303 } else if (strcmp(argv[0].str, "fh") == 0) {
304 size = 2;
305 } else {
306 size = 1;
307 }
308
309 unsigned long address = argv[1].u;
310 unsigned long len = argv[2].u;
311 unsigned long stop = address + len;
312 unsigned int val = argv[3].u;
313
314 if ((address & (size - 1)) != 0) {
315 printf("unaligned address, cannot modify\n");
316 return -1;
317 }
318
319 #if WITH_KERNEL_VM
320 if (check_address_mapped(address, &stop))
321 return -1;
322 #endif
323
324 for ( ; address < stop; address += size) {
325 switch (size) {
326 case 4:
327 *(uint32_t *)address = (uint32_t)val;
328 break;
329 case 2:
330 *(uint16_t *)address = (uint16_t)val;
331 break;
332 case 1:
333 *(uint8_t *)address = (uint8_t)val;
334 break;
335 }
336 }
337
338 return 0;
339 }
340
cmd_copy_mem(int argc,const console_cmd_args * argv)341 static int cmd_copy_mem(int argc, const console_cmd_args *argv) {
342 if (argc < 4) {
343 printf("not enough arguments\n");
344 printf("%s <source address> <target address> <len>\n", argv[0].str);
345 return -1;
346 }
347
348 addr_t source = argv[1].u;
349 addr_t target = argv[2].u;
350 size_t len = argv[3].u;
351
352 memcpy((void *)target, (const void *)source, len);
353
354 return 0;
355 }
356
cmd_memtest(int argc,const console_cmd_args * argv)357 static int cmd_memtest(int argc, const console_cmd_args *argv) {
358 if (argc < 3) {
359 printf("not enough arguments\n");
360 printf("%s <base> <len>\n", argv[0].str);
361 return -1;
362 }
363
364 uint32_t *ptr;
365 size_t len;
366
367 ptr = (uint32_t *)argv[1].u;
368 len = (size_t)argv[2].u;
369
370 size_t i;
371 // write out
372 printf("writing first pass...");
373 for (i = 0; i < len / 4; i++) {
374 ptr[i] = i;
375 }
376 printf("done\n");
377
378 // verify
379 printf("verifying...");
380 for (i = 0; i < len / 4; i++) {
381 if (ptr[i] != i)
382 printf("error at %p\n", &ptr[i]);
383 }
384 printf("done\n");
385
386 return 0;
387 }
388
cmd_chain(int argc,const console_cmd_args * argv)389 static int cmd_chain(int argc, const console_cmd_args *argv) {
390 if (argc < 2) {
391 printf("not enough arguments\n");
392 printf("%s <address>\n", argv[0].str);
393 return -1;
394 }
395
396 arch_chain_load(argv[1].p, 0, 0, 0, 0);
397
398 return 0;
399 }
400
cmd_sleep(int argc,const console_cmd_args * argv)401 static int cmd_sleep(int argc, const console_cmd_args *argv) {
402 lk_time_t t = 1000; /* default to 1 second */
403
404 if (argc >= 2) {
405 t = argv[1].u;
406 if (!strcmp(argv[0].str, "sleep"))
407 t *= 1000;
408 }
409
410 thread_sleep(t);
411
412 return 0;
413 }
414
cmd_time(int argc,const console_cmd_args * argv)415 static int cmd_time(int argc, const console_cmd_args *argv) {
416
417 lk_time_t t = current_time();
418 printf("Current time: %u\n", t);
419
420 return 0;
421 }
422
cmd_timeh(int argc,const console_cmd_args * argv)423 static int cmd_timeh(int argc, const console_cmd_args *argv) {
424
425 lk_bigtime_t t = current_time_hires();
426 printf("Current time hires: %llu\n", t);
427
428 return 0;
429 }
430
431 /* fix warning for the near-null pointer dereference below with gcc 12.x+ */
432 #pragma GCC diagnostic push
433 #pragma GCC diagnostic ignored "-Warray-bounds"
cmd_crash(int argc,const console_cmd_args * argv)434 static int cmd_crash(int argc, const console_cmd_args *argv) {
435 #if ARCH_ARM && ARM_ONLY_THUMB
436 /* a branch directly to an aligned address should trigger a fault */
437 asm("bx %0":: "r"(0));
438 #else
439 /* should crash */
440 volatile uint32_t *ptr = (void *)1;
441 *ptr = 1;
442 #endif
443
444 /* if it didn't, panic the system */
445 panic("crash");
446
447 return 0;
448 }
449 #pragma GCC diagnostic pop
450
cmd_panic(int argc,const console_cmd_args * argv)451 static int cmd_panic(int argc, const console_cmd_args *argv) {
452 panic("Test panic\n");
453 return 0;
454 }
455
cmd_stackstomp(int argc,const console_cmd_args * argv)456 static int cmd_stackstomp(int argc, const console_cmd_args *argv) {
457 for (size_t i = 0; i < DEFAULT_STACK_SIZE * 2; i++) {
458 uint8_t death[i];
459
460 memset(death, 0xaa, i);
461 thread_sleep(1);
462 }
463
464 printf("survived.\n");
465
466 return 0;
467 }
468
469
470