--- tme/host/posix/posix-memory.c 2018/04/24 16:39:14 1.1.1.2 +++ tme/host/posix/posix-memory.c 2018/04/24 16:46:14 1.1.1.5 @@ -1,4 +1,4 @@ -/* $Id: posix-memory.c,v 1.1.1.2 2018/04/24 16:39:14 root Exp $ */ +/* $Id: posix-memory.c,v 1.1.1.5 2018/04/24 16:46:14 root Exp $ */ /* host/posix/posix-memory.c - implementation of memory on a POSIX system: */ @@ -34,10 +34,11 @@ */ #include -_TME_RCSID("$Id: posix-memory.c,v 1.1.1.2 2018/04/24 16:39:14 root Exp $"); +_TME_RCSID("$Id: posix-memory.c,v 1.1.1.5 2018/04/24 16:46:14 root Exp $"); /* includes: */ #include +#include #include #include #include @@ -52,12 +53,39 @@ _TME_RCSID("$Id: posix-memory.c,v 1.1.1. #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; @@ -68,10 +96,22 @@ struct tme_posix_memory { int tme_posix_memory_mapped; /* our rwlock: */ - tme_mutex_t tme_posix_memory_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: */ @@ -83,6 +123,29 @@ _tme_posix_memory_bus_cycle(void *_memor /* 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 @@ -95,28 +158,58 @@ _tme_posix_memory_bus_cycle(void *_memor 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, unsigned int cycles) + tme_bus_addr_t address_wider, + unsigned int cycles) { struct tme_posix_memory *memory; - tme_bus_addr_t memory_address_last; + 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); - /* this TLB entry can cover the whole device: */ - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, 0); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, memory_address_last); - /* 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; @@ -125,16 +218,174 @@ _tme_posix_memory_tlb_fill(void *_memory } tlb->tme_bus_tlb_rwlock = &memory->tme_posix_memory_rwlock; - /* all memory devices allow reading and writing: */ - tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; - /* 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; @@ -143,6 +394,7 @@ TME_ELEMENT_SUB_NEW_DECL(tme_host_posix, int fd; struct stat statbuf; struct tme_posix_memory *memory; + struct tme_bus_cacheable *cacheable; ssize_t bytes_read; int arg_i; int usage; @@ -297,6 +549,38 @@ TME_ELEMENT_SUB_NEW_DECL(tme_host_posix, /* 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);