1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 /*****************************************************************************/
9 /*                                                                           */
10 /* Adding New Commands                                                       */
11 /*                                                                           */
12 /* Each new command must have, at minimum, 3 components: a string that is    */
13 /* typed to invoke it (<name>_call), a string describing how to use the      */
14 /* command (<name>_help), and a function that implements it (<name>_f).      */
15 /*                                                                           */
16 /* After implementing these three things, you need to go to the bottom of    */
17 /* this file and add them to the cli_commands array.                         */
18 /*                                                                           */
19 /* <name>_call                                                               */
20 /*   This should be only lower case letters, no spaces/special characters.   */
21 /*                                                                           */
22 /* <name>_help                                                               */
23 /*   This can contain any characters you want and be as long as you want. It */
24 /*   should explain what the command does and describe each argument. Each   */
25 /*   line of this string must start with 2 spaces so help printouts are      */
26 /*   uniformly indented and easy to read.                                    */
27 /*                                                                           */
28 /* <name>_f                                                                  */
29 /*   The prototype is cli_ret_et <name>_f(int32_t argc, char **argv).  The   */
30 /*   parameter argv is an array of strings containing arguments, and argc is */
31 /*   the number of arguments in argv, so very similar to argc and argc in a  */
32 /*   typical C main function.  The first argument is always the command name */
33 /*   and then following arguments are what the user typed.  Each argument is */
34 /*   guaranteed to be a null terminated array of characters, so it is safe   */
35 /*   use functions such as strcmp that depend on this.                       */
36 /*                                                                           */
37 /* Useful APIs                                                               */
38 /*   cli_print and cli_printf                                                */
39 /*     Non-formatted and formatted print functions used to write text to the */
40 /*     console, see cli_module.h for full descriptions.                      */
41 /*   cli_getline                                                             */
42 /*     Retrieves a line of user input from the console, see cli_module.h for */
43 /*     full description.                                                     */
44 /*   cli_snprintf                                                            */
45 /*     Takes the place of snprintf and it's derivatives, a bit rudimentary   */
46 /*     but has no heap dependence, see cli_module.h for full descriptions.   */
47 /*   Most C Library Functions                                                */
48 /*     Most of the C library is accessible, barring functions that depend on */
49 /*     having heap access. (snprintf and derivatives, sscanf and derivatives,*/
50 /*     etc.)                                                                 */
51 /*                                                                           */
52 /*****************************************************************************/
53 
54 #include <cli.h>
55 #include <cli_config.h>
56 #include <cli_fifo.h>
57 #include <cli_platform.h>
58 
59 #include <fwk_io.h>
60 
61 #include <stdint.h>
62 #include <stdlib.h>
63 #include <string.h>
64 
65 /*
66  * dump_memory
67  * Reads the contents of a memory region and prints them to the terminal.
68  */
69 static const char dump_memory_call[] = "dumpmem";
70 static const char dump_memory_help[] =
71     "  Reads raw bytes from memory and displays it on the screen.\n"
72     "    Usage: dumpmem <base address> <number of bytes to read>\n"
73     "      Base address and size must be on 8 byte boundaries.\n";
74 #define NUM_BYTES_PER_LINE (8)
dump_memory_f(int32_t argc,char ** argv)75 static int32_t dump_memory_f(int32_t argc, char **argv)
76 {
77     uint8_t bytes[8] = { 0 };
78 
79     /*
80      * Reads aligned to 8 byte bondaries so remove lower 3 bits of address and
81      * size parameters.
82      */
83     uint32_t j;
84     uint32_t addr = (uint32_t)(strtoul(argv[1], 0, 0) & 0xFFFFFFF8);
85     uint32_t size = (uint32_t)(strtoul(argv[2], 0, 0) & 0x000003F8);
86     uint32_t i = 0;
87 
88     /* Sanity check. */
89     if (size == 0)
90         return FWK_E_PARAM;
91 
92     cli_printf(NONE, "Reading %d bytes from 0x%08x.\n", size, addr);
93 
94     /* Loop through all bytes. */
95     for (i = 0; i < size; i = i + NUM_BYTES_PER_LINE) {
96         /* Read line worth of bytes from EEPROM. */
97         memcpy((void *)bytes, (void *)(addr + i), NUM_BYTES_PER_LINE);
98 
99         /* Print line base address. */
100         cli_printf(NONE, "0x%08x", addr + i);
101 
102         /* Print hex bytes. */
103         for (j = 0; j < NUM_BYTES_PER_LINE; j++)
104             cli_printf(NONE, " %02x", bytes[j]);
105 
106         /* Print ASCII representation. */
107         cli_print(" \"");
108         for (j = 0; j < NUM_BYTES_PER_LINE; j++) {
109             if ((bytes[j] >= 0x20) && (bytes[j] <= 0x7E))
110                 /* Character is printing. */
111                 fwk_io_putch(fwk_io_stdout, bytes[j]);
112             else
113                 /* Character is non-printing so put a period. */
114                 fwk_io_putch(fwk_io_stdout, '.');
115         }
116         cli_print("\"\n");
117     }
118 
119     return FWK_SUCCESS;
120 }
121 
122 /*
123  * Cycle Memory
124  * Cycle the memory reads for up to 256 words and find the rate of change or
125  * deviations for given iteration
126  */
127 #define MAX_CYCLE_BUFFER_SZ 256
128 volatile uint32_t cycle_buffer[MAX_CYCLE_BUFFER_SZ] = { 0 };
129 
130 static const char cycle_memory_call[] = "cyclemem";
131 static const char cycle_memory_help[] =
132     "  Cycle memory for given iterations and displays data shifts.\n"
133     "   Usage: cyclemem <base address in hex> <num of words in decimal (max "
134     "256> <number of iterations to read in decimal>\n"
135     "   Base address and size must be on 4 byte boundaries.\n";
cycle_memory_f(int32_t argc,char ** argv)136 static int32_t cycle_memory_f(int32_t argc, char **argv)
137 {
138     uint32_t addr = (uint32_t)(strtoul(argv[1], 0, 0) & 0xFFFFFFF8);
139     uint32_t size = (uint32_t)strtoul(argv[2], 0, 0);
140     uint32_t iterations = (uint32_t)strtoul(argv[3], 0, 0);
141     volatile uint32_t *tmp_address = (volatile uint32_t *)addr;
142     uint32_t deviation_count = 0;
143     uint32_t cycle_count, index = 0;
144 
145     /* Sanity check. */
146     if ((size == 0) || (size > MAX_CYCLE_BUFFER_SZ))
147         return FWK_E_PARAM;
148 
149     memset((void *)cycle_buffer, 0, sizeof(uint32_t) * MAX_CYCLE_BUFFER_SZ);
150 
151     cli_printf(
152         NONE,
153         "Cycle Compare Starts for 0x%08x for Length 0x%08x for iterations "
154         "%d.\n",
155         addr,
156         size,
157         iterations);
158 
159     /* Snapshot the current state */
160     for (index = 0; index < size; index++)
161         cycle_buffer[index] = tmp_address[index];
162 
163     /* Loop through all bytes. */
164     for (cycle_count = 0; cycle_count < iterations; cycle_count++) {
165         for (index = 0; index < size; index++) {
166             if (tmp_address[index] != cycle_buffer[index]) {
167                 cycle_buffer[index] = tmp_address[index];
168                 deviation_count++;
169             }
170         }
171     }
172 
173     cli_printf(NONE, "Cycle Compare Deviation Count %d \r\n", deviation_count);
174 
175     return FWK_SUCCESS;
176 }
177 
178 /*
179  * read_memory
180  * Reads a value from memory and displays it on the terminal.
181  */
182 static const char read_memory_call[] = "readmem";
183 static const char read_memory_help[] =
184     "  Reads a value from memory.\n"
185     "    Usage: readmem <address> <width in bytes 1|2|4|8>\n"
186     "  WARNING: READING FROM ILLEGAL ADDRESSES CAN CRASH THE SYSTEM.";
read_memory_f(int32_t argc,char ** argv)187 static int32_t read_memory_f(int32_t argc, char **argv)
188 {
189     /* Checking for the correct number of arguments. */
190     if (argc != 3)
191         return FWK_E_PARAM;
192 
193     /* Getting address of access. */
194     uintptr_t address = (uintptr_t)strtoul(argv[1], 0, 0);
195 
196     /* Getting width of access and making sure it is valid. */
197     uint32_t width = strtoul(argv[2], 0, 0);
198     if (width != 1 && width != 2 && width != 4 && width != 8)
199         return FWK_E_PARAM;
200 
201     /* Switching based on width. */
202     cli_print("Value: 0x");
203     switch (width) {
204     case 1:
205         /* No boundary restrictions on single byte accesses. */
206         cli_printf(NONE, "%02x", *((uint8_t *)address));
207         break;
208     case 2:
209         if (address % 2) {
210             /* 16 bit accesses need to be aligned on 2 byte boundaries. */
211             return FWK_E_PARAM;
212         }
213         cli_printf(NONE, "%04x", *((uint16_t *)address));
214         break;
215     case 4:
216         if (address % 4) {
217             /* 32 bit accesses need to be aligned to 4 byte boundaries. */
218             return FWK_E_PARAM;
219         }
220         cli_printf(NONE, "%08x", *((uint32_t *)address));
221         break;
222     case 8:
223         if (address % 4) {
224             /* 64 bit accesses need to be aligned on 4 byte boundaries. */
225             return FWK_E_PARAM;
226         }
227         cli_printf(NONE, "%016lx", *((uint64_t *)address));
228         break;
229     }
230 
231     cli_print("\n");
232 
233     return FWK_SUCCESS;
234 }
235 
236 /*
237  * write_memory
238  * Writes either an 8, 16, 32, or 64 bit value to a memory address.
239  */
240 static const char write_memory_call[] = "writemem";
241 static const char write_memory_help[] =
242     "  Writes a value to memory.\n"
243     "    Usage: writemem <base address> <width in bytes 1|2|4|8> <value to "
244     "write>\n";
write_memory_f(int32_t argc,char ** argv)245 static int32_t write_memory_f(int32_t argc, char **argv)
246 {
247     /* Checking for the correct number of arguments. */
248     if (argc != 4)
249         return FWK_E_PARAM;
250 
251     /* Getting address of access. */
252     uintptr_t address = (uintptr_t)strtoul(argv[1], 0, 0);
253 
254     /* Getting width of access and making sure it is valid. */
255     uint32_t width = strtoul(argv[2], 0, 0);
256     if (width != 1 && width != 2 && width != 4 && width != 8)
257         return FWK_E_PARAM;
258 
259     /* Switching based on width. */
260     switch (width) {
261     case 1:
262         /* No boundary restrictions on single byte accesses. */
263         *((uint8_t *)address) = (uint8_t)strtoul(argv[3], 0, 0);
264         break;
265     case 2:
266         if (address % 2) {
267             /* 16 bit accesses need to be aligned on 2 byte boundaries. */
268             return FWK_E_PARAM;
269         }
270         *((uint16_t *)address) = (uint16_t)strtoul(argv[3], 0, 0);
271         break;
272     case 4:
273         if (address % 4) {
274             /* 32 bit accesses need to be aligned to 4 byte boundaries. */
275             return FWK_E_PARAM;
276         }
277         *((uint32_t *)address) = (uint32_t)strtoul(argv[3], 0, 0);
278         break;
279     case 8:
280         if (address % 4) {
281             /* 64 bit accesses need to be aligned on 4 byte boundaries. */
282             return FWK_E_PARAM;
283         }
284         *((uint64_t *)address) = (uint64_t)strtoull(argv[3], 0, 0);
285         break;
286     }
287 
288     return FWK_SUCCESS;
289 }
290 
291 /*
292  * time
293  * Prints the system up time or real time.
294  */
295 static const char uptime_call[] = "uptime";
296 static const char uptime_help[] = "  Prints the system uptime.";
uptime_f(int32_t argc,char ** argv)297 static int32_t uptime_f(int32_t argc, char **argv)
298 {
299     cli_timestamp_t t = { 0 };
300     cli_platform_get_time(&t);
301     cli_printf(
302         NONE,
303         "System Uptime: %02d:%02d:%02d:%02d.%02d\n",
304         t.days,
305         t.hours,
306         t.minutes,
307         t.seconds,
308         t.fraction);
309     return FWK_SUCCESS;
310 }
311 
312 /*
313  * reset_system
314  * Performs a software reset.
315  */
316 static const char reset_sys_call[] = "reset";
317 static const char reset_sys_help[] = "  Resets the system immediately.";
reset_sys_f(int32_t argc,char ** argv)318 static int32_t reset_sys_f(int32_t argc, char **argv)
319 {
320     cli_print("This command is not implemented.\n");
321     return 0;
322 }
323 
324 /*****************************************************************************/
325 /* Command Structure Array                                                   */
326 /*****************************************************************************/
327 extern const char checkpoint_call[];
328 extern const char checkpoint_help[];
329 extern int32_t checkpoint_f(int32_t argc, char **argv);
330 
331 /* The last parameter in each of the commands below indicates whether the */
332 /* command handles its own help or not.  Right now, the PCIe/CCIX commands */
333 /* are the only ones that do that. */
334 
335 cli_command_st cli_commands[] = {
336     /* Add commands in this section. */
337     { dump_memory_call, dump_memory_help, &dump_memory_f, false },
338     { cycle_memory_call, cycle_memory_help, &cycle_memory_f, false },
339     { read_memory_call, read_memory_help, &read_memory_f, false },
340     { write_memory_call, write_memory_help, &write_memory_f, false },
341     { reset_sys_call, reset_sys_help, &reset_sys_f, false },
342     { uptime_call, uptime_help, &uptime_f, false },
343     { checkpoint_call, checkpoint_help, &checkpoint_f, false },
344 
345     /* End of commands. */
346     { 0, 0, 0 }
347 };
348