1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Memory dump commands			File: ui_examcmds.c
5    *
6    *  UI functions for examining data in various ways
7    *
8    *  Author:  Mitch Lichtenberg
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47
48#include "cfe.h"
49#include "ui_command.h"
50#include "disasm.h"
51
52#include "lib_hssubr.h"
53#include "lib_memfuncs.h"
54
55
56static int ui_cmd_memdump(ui_cmdline_t *cmd,int argc,char *argv[]);
57static int ui_cmd_memedit(ui_cmdline_t *cmd,int argc,char *argv[]);
58static int ui_cmd_memfill(ui_cmdline_t *cmd,int argc,char *argv[]);
59static int ui_cmd_disasm(ui_cmdline_t *cmd,int argc,char *argv[]);
60
61#if CPUCFG_REGS64
62#define XTOI(x) xtoq(x)
63#define PTRFMT "%016llX"
64#else
65#define XTOI(x) xtoi(x)
66#define PTRFMT "%08lX"
67#endif
68
69int ui_init_examcmds(void);
70int dumpmem(hsaddr_t addr,hsaddr_t dispaddr,int length,int wlen);
71
72
73#define ATYPE_SIZE_NONE	0
74#define ATYPE_SIZE_BYTE	1
75#define ATYPE_SIZE_HALF	2
76#define ATYPE_SIZE_WORD	4
77#define ATYPE_SIZE_QUAD	8
78#define ATYPE_SIZE_MASK	0x0F
79
80#define ATYPE_TYPE_NONE	0
81#define ATYPE_TYPE_PHYS 0x10
82#define ATYPE_TYPE_KERN 0x20
83#define ATYPE_TYPE_MASK 0xF0
84
85static hsaddr_t prev_addr = 0;	/* initialized below in ui_init_examcmds */
86static int prev_length = 256;
87static int prev_dlength = 16;
88static int prev_wtype = ATYPE_SIZE_WORD | ATYPE_TYPE_KERN;
89
90static int getaddrargs(ui_cmdline_t *cmd,int *curtype,hsaddr_t *addr,int *length)
91{
92    int atype = *curtype;
93    hsaddr_t newaddr;
94    int newlen;
95    char *x;
96    hsaddr_t wlen;
97
98    if (cmd_sw_isset(cmd,"-b")) {
99	atype &= ~ATYPE_SIZE_MASK;
100	atype |= ATYPE_SIZE_BYTE;
101	}
102    else if (cmd_sw_isset(cmd,"-h")) {
103	atype &= ~ATYPE_SIZE_MASK;
104	atype |= ATYPE_SIZE_HALF;
105	}
106    else if (cmd_sw_isset(cmd,"-w")) {
107	atype &= ~ATYPE_SIZE_MASK;
108	atype |= ATYPE_SIZE_WORD;
109	}
110    else if (cmd_sw_isset(cmd,"-q")) {
111	atype &= ~ATYPE_SIZE_MASK;
112	atype |= ATYPE_SIZE_QUAD;
113	}
114
115    wlen = atype & ATYPE_SIZE_MASK;
116    if (wlen == 0) wlen = 1;		/* bytes are the default */
117
118    if (cmd_sw_isset(cmd,"-p")) {
119	atype &= ~ATYPE_TYPE_MASK;
120	atype |= ATYPE_TYPE_PHYS;
121	}
122    else if (cmd_sw_isset(cmd,"-v")) {
123	atype &= ~ATYPE_TYPE_MASK;
124	atype |= ATYPE_TYPE_KERN;
125	}
126
127    *curtype = atype;
128
129    if (addr) {
130	x = cmd_getarg(cmd,0);
131	if (x) {
132	    if (strcmp(x,".") == 0) newaddr = *addr;
133	    else {
134		/*
135		 * hold on to your lunch, this is really, really bad!
136		 * Make 64-bit addresses expressed as 8-digit numbers
137		 * sign extend automagically.  Saves typing, but is very
138		 * gross.
139		 */
140		int longaddr = 0;
141		longaddr = strlen(x);
142		if (memcmp(x,"0x",2) == 0) longaddr -= 2;
143		longaddr = (longaddr > 8) ? 1 : 0;
144
145		if (longaddr) newaddr = (hsaddr_t) xtoq(x);
146		else newaddr = (hsaddr_t) xtoi(x);
147		}
148	    *addr = newaddr & ~(wlen - 1);	/* align to natural boundary */
149	    }
150	}
151
152    if (length) {
153	x = cmd_getarg(cmd,1);
154	if (x) {
155	    newlen = (long) xtoi(x);
156	    *length = newlen;
157	    }
158	}
159
160    return 0;
161
162}
163
164static int stuffmem(hsaddr_t addr,int wlen,char *tail)
165{
166    char *tok;
167    int count = 0;
168    uint8_t b;
169    uint16_t h;
170    uint32_t w;
171    uint64_t q;
172    int res = 0;
173
174    addr &= ~(wlen-1);
175
176    while ((tok = gettoken(&tail))) {
177	switch (wlen) {
178	    default:
179	    case 1:
180	      b = (uint8_t) xtoq(tok);
181	      if ((res = mem_poke(addr, b, MEM_BYTE))) {
182		  /*Did not edit*/
183		  return res;
184		}
185	      break;
186	    case 2:
187	      h = (uint16_t) xtoq(tok);
188	      if ((res = mem_poke(addr, h, MEM_HALFWORD))) {
189		  /*Did not edit*/
190		  return res;
191		}
192		break;
193	    case 4:
194	      w = (uint32_t) xtoq(tok);
195	      if ((res = mem_poke(addr, w, MEM_WORD))) {
196		  /*Did not edit*/
197		  return res;
198		}
199		break;
200	    case 8:
201	      q = (uint64_t) xtoq(tok);
202	      if ((res = mem_poke(addr, q, MEM_QUADWORD))) {
203		  /*Did not edit*/
204		  return res;
205		}
206		break;
207	    }
208
209	addr += wlen;
210	count++;
211	}
212
213    return count;
214}
215
216int dumpmem(hsaddr_t addr,hsaddr_t dispaddr,int length,int wlen)
217{
218    int idx,x;
219    uint8_t b;
220    uint16_t h;
221    uint32_t w;
222    uint64_t q;
223    int res = 0;
224
225    /*
226     * The reason we save the line in this union is to provide the
227     * property that the dump command will only touch the
228     * memory once.  This might be useful when looking at
229     * device registers.
230     */
231
232    union {
233	uint8_t bytes[16];
234	uint16_t halves[8];
235	uint32_t words[4];
236	uint64_t quads[2];
237    } line;
238
239    addr &= ~(wlen-1);
240
241    for (idx = 0; idx < length; idx += 16) {
242	xprintf(PTRFMT "%c ",dispaddr+idx,(dispaddr != addr) ? '%' : ':');
243	switch (wlen) {
244	    default:
245	    case 1:
246		for (x = 0; x < 16; x++) {
247		    if (idx+x < length) {
248			if ((res = mem_peek(&b, (addr+idx+x), MEM_BYTE))) {
249			    return res;
250			    }
251			line.bytes[x] = b;
252			xprintf("%02X ",b);
253			}
254		    else {
255			xprintf("   ");
256			}
257		    }
258		break;
259	    case 2:
260		for (x = 0; x < 16; x+=2) {
261		    if (idx+x < length) {
262			if ((res = mem_peek(&h, (addr+idx+x), MEM_HALFWORD))) {
263			    return res;
264			    }
265			line.halves[x/2] = h;
266			xprintf("%04X ",h);
267			}
268		    else {
269			xprintf("     ");
270			}
271		    }
272		break;
273	    case 4:
274		for (x = 0; x < 16; x+=4) {
275		    if (idx+x < length) {
276
277			if ((res = mem_peek(&w , (addr+idx+x), MEM_WORD))) {
278			    return res;
279			    }
280			line.words[x/4] = w;
281			xprintf("%08X ",w);
282			}
283		    else {
284			xprintf("         ");
285			}
286		    }
287		break;
288	    case 8:
289		for (x = 0; x < 16; x+=8) {
290		    if (idx+x < length) {
291			if ((res = mem_peek(&q, (addr+idx+x), MEM_QUADWORD))) {
292			    return res;
293			    }
294			line.quads[x/8] = q;
295			xprintf("%016llX ",q);
296			}
297		    else {
298			xprintf("                 ");
299			}
300		    }
301		break;
302	    }
303
304	xprintf(" ");
305	for (x = 0; x < 16; x++) {
306	    if (idx+x < length) {
307		b = line.bytes[x];
308		if ((b < 32) || (b > 127)) xprintf(".");
309		else xprintf("%c",b);
310		}
311	    else {
312		xprintf(" ");
313		}
314	    }
315	xprintf("\n");
316	}
317
318    return 0;
319}
320
321static int ui_cmd_memedit(ui_cmdline_t *cmd,int argc,char *argv[])
322{
323    uint8_t b;
324    uint16_t h;
325    uint32_t w;
326    uint64_t q;
327
328    hsaddr_t addr;
329    char *vtext;
330    int wlen;
331    int count;
332    int idx = 1;
333    int stuffed = 0;
334    int res = 0;
335
336    getaddrargs(cmd,&prev_wtype,&prev_addr,NULL);
337
338    wlen = prev_wtype & ATYPE_SIZE_MASK;
339
340    vtext = cmd_getarg(cmd,idx++);
341
342    addr = prev_addr;
343
344    while (vtext) {
345	count = stuffmem(addr,wlen,vtext);
346	if (count < 0) {
347	    ui_showerror(count,"Could not modify memory");
348	    return count;			/* error */
349	    }
350	addr += count*wlen;
351	prev_addr += count*wlen;
352	stuffed += count;
353	vtext = cmd_getarg(cmd,idx++);
354	}
355
356    if (stuffed == 0) {
357	char line[256];
358	char prompt[32];
359
360	xprintf("Type '.' to exit, '-' to back up, '=' to dump memory.\n");
361	for (;;) {
362
363	    addr = prev_addr;
364	    if ((prev_wtype & ATYPE_TYPE_MASK) == ATYPE_TYPE_PHYS) {
365		addr = UNCADDR(addr);
366		}
367
368	    xprintf(PTRFMT "%c ",prev_addr,(addr != prev_addr) ? '%' : ':');
369
370	    switch (wlen) {
371		default:
372		case 1:
373		    if ((res = mem_peek(&b, addr, MEM_BYTE))) {
374			return res;
375			}
376		    xsprintf(prompt,"[%02X]: ", b);
377		    break;
378		case 2:
379		    if ((res = mem_peek(&h, addr, MEM_HALFWORD))) {
380			return res;
381			}
382		    xsprintf(prompt,"[%04X]: ",h);
383		    break;
384		case 4:
385		    if ((res = mem_peek(&w, addr, MEM_WORD))) {
386			return res;
387			}
388		    xsprintf(prompt,"[%08X]: ",w);
389		    break;
390		case 8:
391		    if ((res = mem_peek(&q, addr, MEM_QUADWORD))) {
392			return res;
393			}
394		    xsprintf(prompt,"[%016llX]: ",q);
395		    break;
396		}
397
398	    console_readline(prompt,line,sizeof(line));
399	    if (line[0] == '-') {
400		prev_addr -= wlen;
401		continue;
402		}
403	    if (line[0] == '=') {
404		dumpmem(prev_addr,prev_addr,16,wlen);
405		continue;
406		}
407	    if (line[0] == '.') {
408		break;
409		}
410	    if (line[0] == '\0') {
411		prev_addr += wlen;
412		continue;
413		}
414	    count = stuffmem(addr,wlen,line);
415	    if (count < 0) return count;
416	    if (count == 0) break;
417	    prev_addr += count*wlen;
418	    }
419	}
420
421    return 0;
422}
423
424static int ui_cmd_memfill(ui_cmdline_t *cmd,int argc,char *argv[])
425{
426    hsaddr_t addr;
427    char *atext;
428    int wlen;
429    int idx = 2;
430    int len;
431    uint64_t pattern;
432    int res;
433
434    getaddrargs(cmd,&prev_wtype,&prev_addr,&len);
435
436    atext = cmd_getarg(cmd,idx++);
437    if (!atext) return ui_showusage(cmd);
438    pattern = xtoq(atext);
439
440    addr = prev_addr;
441
442    if ((prev_wtype & ATYPE_TYPE_MASK) == ATYPE_TYPE_PHYS) {
443	addr = UNCADDR(addr);
444	}
445
446    wlen = prev_wtype & ATYPE_SIZE_MASK;
447
448    switch (wlen) {
449	case 1:
450	    while (len > 0) {
451	        if ((res = mem_poke(addr,  pattern, MEM_BYTE))) {
452		    /*Did not edit*/
453		    return 0;
454		    }
455		addr++;
456		len--;
457		}
458	    break;
459	case 2:
460	    while (len > 0) {
461		if ((res = mem_poke(addr,  pattern, MEM_HALFWORD))) {
462		    return 0;
463		    }
464		addr += 2;
465		len--;
466		}
467	    break;
468	case 4:
469	    while (len > 0) {
470	        if ((res = mem_poke(addr,  pattern, MEM_WORD))) {
471		    return -1;
472		    }
473		addr += 4;
474		len--;
475		}
476	    break;
477	case 8:
478	    while (len > 0) {
479		if ((res = mem_poke(addr,  pattern, MEM_QUADWORD))) {
480		    return 0;
481		    }
482		addr += 8;
483		len--;
484		}
485	    break;
486	}
487
488    return 0;
489}
490
491
492#if 1
493#define FILL(ptr,len,pattern) printf("Pattern: %016llX\n",pattern); \
494             for (idx = 0; idx < len; idx+=wlen) hs_write64(ptr+idx,pattern)
495#define CHECK(ptr,len,pattern) for (idx = 0; idx < len; idx+=wlen) { \
496             if (hs_read64(ptr+idx)!=pattern) {printf("Mismatch at %016llX: Expected %016llX got %016llX", \
497					   ptr+idx,pattern,hs_read64(ptr+idx)); \
498                                           error = !nostop; loopmode = nostop;break;} \
499             }
500
501#define MEMTEST(ptr,len,pattern) if (!error) { FILL(ptr,len,pattern) ; CHECK(ptr,len,pattern); }
502
503static int ui_cmd_memtest(ui_cmdline_t *cmd,int argc,char *argv[])
504{
505    hsaddr_t addr = 0;
506    int len = 0;
507    int wtype = 0;
508    hsaddr_t wlen;
509    int idx = 0;
510    int error = 0;
511    int loopmode = 0;
512    int pass =0;
513    int nostop;
514
515    nostop = cmd_sw_isset(cmd,"-nostop");
516
517    getaddrargs(cmd,&wtype,&addr,&len);
518
519    wlen = 8;
520    addr &= ~(wlen-1);
521
522    if ((prev_wtype & ATYPE_TYPE_MASK) == ATYPE_TYPE_PHYS) {
523	addr = UNCADDR(addr);
524	}
525
526    if (cmd_sw_isset(cmd,"-loop")) {
527	loopmode = 1;
528	}
529
530    pass = 0;
531    for (;;) {
532	if (loopmode) {
533	    printf("Pass %d\n",pass);
534	    if (console_status()) break;
535	    }
536	MEMTEST(addr,len,(idx*8));
537	MEMTEST(addr,len,	0);
538	MEMTEST(addr,len,0xFFFFFFFFFFFFFFFFULL);
539	MEMTEST(addr,len,0x5555555555555555ULL);
540	MEMTEST(addr,len,0xAAAAAAAAAAAAAAAAULL);
541	MEMTEST(addr,len,0xFF00FF00FF00FF00ULL);
542	MEMTEST(addr,len,0x00FF00FF00FF00FFULL);
543	if (!loopmode) break;
544	pass++;
545	}
546
547    return 0;
548}
549#endif
550
551static int ui_cmd_memdump(ui_cmdline_t *cmd,int argc,char *argv[])
552{
553    hsaddr_t addr;
554    int res;
555
556    getaddrargs(cmd,&prev_wtype,&prev_addr,&prev_length);
557
558    addr = prev_addr;
559    if ((prev_wtype & ATYPE_TYPE_MASK) == ATYPE_TYPE_PHYS) {
560	addr = UNCADDR(addr);
561	}
562
563    res = dumpmem(addr,
564	    prev_addr,
565	    prev_length,
566	    prev_wtype & ATYPE_SIZE_MASK);
567
568    if (res < 0) {
569	ui_showerror(res,"Could not display memory");
570	}
571    else {
572	prev_addr += prev_length;
573	}
574
575    return res;
576}
577
578static int ui_cmd_disasm(ui_cmdline_t *cmd,int argc,char *argv[])
579{
580    hsaddr_t addr;
581    char buf[512];
582    int idx;
583    uint32_t inst;
584    int res;
585
586    getaddrargs(cmd,&prev_wtype,&prev_addr,&prev_dlength);
587
588    prev_addr &= ~3;
589
590    addr = prev_addr;
591    if ((prev_wtype & ATYPE_TYPE_MASK) == ATYPE_TYPE_PHYS) {
592	addr = UNCADDR(addr);
593	}
594
595    for (idx = 0; idx < prev_dlength; idx++) {
596	if ((res = mem_peek(&inst, addr, MEM_WORD))) {
597	    ui_showerror(res,"Could not disassemble memory");
598	    return res;
599	    }
600	disasm_inst(buf,sizeof(buf),inst,(uint64_t) prev_addr);
601	xprintf("%P%c %08x    %s\n",prev_addr,(addr != prev_addr) ? '%' : ':',inst,buf);
602	addr += 4;
603	prev_addr += 4;
604	}
605
606    return 0;
607}
608
609int ui_init_examcmds(void)
610{
611    cmd_addcmd("u",
612	       ui_cmd_disasm,
613	       NULL,
614	       "Disassemble instructions.",
615	       "u [addr [length]]\n\n"
616	       "This command disassembles instructions at the specified address.\n"
617	       "CFE will display standard register names and symbolic names for\n"
618	       "certain CP0 registers.  The 'u' command remembers the last address\n"
619	       "that was disassembled so you can enter 'u' again with no parameters\n"
620	       "to continue a previous request.\n",
621               "-p;Address is an uncached physical address|"
622	       "-v;Address is a kernel virtual address");
623
624
625    cmd_addcmd("d",
626	       ui_cmd_memdump,
627	       NULL,
628	       "Dump memory.",
629	       "d [-b|-h|-w|-q] [addr [length]]\n\n"
630	       "This command displays data from memory as bytes, halfwords, words,\n"
631	       "or quadwords.  ASCII text, if present, will appear to the right of\n"
632	       "the hex data.  The dump command remembers the previous word size,\n"
633	       "dump length and last displayed address, so you can enter 'd' again\n"
634	       "to continue a previous dump request.",
635	       "-b;Dump memory as bytes|"
636	       "-h;Dump memory as halfwords (16-bits)|"
637               "-w;Dump memory as words (32-bits)|"
638               "-q;Dump memory as quadwords (64-bits)|"
639               "-p;Address is an uncached physical address|"
640	       "-v;Address is a kernel virtual address");
641
642
643    cmd_addcmd("e",
644	       ui_cmd_memedit,
645	       NULL,
646	       "Modify contents of memory.",
647	       "e [-b|-h|-w|-q] [addr [data...]]\n\n"
648	       "This command modifies the contents of memory.  If you do not specify\n"
649	       "data on the command line, CFE will prompt for it.  When prompting for\n"
650	       "data you may enter '-' to back up, '=' to dump memory at the current\n"
651	       "location, or '.' to exit edit mode.",
652	       "-b;Edit memory as bytes|"
653	       "-h;Edit memory as halfwords (16-bits)|"
654               "-w;Edit memory as words (32-bits)|"
655               "-q;Edit memory as quadwords (64-bits)|"
656	       "-p;Address is an uncached physical address|"
657	       "-v;Address is a kernel virtual address");
658
659    cmd_addcmd("f",
660	       ui_cmd_memfill,
661	       NULL,
662	       "Fill contents of memory.",
663	       "f [-b|-h|-w|-q] addr length pattern\n\n"
664	       "This command modifies the contents of memory.  You can specify the\n"
665	       "starting address, length, and pattern of data to fill (in hex)\n",
666	       "-b;Edit memory as bytes|"
667	       "-h;Edit memory as halfwords (16-bits)|"
668               "-w;Edit memory as words (32-bits)|"
669               "-q;Edit memory as quadwords (64-bits)|"
670	       "-p;Address is an uncached physical address|"
671	       "-v;Address is a kernel virtual address");
672
673#if 1
674    cmd_addcmd("memtest",
675	       ui_cmd_memtest,
676	       NULL,
677	       "Test memory.",
678	       "memtest [options] addr length\n\n"
679	       "This command tests memory.  It is a very crude test, so don't\n"
680	       "rely on it for anything really important.  Addr and length are in hex\n",
681	       /*  "-p;Address is an uncached physical address|" */
682	       "-v;Address is a kernel virtual address|"
683	       "-nostop;Don't stop on error|"
684	       "-loop;Loop till keypress");
685#endif
686
687
688    prev_addr = PTR2HSADDR(KERNADDR(0));
689
690    return 0;
691}
692
693
694
695
696
697
698
699
700
701
702
703