1/* Memory attributes support, for GDB.
2
3   Copyright 2001, 2002 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.  */
21
22#include "defs.h"
23#include "command.h"
24#include "gdbcmd.h"
25#include "memattr.h"
26#include "target.h"
27#include "value.h"
28#include "language.h"
29#include "gdb_string.h"
30
31const struct mem_attrib default_mem_attrib =
32{
33  MEM_RW,			/* mode */
34  MEM_WIDTH_UNSPECIFIED,
35  0,				/* hwbreak */
36  0,				/* cache */
37  0				/* verify */
38};
39
40static struct mem_region *mem_region_chain = NULL;
41static int mem_number = 0;
42
43static struct mem_region *
44create_mem_region (CORE_ADDR lo, CORE_ADDR hi,
45		   const struct mem_attrib *attrib)
46{
47  struct mem_region *n, *new;
48
49  /* lo == hi is a useless empty region */
50  if (lo >= hi && hi != 0)
51    {
52      printf_unfiltered ("invalid memory region: low >= high\n");
53      return NULL;
54    }
55
56  n = mem_region_chain;
57  while (n)
58    {
59      /* overlapping node */
60      if ((lo >= n->lo && (lo < n->hi || n->hi == 0))
61	  || (hi > n->lo && (hi <= n->hi || n->hi == 0))
62	  || (lo <= n->lo && (hi >= n->hi || hi == 0)))
63	{
64	  printf_unfiltered ("overlapping memory region\n");
65	  return NULL;
66	}
67      n = n->next;
68    }
69
70  new = xmalloc (sizeof (struct mem_region));
71  new->lo = lo;
72  new->hi = hi;
73  new->number = ++mem_number;
74  new->enabled_p = 1;
75  new->attrib = *attrib;
76
77  /* link in new node */
78  new->next = mem_region_chain;
79  mem_region_chain = new;
80
81  return new;
82}
83
84static void
85delete_mem_region (struct mem_region *m)
86{
87  xfree (m);
88}
89
90/*
91 * Look up the memory region cooresponding to ADDR.
92 */
93struct mem_region *
94lookup_mem_region (CORE_ADDR addr)
95{
96  static struct mem_region region;
97  struct mem_region *m;
98  CORE_ADDR lo;
99  CORE_ADDR hi;
100
101  /* First we initialize LO and HI so that they describe the entire
102     memory space.  As we process the memory region chain, they are
103     redefined to describe the minimal region containing ADDR.  LO
104     and HI are used in the case where no memory region is defined
105     that contains ADDR.  If a memory region is disabled, it is
106     treated as if it does not exist.  */
107
108  lo = (CORE_ADDR) 0;
109  hi = (CORE_ADDR) ~ 0;
110
111  for (m = mem_region_chain; m; m = m->next)
112    {
113      if (m->enabled_p == 1)
114	{
115	  if (addr >= m->lo && (addr < m->hi || m->hi == 0))
116	    return m;
117
118	  if (addr >= m->hi && lo < m->hi)
119	    lo = m->hi;
120
121	  if (addr <= m->lo && hi > m->lo)
122	    hi = m->lo;
123	}
124    }
125
126  /* Because no region was found, we must cons up one based on what
127     was learned above.  */
128  region.lo = lo;
129  region.hi = hi;
130  region.attrib = default_mem_attrib;
131  return &region;
132}
133
134
135static void
136mem_command (char *args, int from_tty)
137{
138  CORE_ADDR lo, hi;
139  char *tok;
140  struct mem_attrib attrib;
141
142  if (!args)
143    error_no_arg ("No mem");
144
145  tok = strtok (args, " \t");
146  if (!tok)
147    error ("no lo address");
148  lo = parse_and_eval_address (tok);
149
150  tok = strtok (NULL, " \t");
151  if (!tok)
152    error ("no hi address");
153  hi = parse_and_eval_address (tok);
154
155  attrib = default_mem_attrib;
156  while ((tok = strtok (NULL, " \t")) != NULL)
157    {
158      if (strcmp (tok, "rw") == 0)
159	attrib.mode = MEM_RW;
160      else if (strcmp (tok, "ro") == 0)
161	attrib.mode = MEM_RO;
162      else if (strcmp (tok, "wo") == 0)
163	attrib.mode = MEM_WO;
164
165      else if (strcmp (tok, "8") == 0)
166	attrib.width = MEM_WIDTH_8;
167      else if (strcmp (tok, "16") == 0)
168	{
169	  if ((lo % 2 != 0) || (hi % 2 != 0))
170	    error ("region bounds not 16 bit aligned");
171	  attrib.width = MEM_WIDTH_16;
172	}
173      else if (strcmp (tok, "32") == 0)
174	{
175	  if ((lo % 4 != 0) || (hi % 4 != 0))
176	    error ("region bounds not 32 bit aligned");
177	  attrib.width = MEM_WIDTH_32;
178	}
179      else if (strcmp (tok, "64") == 0)
180	{
181	  if ((lo % 8 != 0) || (hi % 8 != 0))
182	    error ("region bounds not 64 bit aligned");
183	  attrib.width = MEM_WIDTH_64;
184	}
185
186#if 0
187      else if (strcmp (tok, "hwbreak") == 0)
188	attrib.hwbreak = 1;
189      else if (strcmp (tok, "swbreak") == 0)
190	attrib.hwbreak = 0;
191#endif
192
193      else if (strcmp (tok, "cache") == 0)
194	attrib.cache = 1;
195      else if (strcmp (tok, "nocache") == 0)
196	attrib.cache = 0;
197
198#if 0
199      else if (strcmp (tok, "verify") == 0)
200	attrib.verify = 1;
201      else if (strcmp (tok, "noverify") == 0)
202	attrib.verify = 0;
203#endif
204
205      else
206	error ("unknown attribute: %s", tok);
207    }
208
209  create_mem_region (lo, hi, &attrib);
210}
211
212
213static void
214mem_info_command (char *args, int from_tty)
215{
216  struct mem_region *m;
217  struct mem_attrib *attrib;
218
219  if (!mem_region_chain)
220    {
221      printf_unfiltered ("There are no memory regions defined.\n");
222      return;
223    }
224
225  printf_filtered ("Num ");
226  printf_filtered ("Enb ");
227  printf_filtered ("Low Addr   ");
228  if (TARGET_ADDR_BIT > 32)
229    printf_filtered ("        ");
230  printf_filtered ("High Addr  ");
231  if (TARGET_ADDR_BIT > 32)
232    printf_filtered ("        ");
233  printf_filtered ("Attrs ");
234  printf_filtered ("\n");
235
236  for (m = mem_region_chain; m; m = m->next)
237    {
238      char *tmp;
239      printf_filtered ("%-3d %-3c\t",
240		       m->number,
241		       m->enabled_p ? 'y' : 'n');
242      if (TARGET_ADDR_BIT <= 32)
243	tmp = hex_string_custom ((unsigned long) m->lo, 8);
244      else
245	tmp = hex_string_custom ((unsigned long) m->lo, 16);
246
247      printf_filtered ("%s ", tmp);
248
249      if (TARGET_ADDR_BIT <= 32)
250	{
251	if (m->hi == 0)
252	  tmp = "0x100000000";
253	else
254	  tmp = hex_string_custom ((unsigned long) m->hi, 8);
255	}
256      else
257	{
258	if (m->hi == 0)
259	  tmp = "0x10000000000000000";
260	else
261	  tmp = hex_string_custom ((unsigned long) m->hi, 16);
262	}
263
264      printf_filtered ("%s ", tmp);
265
266      /* Print a token for each attribute.
267
268       * FIXME: Should we output a comma after each token?  It may
269       * make it easier for users to read, but we'd lose the ability
270       * to cut-and-paste the list of attributes when defining a new
271       * region.  Perhaps that is not important.
272       *
273       * FIXME: If more attributes are added to GDB, the output may
274       * become cluttered and difficult for users to read.  At that
275       * time, we may want to consider printing tokens only if they
276       * are different from the default attribute.  */
277
278      attrib = &m->attrib;
279      switch (attrib->mode)
280	{
281	case MEM_RW:
282	  printf_filtered ("rw ");
283	  break;
284	case MEM_RO:
285	  printf_filtered ("ro ");
286	  break;
287	case MEM_WO:
288	  printf_filtered ("wo ");
289	  break;
290	}
291
292      switch (attrib->width)
293	{
294	case MEM_WIDTH_8:
295	  printf_filtered ("8 ");
296	  break;
297	case MEM_WIDTH_16:
298	  printf_filtered ("16 ");
299	  break;
300	case MEM_WIDTH_32:
301	  printf_filtered ("32 ");
302	  break;
303	case MEM_WIDTH_64:
304	  printf_filtered ("64 ");
305	  break;
306	case MEM_WIDTH_UNSPECIFIED:
307	  break;
308	}
309
310#if 0
311      if (attrib->hwbreak)
312	printf_filtered ("hwbreak");
313      else
314	printf_filtered ("swbreak");
315#endif
316
317      if (attrib->cache)
318	printf_filtered ("cache ");
319      else
320	printf_filtered ("nocache ");
321
322#if 0
323      if (attrib->verify)
324	printf_filtered ("verify ");
325      else
326	printf_filtered ("noverify ");
327#endif
328
329      printf_filtered ("\n");
330
331      gdb_flush (gdb_stdout);
332    }
333}
334
335
336/* Enable the memory region number NUM. */
337
338static void
339mem_enable (int num)
340{
341  struct mem_region *m;
342
343  for (m = mem_region_chain; m; m = m->next)
344    if (m->number == num)
345      {
346	m->enabled_p = 1;
347	return;
348      }
349  printf_unfiltered ("No memory region number %d.\n", num);
350}
351
352static void
353mem_enable_command (char *args, int from_tty)
354{
355  char *p = args;
356  char *p1;
357  int num;
358  struct mem_region *m;
359
360  dcache_invalidate (target_dcache);
361
362  if (p == 0)
363    {
364      for (m = mem_region_chain; m; m = m->next)
365	m->enabled_p = 1;
366    }
367  else
368    while (*p)
369      {
370	p1 = p;
371	while (*p1 >= '0' && *p1 <= '9')
372	  p1++;
373	if (*p1 && *p1 != ' ' && *p1 != '\t')
374	  error ("Arguments must be memory region numbers.");
375
376	num = atoi (p);
377	mem_enable (num);
378
379	p = p1;
380	while (*p == ' ' || *p == '\t')
381	  p++;
382      }
383}
384
385
386/* Disable the memory region number NUM. */
387
388static void
389mem_disable (int num)
390{
391  struct mem_region *m;
392
393  for (m = mem_region_chain; m; m = m->next)
394    if (m->number == num)
395      {
396	m->enabled_p = 0;
397	return;
398      }
399  printf_unfiltered ("No memory region number %d.\n", num);
400}
401
402static void
403mem_disable_command (char *args, int from_tty)
404{
405  char *p = args;
406  char *p1;
407  int num;
408  struct mem_region *m;
409
410  dcache_invalidate (target_dcache);
411
412  if (p == 0)
413    {
414      for (m = mem_region_chain; m; m = m->next)
415	m->enabled_p = 0;
416    }
417  else
418    while (*p)
419      {
420	p1 = p;
421	while (*p1 >= '0' && *p1 <= '9')
422	  p1++;
423	if (*p1 && *p1 != ' ' && *p1 != '\t')
424	  error ("Arguments must be memory region numbers.");
425
426	num = atoi (p);
427	mem_disable (num);
428
429	p = p1;
430	while (*p == ' ' || *p == '\t')
431	  p++;
432      }
433}
434
435/* Clear memory region list */
436
437static void
438mem_clear (void)
439{
440  struct mem_region *m;
441
442  while ((m = mem_region_chain) != 0)
443    {
444      mem_region_chain = m->next;
445      delete_mem_region (m);
446    }
447}
448
449/* Delete the memory region number NUM. */
450
451static void
452mem_delete (int num)
453{
454  struct mem_region *m1, *m;
455
456  if (!mem_region_chain)
457    {
458      printf_unfiltered ("No memory region number %d.\n", num);
459      return;
460    }
461
462  if (mem_region_chain->number == num)
463    {
464      m1 = mem_region_chain;
465      mem_region_chain = m1->next;
466      delete_mem_region (m1);
467    }
468  else
469    for (m = mem_region_chain; m->next; m = m->next)
470      {
471	if (m->next->number == num)
472	  {
473	    m1 = m->next;
474	    m->next = m1->next;
475	    delete_mem_region (m1);
476	    break;
477	  }
478      }
479}
480
481static void
482mem_delete_command (char *args, int from_tty)
483{
484  char *p = args;
485  char *p1;
486  int num;
487
488  dcache_invalidate (target_dcache);
489
490  if (p == 0)
491    {
492      if (query ("Delete all memory regions? "))
493	mem_clear ();
494      dont_repeat ();
495      return;
496    }
497
498  while (*p)
499    {
500      p1 = p;
501      while (*p1 >= '0' && *p1 <= '9')
502	p1++;
503      if (*p1 && *p1 != ' ' && *p1 != '\t')
504	error ("Arguments must be memory region numbers.");
505
506      num = atoi (p);
507      mem_delete (num);
508
509      p = p1;
510      while (*p == ' ' || *p == '\t')
511	p++;
512    }
513
514  dont_repeat ();
515}
516
517extern initialize_file_ftype _initialize_mem; /* -Wmissing-prototype */
518
519void
520_initialize_mem (void)
521{
522  add_com ("mem", class_vars, mem_command,
523	   "Define attributes for memory region.\n\
524Usage: mem <lo addr> <hi addr> [<mode> <width> <cache>], \n\
525where <mode>  may be rw (read/write), ro (read-only) or wo (write-only), \n\
526      <width> may be 8, 16, 32, or 64, and \n\
527      <cache> may be cache or nocache");
528
529  add_cmd ("mem", class_vars, mem_enable_command,
530	   "Enable memory region.\n\
531Arguments are the code numbers of the memory regions to enable.\n\
532Usage: enable mem <code number>\n\
533Do \"info mem\" to see current list of code numbers.", &enablelist);
534
535  add_cmd ("mem", class_vars, mem_disable_command,
536	   "Disable memory region.\n\
537Arguments are the code numbers of the memory regions to disable.\n\
538Usage: disable mem <code number>\n\
539Do \"info mem\" to see current list of code numbers.", &disablelist);
540
541  add_cmd ("mem", class_vars, mem_delete_command,
542	   "Delete memory region.\n\
543Arguments are the code numbers of the memory regions to delete.\n\
544Usage: delete mem <code number>\n\
545Do \"info mem\" to see current list of code numbers.", &deletelist);
546
547  add_info ("mem", mem_info_command,
548	    "Memory region attributes");
549}
550