Files
Amberelle Mason ac30ff9032 Initial import
Initial import of SunOS 4.1.1 and TME 0.8
2023-05-01 12:16:40 -04:00

594 lines
17 KiB
C

/* $Id: posix-memory.c,v 1.7 2009/08/30 21:50:17 fredette Exp $ */
/* host/posix/posix-memory.c - implementation of memory on a POSIX system: */
/*
* Copyright (c) 2003 Matt Fredette
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <tme/common.h>
_TME_RCSID("$Id: posix-memory.c,v 1.7 2009/08/30 21:50:17 fredette Exp $");
/* includes: */
#include <tme/generic/bus-device.h>
#include <tme/hash.h>
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include <sys/stat.h>
#ifdef HAVE_MMAP
#include <sys/types.h>
#include <sys/mman.h>
#endif /* HAVE_MMAP */
/* macros: */
#define TME_POSIX_MEMORY_RAM (0)
#define TME_POSIX_MEMORY_ROM (1)
#define TME_POSIX_MEMORY_PERSISTENT (2)
/* the minimum size of a cacheable RAM: */
#define TME_MEMORY_POSIX_CACHEABLE_SIZE_RAM (1024 * 1024)
/* the minimum size of a cacheable ROM: */
#define TME_MEMORY_POSIX_CACHEABLE_SIZE_ROM (64 * 1024)
/* the size of the writable TLB entry set: */
#define TME_MEMORY_POSIX_TLBS_SIZE (631)
/* structures: */
/* a valids bitmask: */
struct tme_posix_memory_valids {
/* valid bitmasks are kept on a linked list: */
struct tme_posix_memory_valids *tme_posix_memory_valids_next;
/* the log2 of the page size for this valids bitmask: */
tme_uint32_t tme_posix_memory_valids_page_size_log2;
/* the valids bitmask: */
tme_shared tme_uint8_t tme_posix_memory_valids_bitmask[1];
};
/* the memory structure: */
struct tme_posix_memory {
/* our simple bus device header: */
struct tme_bus_device tme_posix_memory_device;
/* the mutex protecting the device: */
tme_mutex_t tme_posix_memory_mutex;
/* our memory type: */
unsigned int tme_posix_memory_type;
/* the file descriptor to any backing file: */
int tme_posix_memory_fd;
/* this is nonzero if the backing file is mmapped: */
int tme_posix_memory_mapped;
/* our rwlock: */
tme_rwlock_t tme_posix_memory_rwlock;
/* our memory contents: */
tme_uint8_t *tme_posix_memory_contents;
/* our writable TLB entry set: */
struct tme_token **tme_posix_memory_tlb_tokens;
/* any valids bitmasks: */
struct tme_posix_memory_valids *tme_posix_memory_valids;
/* the current writable TLB size: */
tme_uint32_t tme_posix_memory_tlb_size;
/* our bus cacheable structure: */
struct tme_bus_cacheable tme_posix_memory_cacheable;
};
/* the memory bus cycle handler: */
static int
_tme_posix_memory_bus_cycle(void *_memory, struct tme_bus_cycle *cycle)
{
struct tme_posix_memory *memory;
/* recover our data structure: */
memory = (struct tme_posix_memory *) _memory;
#if 0
if (cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
printf("posix write @ %x\n", (unsigned int)cycle->tme_bus_cycle_address);
}
if (cycle->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
printf("posix read @ %x\n", (unsigned int)cycle->tme_bus_cycle_address);
}
#endif
#if 1
if (cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
extern int get_sunbw2_copy(void);
extern int get_sunbw2_copy_addr(void);
int printf(const char *format, ...);
if (get_sunbw2_copy()) {
if ((cycle->tme_bus_cycle_address & 0x007e0000) == get_sunbw2_copy_addr()) {
//printf("posix hit! @ %x\n", (unsigned int)cycle->tme_bus_cycle_address);
}
}
}
#endif
/* run the cycle: */
tme_bus_cycle_xfer_memory(cycle,
((cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE
&& memory->tme_posix_memory_type == TME_POSIX_MEMORY_ROM)
? NULL
: memory->tme_posix_memory_contents),
memory->tme_posix_memory_device.tme_bus_device_address_last);
/* no faults: */
return (TME_OK);
}
/* this invalidates all outstanding TLBs. it must be called with the
mutex held: */
static void
_tme_posix_memory_tlbs_invalidate(struct tme_posix_memory *memory)
{
signed long tlb_i;
struct tme_token **tlb_tokens;
struct tme_token *tlb_token;
/* invalidate all writable TLBS: */
tlb_i = TME_MEMORY_POSIX_TLBS_SIZE - 1;
tlb_tokens = memory->tme_posix_memory_tlb_tokens;
do {
tlb_token = tlb_tokens[tlb_i];
if (tlb_token != NULL) {
tlb_tokens[tlb_i] = NULL;
tme_token_invalidate(tlb_token);
}
} while (--tlb_i >= 0);
}
/* the memory TLB filler: */
static int
_tme_posix_memory_tlb_fill(void *_memory, struct tme_bus_tlb *tlb,
tme_bus_addr_t address_wider,
unsigned int cycles)
{
struct tme_posix_memory *memory;
unsigned long address;
unsigned long memory_address_last;
struct tme_token *tlb_token;
struct tme_token **_tlb_token;
struct tme_token *tlb_token_other;
struct tme_posix_memory_valids *valids;
unsigned long page_index;
tme_uint32_t tlb_size;
/* recover our data structure: */
memory = (struct tme_posix_memory *) _memory;
/* get the normal-width address: */
address = address_wider;
assert(address == address_wider);
/* the address must be within range: */
memory_address_last = memory->tme_posix_memory_device.tme_bus_device_address_last;
assert(memory_address_last == memory->tme_posix_memory_device.tme_bus_device_address_last);
assert(address <= memory_address_last);
/* initialize the TLB entry: */
tme_bus_tlb_initialize(tlb);
/* all memory devices allow fast reading. all memory devices except
ROMs allow fast writing: */
tlb->tme_bus_tlb_emulator_off_read = memory->tme_posix_memory_contents;
if (memory->tme_posix_memory_type != TME_POSIX_MEMORY_ROM) {
tlb->tme_bus_tlb_emulator_off_write = memory->tme_posix_memory_contents;
}
tlb->tme_bus_tlb_rwlock = &memory->tme_posix_memory_rwlock;
/* our bus cycle handler: */
tlb->tme_bus_tlb_cycle_private = memory;
tlb->tme_bus_tlb_cycle = _tme_posix_memory_bus_cycle;
/* if this device is cacheable: */
if (__tme_predict_true(memory->tme_posix_memory_tlb_tokens != NULL)) {
/* this TLB entry is for cacheable memory: */
tlb->tme_bus_tlb_cacheable = &memory->tme_posix_memory_cacheable;
/* if this TLB entry is for writing: */
if (cycles & TME_BUS_CYCLE_WRITE) {
/* lock our mutex: */
tme_mutex_lock(&memory->tme_posix_memory_mutex);
/* get the backing TLB entry: */
tlb_token = tlb->tme_bus_tlb_token;
/* hash the TLB entry into our writable TLB entry set: */
_tlb_token
= (((tme_hash_data_to_ulong(tlb_token)
/ sizeof(struct tme_token))
% TME_MEMORY_POSIX_TLBS_SIZE)
+ memory->tme_posix_memory_tlb_tokens);
/* if there is a different TLB entry already at this position in
the writable TLB entry set: */
tlb_token_other = *_tlb_token;
if (__tme_predict_true(tlb_token_other != NULL)) {
if (tlb_token_other != tlb_token) {
/* invalidate this other TLB entry: */
tme_token_invalidate(tlb_token_other);
}
}
/* save the TLB entry into the writable TLB entry set: */
*_tlb_token = tlb->tme_bus_tlb_token;
/* loop over the valids bitmasks: */
for (valids = memory->tme_posix_memory_valids;
valids != NULL;
valids = valids->tme_posix_memory_valids_next) {
/* clear the bit for this address' page in this valids
bitmask: */
page_index = (address >> valids->tme_posix_memory_valids_page_size_log2);
*(valids->tme_posix_memory_valids_bitmask
+ (page_index / 8))
&= ~(1 << (page_index % 8));
}
/* this TLB entry allows reading and writing: */
tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
/* this TLB entry only covers the current TLB size: */
tlb_size = memory->tme_posix_memory_tlb_size;
address &= 0 - (unsigned long) tlb_size;
tlb->tme_bus_tlb_addr_first = address;
tlb->tme_bus_tlb_addr_last = TME_MIN(address | (tlb_size - 1), memory_address_last);
/* unlock our mutex: */
tme_mutex_unlock(&memory->tme_posix_memory_mutex);
return (TME_OK);
}
/* this TLB entry only allows reading: */
tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ;
tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
}
/* otherwise, this device is not cacheable: */
else {
/* all memory devices allow reading and writing: */
tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
}
/* this TLB entry can cover the whole device: */
tlb->tme_bus_tlb_addr_first = 0;
tlb->tme_bus_tlb_addr_last = memory_address_last;
return (TME_OK);
}
/* this function allocates a new valids bitmask: */
static tme_shared tme_uint8_t *
_tme_posix_memory_valids_new(void *_memory,
tme_uint32_t page_size_log2)
{
struct tme_posix_memory *memory;
tme_uint32_t page_size;
unsigned long page_count;
struct tme_posix_memory_valids *valids;
/* recover our data structure: */
memory = (struct tme_posix_memory *) _memory;
/* lock our mutex: */
tme_mutex_lock(&memory->tme_posix_memory_mutex);
/* get the page size for this valids bitmask: */
assert (page_size_log2 < (sizeof(page_size) * 8 - 1));
page_size = 1;
page_size <<= page_size_log2;
/* update the current writable TLB size: */
memory->tme_posix_memory_tlb_size
= TME_MIN(memory->tme_posix_memory_tlb_size, page_size);
/* get the page count for this valids bitmask: */
page_count
= ((memory->tme_posix_memory_cacheable.tme_bus_cacheable_size
+ (page_size - 1))
>> page_size_log2);
/* allocate and initialize a new valids bitmask: */
valids
= ((struct tme_posix_memory_valids *)
tme_malloc(sizeof(struct tme_posix_memory_valids)
+ ((page_count + 7) / 8)));
valids->tme_posix_memory_valids_page_size_log2 = page_size_log2;
memset((tme_uint8_t *) valids->tme_posix_memory_valids_bitmask + 0,
0xff,
((page_count + 7) / 8));
/* add this new valids bitmask to the list: */
valids->tme_posix_memory_valids_next = memory->tme_posix_memory_valids;
memory->tme_posix_memory_valids = valids;
/* invalidate all outstanding TLB entries: */
_tme_posix_memory_tlbs_invalidate(memory);
/* unlock our mutex: */
tme_mutex_unlock(&memory->tme_posix_memory_mutex);
/* return the bitmask: */
return (valids->tme_posix_memory_valids_bitmask);
}
/* this function sets a bit in the valids bitmask: */
static void
_tme_posix_memory_valids_set(void *_memory,
tme_shared tme_uint8_t *valids_bitmask,
unsigned long page_index)
{
struct tme_posix_memory *memory;
/* recover our data structure: */
memory = (struct tme_posix_memory *) _memory;
/* lock our mutex: */
tme_mutex_lock(&memory->tme_posix_memory_mutex);
/* set the bit for this page in the valids bitmask: */
*(valids_bitmask
+ (page_index / 8))
|= TME_BIT(page_index % 8);
/* invalidate all outstanding TLB entries: */
_tme_posix_memory_tlbs_invalidate(memory);
/* unlock our mutex: */
tme_mutex_unlock(&memory->tme_posix_memory_mutex);
}
/* the new memory function: */
TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,memory) {
unsigned int memory_type;
unsigned long memory_size;
const char *filename;
int fd;
struct stat statbuf;
struct tme_posix_memory *memory;
struct tme_bus_cacheable *cacheable;
ssize_t bytes_read;
int arg_i;
int usage;
/* assume we have no backing file: */
filename = NULL;
memory_type = -1;
memory_size = 0;
arg_i = 1;
usage = FALSE;
/* we are regular RAM if our arguments are:
ram SIZE
*/
if (TME_ARG_IS(args[arg_i + 0], "ram")
&& (memory_size = tme_bus_addr_parse(args[arg_i + 1], 0)) > 0) {
memory_type = TME_POSIX_MEMORY_RAM;
arg_i += 2;
}
/* we are ROM if our arguments are:
rom FILE
*/
else if (TME_ARG_IS(args[arg_i + 0], "rom")
&& (filename = args[arg_i + 1]) != NULL) {
memory_type = TME_POSIX_MEMORY_ROM;
arg_i += 2;
}
/* we are persistent storage if our arguments are:
persistent FILE
*/
else if (TME_ARG_IS(args[arg_i + 0], "persistent")
&& (filename = args[arg_i + 1]) != NULL) {
memory_type = TME_POSIX_MEMORY_PERSISTENT;
arg_i += 2;
}
else {
usage = TRUE;
}
if (args[arg_i + 0] != NULL) {
tme_output_append_error(_output,
"%s %s",
args[arg_i],
_("unexpected"));
usage = TRUE;
}
if (usage) {
tme_output_append_error(_output,
"%s %s { rom %s | ram %s | persistent %s }",
_("usage:"),
args[0],
_("ROM-FILE"),
_("SIZE"),
_("PERSISTENT-FILE"));
return (-1);
}
/* start the memory structure: */
memory = tme_new0(struct tme_posix_memory, 1);
memory->tme_posix_memory_type = memory_type;
/* if we have a backing file: */
fd = -1;
if (filename != NULL) {
/* open the file for reading: */
fd = open(filename, (memory_type == TME_POSIX_MEMORY_ROM
? O_RDONLY
: O_RDWR));
if (fd < 0) {
tme_output_append_error(_output,
"%s",
filename);
tme_free(memory);
return (errno);
}
/* stat the file: */
if (fstat(fd, &statbuf) < 0) {
tme_output_append_error(_output,
"%s",
filename);
close(fd);
tme_free(memory);
return (errno);
}
memory_size = statbuf.st_size;
if (memory_size == 0) {
tme_output_append_error(_output,
"%s",
filename);
close(fd);
tme_free(memory);
return (EINVAL);
}
#ifdef HAVE_MMAP
/* try to mmap the file: */
memory->tme_posix_memory_contents =
mmap(NULL,
statbuf.st_size,
PROT_READ
| (memory_type != TME_POSIX_MEMORY_ROM
? PROT_WRITE
: 0),
MAP_SHARED,
fd,
0);
if (memory->tme_posix_memory_contents != MAP_FAILED) {
memory->tme_posix_memory_mapped = TRUE;
}
#endif /* HAVE_MMAP */
}
/* if we have to, allocate memory space: */
if (!memory->tme_posix_memory_mapped) {
memory->tme_posix_memory_contents = tme_new0(tme_uint8_t, memory_size);
/* if we have to, read in the backing file: */
if (fd >= 0) {
bytes_read = read(fd, memory->tme_posix_memory_contents, memory_size);
if (bytes_read < 0
|| memory_size != (unsigned long) bytes_read) {
/* XXX diagnostic: */
close(fd);
tme_free(memory->tme_posix_memory_contents);
tme_free(memory);
return (-1);
}
/* if this is a ROM, we can close the file now: */
if (memory_type == TME_POSIX_MEMORY_ROM) {
close(fd);
fd = -1;
}
}
}
/* remember any backing fd: */
memory->tme_posix_memory_fd = fd;
/* initialize our rwlock: */
tme_rwlock_init(&memory->tme_posix_memory_rwlock);
/* initialize our mutex: */
tme_mutex_init(&memory->tme_posix_memory_mutex);
/* assume that this memory won't be cacheable: */
memory->tme_posix_memory_tlb_tokens = NULL;
/* if we are regular RAM or ROM over a threshold size: */
if ((memory_type == TME_POSIX_MEMORY_RAM
&& memory_size >= TME_MEMORY_POSIX_CACHEABLE_SIZE_RAM)
|| (memory_type == TME_POSIX_MEMORY_ROM
&& memory_size >= TME_MEMORY_POSIX_CACHEABLE_SIZE_ROM)) {
/* allocate the writable TLB entry set: */
memory->tme_posix_memory_tlb_tokens
= tme_new0(struct tme_token *,
TME_MEMORY_POSIX_TLBS_SIZE);
/* initialize the valids list: */
memory->tme_posix_memory_valids = NULL;
/* initialize the current writable TLB size: */
memory->tme_posix_memory_tlb_size = ((tme_uint32_t) 1) << 31;
/* initialize the bus cacheable structure: */
cacheable = &memory->tme_posix_memory_cacheable;
cacheable->tme_bus_cacheable_contents = memory->tme_posix_memory_contents;
cacheable->tme_bus_cacheable_size = memory_size;
cacheable->tme_bus_cacheable_private = memory;
cacheable->tme_bus_cacheable_valids_new = _tme_posix_memory_valids_new;
cacheable->tme_bus_cacheable_valids_set = _tme_posix_memory_valids_set;
}
/* initialize our simple bus device descriptor: */
memory->tme_posix_memory_device.tme_bus_device_tlb_fill = _tme_posix_memory_tlb_fill;
memory->tme_posix_memory_device.tme_bus_device_address_last = (memory_size - 1);
/* fill the element: */
element->tme_element_private = memory;
element->tme_element_connections_new = tme_bus_device_connections_new;
return (0);
}