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