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