mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
239 lines
6.4 KiB
C
239 lines
6.4 KiB
C
/* Caching code. Typically used by remote back ends for
|
|
caching remote memory.
|
|
|
|
Copyright 1992, 1993 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#include "defs.h"
|
|
#include "dcache.h"
|
|
#include "gdbcmd.h"
|
|
|
|
int remote_dcache = 0;
|
|
|
|
/* In case the system header files define a prototype for insque and
|
|
remque that uses a pointer to a struct qelem, silence the warnings */
|
|
#define Insque(a,b) insque((PTR)(a), (PTR)(b))
|
|
#define Remque(a) remque((PTR)(a))
|
|
|
|
/* The data cache records all the data read from the remote machine
|
|
since the last time it stopped.
|
|
|
|
Each cache block holds LINE_SIZE bytes of data
|
|
starting at a multiple-of-LINE_SIZE address. */
|
|
|
|
#define LINE_SIZE_MASK ((LINE_SIZE - 1)) /* eg 7*2+1= 111*/
|
|
#define XFORM(x) (((x) & LINE_SIZE_MASK) >> 2)
|
|
|
|
/* Free all the data cache blocks, thus discarding all cached data. */
|
|
void
|
|
dcache_flush (dcache)
|
|
DCACHE *dcache;
|
|
{
|
|
register struct dcache_block *db;
|
|
|
|
if (remote_dcache > 0)
|
|
while ((db = dcache->dcache_valid.next) != &dcache->dcache_valid)
|
|
{
|
|
Remque (db);
|
|
Insque (db, &dcache->dcache_free);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If addr is present in the dcache, return the address of the block
|
|
* containing it.
|
|
*/
|
|
static
|
|
struct dcache_block *
|
|
dcache_hit (dcache, addr)
|
|
DCACHE *dcache;
|
|
unsigned int addr;
|
|
{
|
|
register struct dcache_block *db;
|
|
|
|
if (addr & 3
|
|
|| remote_dcache == 0)
|
|
abort ();
|
|
|
|
/* Search all cache blocks for one that is at this address. */
|
|
db = dcache->dcache_valid.next;
|
|
while (db != &dcache->dcache_valid)
|
|
{
|
|
if ((addr & ~LINE_SIZE_MASK) == db->addr)
|
|
return db;
|
|
db = db->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the int data at address ADDR in dcache block DC. */
|
|
static
|
|
int
|
|
dcache_value (db, addr)
|
|
struct dcache_block *db;
|
|
unsigned int addr;
|
|
{
|
|
if (addr & 3
|
|
|| remote_dcache == 0)
|
|
abort ();
|
|
return (db->data[XFORM (addr)]);
|
|
}
|
|
|
|
/* Get a free cache block, put or keep it on the valid list,
|
|
and return its address. The caller should store into the block
|
|
the address and data that it describes, then remque it from the
|
|
free list and insert it into the valid list. This procedure
|
|
prevents errors from creeping in if a memory retrieval is
|
|
interrupted (which used to put garbage blocks in the valid
|
|
list...). */
|
|
static
|
|
struct dcache_block *
|
|
dcache_alloc (dcache)
|
|
DCACHE *dcache;
|
|
{
|
|
register struct dcache_block *db;
|
|
|
|
if (remote_dcache == 0)
|
|
abort();
|
|
|
|
if ((db = dcache->dcache_free.next) == &dcache->dcache_free)
|
|
{
|
|
/* If we can't get one from the free list, take last valid and put
|
|
it on the free list. */
|
|
db = dcache->dcache_valid.last;
|
|
Remque (db);
|
|
Insque (db, &dcache->dcache_free);
|
|
}
|
|
|
|
Remque (db);
|
|
Insque (db, &dcache->dcache_valid);
|
|
return (db);
|
|
}
|
|
|
|
/* Using the data cache DCACHE return the contents of the word at
|
|
address ADDR in the remote machine. */
|
|
int
|
|
dcache_fetch (dcache, addr)
|
|
DCACHE *dcache;
|
|
CORE_ADDR addr;
|
|
{
|
|
register struct dcache_block *db;
|
|
|
|
if (remote_dcache == 0)
|
|
{
|
|
int i;
|
|
|
|
(*dcache->read_memory) (addr, (unsigned char *) &i, 4);
|
|
return(i);
|
|
}
|
|
|
|
db = dcache_hit (dcache, addr);
|
|
if (db == 0)
|
|
{
|
|
db = dcache_alloc (dcache);
|
|
immediate_quit++;
|
|
(*dcache->read_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
|
|
immediate_quit--;
|
|
db->addr = addr & ~LINE_SIZE_MASK;
|
|
Remque (db); /* Off the free list */
|
|
Insque (db, &dcache->dcache_valid); /* On the valid list */
|
|
}
|
|
return (dcache_value (db, addr));
|
|
}
|
|
|
|
/* Write the word at ADDR both in the data cache and in the remote machine. */
|
|
void
|
|
dcache_poke (dcache, addr, data)
|
|
DCACHE *dcache;
|
|
CORE_ADDR addr;
|
|
int data;
|
|
{
|
|
register struct dcache_block *db;
|
|
|
|
if (remote_dcache == 0)
|
|
{
|
|
(*dcache->write_memory) (addr, (unsigned char *) &data, 4);
|
|
return;
|
|
}
|
|
|
|
/* First make sure the word is IN the cache. DB is its cache block. */
|
|
db = dcache_hit (dcache, addr);
|
|
if (db == 0)
|
|
{
|
|
db = dcache_alloc (dcache);
|
|
immediate_quit++;
|
|
(*dcache->write_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
|
|
immediate_quit--;
|
|
db->addr = addr & ~LINE_SIZE_MASK;
|
|
Remque (db); /* Off the free list */
|
|
Insque (db, &dcache->dcache_valid); /* On the valid list */
|
|
}
|
|
|
|
/* Modify the word in the cache. */
|
|
db->data[XFORM (addr)] = data;
|
|
|
|
/* Send the changed word. */
|
|
immediate_quit++;
|
|
(*dcache->write_memory) (addr, (unsigned char *) &data, 4);
|
|
immediate_quit--;
|
|
}
|
|
|
|
/* Initialize the data cache. */
|
|
DCACHE *
|
|
dcache_init (reading, writing)
|
|
memxferfunc reading;
|
|
memxferfunc writing;
|
|
{
|
|
register i;
|
|
register struct dcache_block *db;
|
|
DCACHE *dcache;
|
|
|
|
dcache = (DCACHE *) xmalloc (sizeof (*dcache));
|
|
dcache->read_memory = reading;
|
|
dcache->write_memory = writing;
|
|
dcache->the_cache = (struct dcache_block *)
|
|
xmalloc (sizeof (*dcache->the_cache) * DCACHE_SIZE);
|
|
|
|
dcache->dcache_free.next = dcache->dcache_free.last = &dcache->dcache_free;
|
|
dcache->dcache_valid.next = dcache->dcache_valid.last = &dcache->dcache_valid;
|
|
for (db = dcache->the_cache, i = 0; i < DCACHE_SIZE; i++, db++)
|
|
Insque (db, &dcache->dcache_free);
|
|
|
|
return(dcache);
|
|
}
|
|
|
|
void
|
|
_initialitize_dcache ()
|
|
{
|
|
add_show_from_set
|
|
(add_set_cmd ("remotecache", class_support, var_boolean,
|
|
(char *) &remote_dcache,
|
|
"\
|
|
Set cache use for remote targets.\n\
|
|
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.",
|
|
&setlist),
|
|
&showlist);
|
|
}
|