simplesim-3.0/memory.c

567 lines
17 KiB
C

/* memory.c - flat memory space routines */
/* SimpleScalar(TM) Tool Suite
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
* All Rights Reserved.
*
* THIS IS A LEGAL DOCUMENT, BY USING SIMPLESCALAR,
* YOU ARE AGREEING TO THESE TERMS AND CONDITIONS.
*
* No portion of this work may be used by any commercial entity, or for any
* commercial purpose, without the prior, written permission of SimpleScalar,
* LLC (info@simplescalar.com). Nonprofit and noncommercial use is permitted
* as described below.
*
* 1. SimpleScalar is provided AS IS, with no warranty of any kind, express
* or implied. The user of the program accepts full responsibility for the
* application of the program and the use of any results.
*
* 2. Nonprofit and noncommercial use is encouraged. SimpleScalar may be
* downloaded, compiled, executed, copied, and modified solely for nonprofit,
* educational, noncommercial research, and noncommercial scholarship
* purposes provided that this notice in its entirety accompanies all copies.
* Copies of the modified software can be delivered to persons who use it
* solely for nonprofit, educational, noncommercial research, and
* noncommercial scholarship purposes provided that this notice in its
* entirety accompanies all copies.
*
* 3. ALL COMMERCIAL USE, AND ALL USE BY FOR PROFIT ENTITIES, IS EXPRESSLY
* PROHIBITED WITHOUT A LICENSE FROM SIMPLESCALAR, LLC (info@simplescalar.com).
*
* 4. No nonprofit user may place any restrictions on the use of this software,
* including as modified by the user, by any other authorized user.
*
* 5. Noncommercial and nonprofit users may distribute copies of SimpleScalar
* in compiled or executable form as set forth in Section 2, provided that
* either: (A) it is accompanied by the corresponding machine-readable source
* code, or (B) it is accompanied by a written offer, with no time limit, to
* give anyone a machine-readable copy of the corresponding source code in
* return for reimbursement of the cost of distribution. This written offer
* must permit verbatim duplication by anyone, or (C) it is distributed by
* someone who received only the executable form, and is accompanied by a
* copy of the written offer of source code.
*
* 6. SimpleScalar was developed by Todd M. Austin, Ph.D. The tool suite is
* currently maintained by SimpleScalar LLC (info@simplescalar.com). US Mail:
* 2395 Timbercrest Court, Ann Arbor, MI 48105.
*
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
*/
#include <stdio.h>
#include <stdlib.h>
#include "host.h"
#include "misc.h"
#include "machine.h"
#include "options.h"
#include "stats.h"
#include "memory.h"
/* create a flat memory space */
struct mem_t *
mem_create(char *name) /* name of the memory space */
{
struct mem_t *mem;
mem = calloc(1, sizeof(struct mem_t));
if (!mem)
fatal("out of virtual memory");
mem->name = mystrdup(name);
return mem;
}
/* translate address ADDR in memory space MEM, returns pointer to host page */
byte_t *
mem_translate(struct mem_t *mem, /* memory space to access */
md_addr_t addr) /* virtual address to translate */
{
struct mem_pte_t *pte, *prev;
/* got here via a first level miss in the page tables */
mem->ptab_misses++; mem->ptab_accesses++;
/* locate accessed PTE */
for (prev=NULL, pte=mem->ptab[MEM_PTAB_SET(addr)];
pte != NULL;
prev=pte, pte=pte->next)
{
if (pte->tag == MEM_PTAB_TAG(addr))
{
/* move this PTE to head of the bucket list */
if (prev)
{
prev->next = pte->next;
pte->next = mem->ptab[MEM_PTAB_SET(addr)];
mem->ptab[MEM_PTAB_SET(addr)] = pte;
}
return pte->page;
}
}
/* no translation found, return NULL */
return NULL;
}
/* allocate a memory page */
void
mem_newpage(struct mem_t *mem, /* memory space to allocate in */
md_addr_t addr) /* virtual address to allocate */
{
byte_t *page;
struct mem_pte_t *pte;
/* see misc.c for details on the getcore() function */
page = getcore(MD_PAGE_SIZE);
if (!page)
fatal("out of virtual memory");
/* generate a new PTE */
pte = calloc(1, sizeof(struct mem_pte_t));
if (!pte)
fatal("out of virtual memory");
pte->tag = MEM_PTAB_TAG(addr);
pte->page = page;
/* insert PTE into inverted hash table */
pte->next = mem->ptab[MEM_PTAB_SET(addr)];
mem->ptab[MEM_PTAB_SET(addr)] = pte;
/* one more page allocated */
mem->page_count++;
}
/* generic memory access function, it's safe because alignments and permissions
are checked, handles any natural transfer sizes; note, faults out if nbytes
is not a power-of-two or larger then MD_PAGE_SIZE */
enum md_fault_type
mem_access(struct mem_t *mem, /* memory space to access */
enum mem_cmd cmd, /* Read (from sim mem) or Write */
md_addr_t addr, /* target address to access */
void *vp, /* host memory address to access */
int nbytes) /* number of bytes to access */
{
byte_t *p = vp;
/* check alignments */
if (/* check size */(nbytes & (nbytes-1)) != 0
|| /* check max size */nbytes > MD_PAGE_SIZE)
return md_fault_access;
if (/* check natural alignment */(addr & (nbytes-1)) != 0)
return md_fault_alignment;
/* perform the copy */
{
if (cmd == Read)
{
while (nbytes-- > 0)
{
*((byte_t *)p) = MEM_READ_BYTE(mem, addr);
p += sizeof(byte_t);
addr += sizeof(byte_t);
}
}
else
{
while (nbytes-- > 0)
{
MEM_WRITE_BYTE(mem, addr, *((byte_t *)p));
p += sizeof(byte_t);
addr += sizeof(byte_t);
}
}
}
#if 0
switch (nbytes)
{
case 1:
if (cmd == Read)
*((byte_t *)p) = MEM_READ_BYTE(mem, addr);
else
MEM_WRITE_BYTE(mem, addr, *((byte_t *)p));
break;
case 2:
if (cmd == Read)
*((half_t *)p) = MEM_READ_HALF(mem, addr);
else
MEM_WRITE_HALF(mem, addr, *((half_t *)p));
break;
case 4:
if (cmd == Read)
*((word_t *)p) = MEM_READ_WORD(mem, addr);
else
MEM_WRITE_WORD(mem, addr, *((word_t *)p));
break;
#ifdef HOST_HAS_QWORD
case 8:
if (cmd == Read)
*((qword_t *)p) = MEM_READ_QWORD(mem, addr);
else
MEM_WRITE_QWORD(mem, addr, *((qword_t *)p));
break;
#endif /* HOST_HAS_QWORD */
default:
break;
}
#endif
/* no fault... */
return md_fault_none;
}
/* register memory system-specific statistics */
void
mem_reg_stats(struct mem_t *mem, /* memory space to declare */
struct stat_sdb_t *sdb) /* stats data base */
{
char buf[512], buf1[512];
sprintf(buf, "%s.page_count", mem->name);
stat_reg_counter(sdb, buf, "total number of pages allocated",
&mem->page_count, mem->page_count, NULL);
sprintf(buf, "%s.page_mem", mem->name);
sprintf(buf1, "%s.page_count * %d / 1024", mem->name, MD_PAGE_SIZE);
stat_reg_formula(sdb, buf, "total size of memory pages allocated",
buf1, "%11.0fk");
sprintf(buf, "%s.ptab_misses", mem->name);
stat_reg_counter(sdb, buf, "total first level page table misses",
&mem->ptab_misses, mem->ptab_misses, NULL);
sprintf(buf, "%s.ptab_accesses", mem->name);
stat_reg_counter(sdb, buf, "total page table accesses",
&mem->ptab_accesses, mem->ptab_accesses, NULL);
sprintf(buf, "%s.ptab_miss_rate", mem->name);
sprintf(buf1, "%s.ptab_misses / %s.ptab_accesses", mem->name, mem->name);
stat_reg_formula(sdb, buf, "first level page table miss rate", buf1, NULL);
}
/* initialize memory system, call before loader.c */
void
mem_init(struct mem_t *mem) /* memory space to initialize */
{
int i;
/* initialize the first level page table to all empty */
for (i=0; i < MEM_PTAB_SIZE; i++)
mem->ptab[i] = NULL;
mem->page_count = 0;
mem->ptab_misses = 0;
mem->ptab_accesses = 0;
}
/* dump a block of memory, returns any faults encountered */
enum md_fault_type
mem_dump(struct mem_t *mem, /* memory space to display */
md_addr_t addr, /* target address to dump */
int len, /* number bytes to dump */
FILE *stream) /* output stream */
{
int data;
enum md_fault_type fault;
if (!stream)
stream = stderr;
addr &= ~sizeof(word_t);
len = (len + (sizeof(word_t) - 1)) & ~sizeof(word_t);
while (len-- > 0)
{
fault = mem_access(mem, Read, addr, &data, sizeof(word_t));
if (fault != md_fault_none)
return fault;
myfprintf(stream, "0x%08p: %08x\n", addr, data);
addr += sizeof(word_t);
}
/* no faults... */
return md_fault_none;
}
/* copy a '\0' terminated string to/from simulated memory space, returns
the number of bytes copied, returns any fault encountered */
enum md_fault_type
mem_strcpy(mem_access_fn mem_fn, /* user-specified memory accessor */
struct mem_t *mem, /* memory space to access */
enum mem_cmd cmd, /* Read (from sim mem) or Write */
md_addr_t addr, /* target address to access */
char *s)
{
int n = 0;
char c;
enum md_fault_type fault;
switch (cmd)
{
case Read:
/* copy until string terminator ('\0') is encountered */
do {
fault = mem_fn(mem, Read, addr++, &c, 1);
if (fault != md_fault_none)
return fault;
*s++ = c;
n++;
} while (c);
break;
case Write:
/* copy until string terminator ('\0') is encountered */
do {
c = *s++;
fault = mem_fn(mem, Write, addr++, &c, 1);
if (fault != md_fault_none)
return fault;
n++;
} while (c);
break;
default:
return md_fault_internal;
}
/* no faults... */
return md_fault_none;
}
/* copy NBYTES to/from simulated memory space, returns any faults */
enum md_fault_type
mem_bcopy(mem_access_fn mem_fn, /* user-specified memory accessor */
struct mem_t *mem, /* memory space to access */
enum mem_cmd cmd, /* Read (from sim mem) or Write */
md_addr_t addr, /* target address to access */
void *vp, /* host memory address to access */
int nbytes)
{
byte_t *p = vp;
enum md_fault_type fault;
/* copy NBYTES bytes to/from simulator memory */
while (nbytes-- > 0)
{
fault = mem_fn(mem, cmd, addr++, p++, 1);
if (fault != md_fault_none)
return fault;
}
/* no faults... */
return md_fault_none;
}
/* copy NBYTES to/from simulated memory space, NBYTES must be a multiple
of 4 bytes, this function is faster than mem_bcopy(), returns any
faults encountered */
enum md_fault_type
mem_bcopy4(mem_access_fn mem_fn, /* user-specified memory accessor */
struct mem_t *mem, /* memory space to access */
enum mem_cmd cmd, /* Read (from sim mem) or Write */
md_addr_t addr, /* target address to access */
void *vp, /* host memory address to access */
int nbytes)
{
byte_t *p = vp;
int words = nbytes >> 2; /* note: nbytes % 2 == 0 is assumed */
enum md_fault_type fault;
while (words-- > 0)
{
fault = mem_fn(mem, cmd, addr, p, sizeof(word_t));
if (fault != md_fault_none)
return fault;
addr += sizeof(word_t);
p += sizeof(word_t);
}
/* no faults... */
return md_fault_none;
}
/* zero out NBYTES of simulated memory, returns any faults encountered */
enum md_fault_type
mem_bzero(mem_access_fn mem_fn, /* user-specified memory accessor */
struct mem_t *mem, /* memory space to access */
md_addr_t addr, /* target address to access */
int nbytes)
{
byte_t c = 0;
enum md_fault_type fault;
/* zero out NBYTES of simulator memory */
while (nbytes-- > 0)
{
fault = mem_fn(mem, Write, addr++, &c, 1);
if (fault != md_fault_none)
return fault;
}
/* no faults... */
return md_fault_none;
}
#if 0
/*
* The SimpleScalar virtual memory address space is 2^31 bytes mapped from
* 0x00000000 to 0x7fffffff. The upper 2^31 bytes are currently reserved for
* future developments. The address space from 0x00000000 to 0x00400000 is
* currently unused. The address space from 0x00400000 to 0x10000000 is used
* to map the program text (code), although accessing any memory outside of
* the defined program space causes an error to be declared. The address
* space from 0x10000000 to "mem_brk_point" is used for the program data
* segment. This section of the address space is initially set to contain the
* initialized data segment and then the uninitialized data segment.
* "mem_brk_point" then grows to higher memory when sbrk() is called to
* service heap growth. The data segment can continue to expand until it
* collides with the stack segment. The stack segment starts at 0x7fffc000
* and grows to lower memory as more stack space is allocated. Initially,
* the stack contains program arguments and environment variables (see
* loader.c for details on initial stack layout). The stack may continue to
* expand to lower memory until it collides with the data segment.
*
* The SimpleScalar virtual memory address space is implemented with a
* one level page table, where the first level table contains MEM_TABLE_SIZE
* pointers to MEM_BLOCK_SIZE byte pages in the second level table. Pages
* are allocated in MEM_BLOCK_SIZE size chunks when first accessed, the initial
* value of page memory is all zero.
*
* Graphically, it all looks like this:
*
* Virtual Level 1 Host Memory Pages
* Address Page (allocated as needed)
* Space Table
* 0x00000000 +----------+ +-+ +-------------------+
* | unused | | |----->| memory page (64k) |
* 0x00400000 +----------+ +-+ +-------------------+
* | | | |
* | text | +-+
* | | | |
* 0x10000000 +----------+ +-+
* | | | |
* | data seg | +-+ +-------------------+
* | | | |----->| memory page (64k) |
* mem_brk_point +----------+ +-+ +-------------------+
* | | | |
* | | +-+
* | | | |
* regs_R[29] +----------+ +-+
* (stack ptr) | | | |
* | stack | +-+
* | | | |
* 0x7fffc000 +----------+ +-+ +-------------------+
* | unsed | | |----->| memory page (64k) |
* 0x7fffffff +----------+ +-+ +-------------------+
*/
/* top of the data segment, sbrk() moves this to higher memory */
extern SS_ADDR_TYPE mem_brk_point;
/* lowest address accessed on the stack */
extern SS_ADDR_TYPE mem_stack_min;
/*
* memory page table defs
*/
/* memory indirect table size (upper mem is not used) */
#define MEM_TABLE_SIZE 0x8000 /* was: 0x7fff */
#ifndef HIDE_MEM_TABLE_DEF /* used by sim-fast.c */
/* the level 1 page table map */
extern char *mem_table[MEM_TABLE_SIZE];
#endif /* HIDE_MEM_TABLE_DEF */
/* memory block size, in bytes */
#define MEM_BLOCK_SIZE 0x10000
/* check permissions, no probes allowed into undefined segment regions */
if (!(/* text access and a read */
(addr >= ld_text_base && addr < (ld_text_base+ld_text_size)
&& cmd == Read)
/* data access within bounds */
|| (addr >= ld_data_base && addr < ld_stack_base)))
fatal("access error: segmentation violation, addr 0x%08p", addr);
/* track the minimum SP for memory access stats */
if (addr > mem_brk_point && addr < mem_stack_min)
mem_stack_min = addr;
/* determines if the memory access is valid, returns error str or NULL */
char * /* error string, or NULL */
mem_valid(struct mem_t *mem, /* memory space to probe */
enum mem_cmd cmd, /* Read (from sim'ed mem) or Write */
md_addr_t addr, /* target address to access */
int nbytes, /* number of bytes to access */
int declare); /* declare any detected error? */
/* determines if the memory access is valid, returns error str or NULL */
char * /* error string, or NULL */
mem_valid(enum mem_cmd cmd, /* Read (from sim mem) or Write */
SS_ADDR_TYPE addr, /* target address to access */
int nbytes, /* number of bytes to access */
int declare) /* declare the error if detected? */
{
char *err_str = NULL;
/* check alignments */
if ((nbytes & (nbytes-1)) != 0 || (addr & (nbytes-1)) != 0)
{
err_str = "bad size or alignment";
}
/* check permissions, no probes allowed into undefined segment regions */
else if (!(/* text access and a read */
(addr >= ld_text_base && addr < (ld_text_base+ld_text_size)
&& cmd == Read)
/* data access within bounds */
|| (addr >= ld_data_base && addr < ld_stack_base)))
{
err_str = "segmentation violation";
}
/* track the minimum SP for memory access stats */
if (addr > mem_brk_point && addr < mem_stack_min)
mem_stack_min = addr;
if (!declare)
return err_str;
else if (err_str != NULL)
fatal(err_str);
else /* no error */
return NULL;
}
/* initialize memory system, call after loader.c */
void
mem_init1(void)
{
/* initialize the bottom of heap to top of data segment */
mem_brk_point = ROUND_UP(ld_data_base + ld_data_size, SS_PAGE_SIZE);
/* set initial minimum stack pointer value to initial stack value */
mem_stack_min = regs_R[SS_STACK_REGNO];
}
#endif