#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)
#define LINE_SIZE_MASK ((LINE_SIZE - 1))
#define XFORM(x) ((x) & LINE_SIZE_MASK)
#define MASK(x) ((x) & ~LINE_SIZE_MASK)
#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[LINE_SIZE];
unsigned char state[LINE_SIZE];
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;
};
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);
}
DCACHE *last_cache;
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 = 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 = 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, sizeof (db->data));
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, sizeof (db->data));
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;
}
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_invalidate (dcache);
last_cache = dcache;
return dcache;
}
void
dcache_free (DCACHE *dcache)
{
if (last_cache == dcache)
last_cache = NULL;
xfree (dcache->the_cache);
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;
printf_filtered (_("Dcache line width %d, depth %d\n"),
LINE_SIZE, DCACHE_SIZE);
if (last_cache)
{
printf_filtered (_("Cache state:\n"));
for (p = last_cache->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 < LINE_SIZE; j++)
printf_filtered ("%02x", p->data[j] & 0xFF);
printf_filtered (("\n"));
for (j = 0; j < LINE_SIZE; j++)
printf_filtered ("%2x", p->state[j]);
printf_filtered ("\n");
}
}
}
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."));
}