#include "defs.h"
#include "dcache.h"
#include "gdbcmd.h"
#include "gdb_string.h"
#include "gdbcore.h"
#include "target.h"
#define DCACHE_SIZE 64
#define LINE_SIZE_POWER (6)
#define LINE_SIZE (1 << LINE_SIZE_POWER)
static int g_line_power = LINE_SIZE_POWER;
static int g_line_size = LINE_SIZE;
#define XFORM(x) ((x) & (g_line_size - 1))
#define MASK(x) ((x) & ~(g_line_size - 1))
#define ENTRY_BAD 0
#define ENTRY_DIRTY 1
#define ENTRY_OK 2
struct dcache_block
{
struct dcache_block *p;
CORE_ADDR addr;
gdb_byte *data;
unsigned char *state;
int anydirty;
int refs;
};
struct dcache_struct
{
struct dcache_block *free_head;
struct dcache_block *free_tail;
struct dcache_block *valid_head;
struct dcache_block *valid_tail;
struct dcache_block *the_cache;
gdb_byte *data_block;
unsigned char *state_block;
};
static struct dcache_block *dcache_hit (DCACHE *dcache, CORE_ADDR addr);
static int dcache_write_line (DCACHE *dcache, struct dcache_block *db);
static int dcache_read_line (DCACHE *dcache, struct dcache_block *db);
static struct dcache_block *dcache_alloc (DCACHE *dcache, CORE_ADDR addr);
static int dcache_writeback (DCACHE *dcache);
static void dcache_info (char *exp, int tty);
void _initialize_dcache (void);
static int dcache_enabled_p = 0;
static void
show_dcache_enabled_p (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("Cache use for remote targets is %s.\n"), value);
}
static DCACHE **g_cache_array = NULL;
static int g_num_caches = 0;
static int g_max_num_caches = 0;
void
dcache_invalidate (DCACHE *dcache)
{
int i;
dcache->valid_head = 0;
dcache->valid_tail = 0;
dcache->free_head = 0;
dcache->free_tail = 0;
for (i = 0; i < DCACHE_SIZE; i++)
{
struct dcache_block *db = dcache->the_cache + i;
if (!dcache->free_head)
dcache->free_head = db;
else
dcache->free_tail->p = db;
dcache->free_tail = db;
db->p = 0;
}
return;
}
static struct dcache_block *
dcache_hit (DCACHE *dcache, CORE_ADDR addr)
{
struct dcache_block *db;
db = dcache->valid_head;
while (db)
{
if (MASK (addr) == db->addr)
{
db->refs++;
return db;
}
db = db->p;
}
return NULL;
}
static int
dcache_write_line (DCACHE *dcache, struct dcache_block *db)
{
CORE_ADDR memaddr;
gdb_byte *myaddr;
int len;
int res;
int reg_len;
struct mem_region *region;
if (!db->anydirty)
return 1;
len = g_line_size;
memaddr = db->addr;
myaddr = db->data;
while (len > 0)
{
int s;
int e;
int dirty_len;
region = lookup_mem_region(memaddr);
if (memaddr + len < region->hi)
reg_len = len;
else
reg_len = region->hi - memaddr;
if (!(region->attrib.cache == 1) || region->attrib.mode == MEM_RO)
{
memaddr += reg_len;
myaddr += reg_len;
len -= reg_len;
continue;
}
while (reg_len > 0)
{
s = XFORM(memaddr);
while (reg_len > 0) {
if (db->state[s] == ENTRY_DIRTY)
break;
s++;
reg_len--;
memaddr++;
myaddr++;
len--;
}
e = s;
while (reg_len > 0) {
if (db->state[e] != ENTRY_DIRTY)
break;
e++;
reg_len--;
}
dirty_len = e - s;
res = target_write (¤t_target, TARGET_OBJECT_RAW_MEMORY,
NULL, myaddr, memaddr, dirty_len);
if (res < dirty_len)
return 0;
memset (&db->state[XFORM(memaddr)], ENTRY_OK, res);
memaddr += res;
myaddr += res;
len -= res;
}
}
db->anydirty = 0;
return 1;
}
static int
dcache_read_line (DCACHE *dcache, struct dcache_block *db)
{
CORE_ADDR memaddr;
gdb_byte *myaddr;
int len;
int res;
int reg_len;
struct mem_region *region;
if (db->anydirty)
{
if (!dcache_write_line (dcache, db))
return 0;
}
len = g_line_size;
memaddr = db->addr;
myaddr = db->data;
while (len > 0)
{
region = lookup_mem_region(memaddr);
if (memaddr + len < region->hi)
reg_len = len;
else
reg_len = region->hi - memaddr;
if (!(region->attrib.cache == 1) || region->attrib.mode == MEM_WO)
{
memaddr += reg_len;
myaddr += reg_len;
len -= reg_len;
continue;
}
res = target_read (¤t_target, TARGET_OBJECT_RAW_MEMORY,
NULL, myaddr, memaddr, reg_len);
if (res < reg_len)
return 0;
memaddr += res;
myaddr += res;
len -= res;
}
memset (db->state, ENTRY_OK, g_line_size * sizeof (unsigned char));
db->anydirty = 0;
return 1;
}
static struct dcache_block *
dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
{
struct dcache_block *db;
db = dcache->free_head;
if (db)
{
dcache->free_head = db->p;
}
else
{
db = dcache->valid_head;
if (!dcache_write_line (dcache, db))
return NULL;
dcache->valid_head = db->p;
}
db->addr = MASK(addr);
db->refs = 0;
db->anydirty = 0;
memset (db->state, ENTRY_BAD, g_line_size * sizeof (unsigned char));
if (!dcache->valid_head)
dcache->valid_head = db;
else
dcache->valid_tail->p = db;
dcache->valid_tail = db;
db->p = 0;
return db;
}
static int
dcache_writeback (DCACHE *dcache)
{
struct dcache_block *db;
db = dcache->valid_head;
while (db)
{
if (!dcache_write_line (dcache, db))
return 0;
db = db->p;
}
return 1;
}
static int
dcache_peek_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
{
struct dcache_block *db = dcache_hit (dcache, addr);
if (!db)
{
db = dcache_alloc (dcache, addr);
if (!db)
return 0;
}
if (db->state[XFORM (addr)] == ENTRY_BAD)
{
if (!dcache_read_line(dcache, db))
return 0;
}
*ptr = db->data[XFORM (addr)];
return 1;
}
static int
dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
{
struct dcache_block *db = dcache_hit (dcache, addr);
if (!db)
{
db = dcache_alloc (dcache, addr);
if (!db)
return 0;
}
db->data[XFORM (addr)] = *ptr;
db->state[XFORM (addr)] = ENTRY_DIRTY;
db->anydirty = 1;
return 1;
}
static void
dcache_set_data (DCACHE *dcache)
{
int i;
dcache->data_block = xmalloc (g_line_size * DCACHE_SIZE * sizeof (gdb_byte));
dcache->state_block = xmalloc (g_line_size * DCACHE_SIZE * sizeof (unsigned char));
for (i = 0; i < DCACHE_SIZE; i++)
{
dcache->the_cache[i].data = dcache->data_block + (i * g_line_size);
dcache->the_cache[i].state = dcache->state_block + (i * g_line_size);
}
}
static void
dcache_resize (DCACHE *dcache)
{
xfree (dcache->data_block);
xfree (dcache->state_block);
dcache_set_data (dcache);
}
DCACHE *
dcache_init (void)
{
int csize = sizeof (struct dcache_block) * DCACHE_SIZE;
DCACHE *dcache;
dcache = (DCACHE *) xmalloc (sizeof (*dcache));
dcache->the_cache = (struct dcache_block *) xmalloc (csize);
memset (dcache->the_cache, 0, csize);
dcache_set_data (dcache);
dcache_invalidate (dcache);
if (g_num_caches == g_max_num_caches)
{
g_max_num_caches += 4;
g_cache_array = xrealloc (g_cache_array, g_max_num_caches * sizeof (DCACHE *));
}
g_cache_array[g_num_caches++] = dcache;
return dcache;
}
void
dcache_free (DCACHE *dcache)
{
int i;
for (i = 0; i < g_num_caches; i++)
{
if (g_cache_array[i] == dcache)
break;
}
g_num_caches--;
if (i < g_num_caches)
{
bcopy (g_cache_array + i + 1, g_cache_array + i, (g_num_caches - i) * sizeof (DCACHE *));
}
xfree (dcache->the_cache);
xfree (dcache->data_block);
xfree (dcache->state_block);
xfree (dcache);
}
int
dcache_xfer_memory (DCACHE *dcache, CORE_ADDR memaddr, gdb_byte *myaddr,
int len, int should_write)
{
int i;
int (*xfunc) (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr);
xfunc = should_write ? dcache_poke_byte : dcache_peek_byte;
for (i = 0; i < len; i++)
{
if (!xfunc (dcache, memaddr + i, myaddr + i))
return 0;
}
if (should_write)
dcache_writeback (dcache);
return len;
}
static void
dcache_info (char *exp, int tty)
{
struct dcache_block *p;
int i;
printf_filtered (_("Dcache line width %d, depth %d\n"),
g_line_size, DCACHE_SIZE);
for (i = 0; i < g_num_caches; i++)
{
printf_filtered (_("Cache state:\n"));
for (p = g_cache_array[i]->valid_head; p; p = p->p)
{
int j;
printf_filtered (_("Line at %s, referenced %d times\n"),
paddr (p->addr), p->refs);
for (j = 0; j < g_line_size; j++)
printf_filtered ("%02x", p->data[j] & 0xFF);
printf_filtered (("\n"));
for (j = 0; j < g_line_size; j++)
printf_filtered ("%2x", p->state[j]);
printf_filtered ("\n");
}
}
}
static void
set_cache_line_power (char *args, int from_tty, struct cmd_list_element *c)
{
int i;
if (g_num_caches == 0)
return;
for (i = 0; i < g_num_caches; i++)
dcache_invalidate (g_cache_array[i]);
g_line_size = 1 << g_line_power;
for (i = 0; i < g_num_caches; i++)
{
dcache_resize (g_cache_array[i]);
dcache_invalidate (g_cache_array[i]);
}
}
void
_initialize_dcache (void)
{
add_setshow_boolean_cmd ("remotecache", class_support,
&dcache_enabled_p, _("\
Set cache use for remote targets."), _("\
Show cache use for remote targets."), _("\
When on, use data caching for remote targets. For many remote targets\n\
this option can offer better throughput for reading target memory.\n\
Unfortunately, gdb does not currently know anything about volatile\n\
registers and thus data caching will produce incorrect results with\n\
volatile registers are in use. By default, this option is off."),
NULL,
show_dcache_enabled_p,
&setlist, &showlist);
add_info ("dcache", dcache_info,
_("Print information on the dcache performance."));
add_setshow_zinteger_cmd ("dcache-linesize-power", class_support,
&g_line_power,
"Set the power for the cache line size",
"Show the power for the cache line size",
"Sets the power for the cache line size, the actual cache line will be 2^cache-line-power.",
set_cache_line_power,
NULL,
&setlist, &showlist);
}