nkeynes@10: /** nkeynes@586: * $Id$ nkeynes@10: * mem.c is responsible for creating and maintaining the overall system memory nkeynes@10: * map, as visible from the SH4 processor. nkeynes@10: * nkeynes@10: * Copyright (c) 2005 Nathan Keynes. nkeynes@10: * nkeynes@10: * This program is free software; you can redistribute it and/or modify nkeynes@10: * it under the terms of the GNU General Public License as published by nkeynes@10: * the Free Software Foundation; either version 2 of the License, or nkeynes@10: * (at your option) any later version. nkeynes@10: * nkeynes@10: * This program is distributed in the hope that it will be useful, nkeynes@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@10: * GNU General Public License for more details. nkeynes@10: */ nkeynes@35: #define MODULE mem_module nkeynes@10: nkeynes@488: #include nkeynes@10: #include nkeynes@19: #include nkeynes@586: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include "dream.h" nkeynes@10: #include "mem.h" nkeynes@10: #include "mmio.h" nkeynes@10: #include "dreamcast.h" nkeynes@10: nkeynes@939: #ifndef PAGE_SIZE nkeynes@939: #define PAGE_SIZE 4096 nkeynes@939: #endif nkeynes@939: nkeynes@502: sh4ptr_t *page_map = NULL; nkeynes@930: mem_region_fn_t *ext_address_space = NULL; nkeynes@930: nkeynes@930: extern struct mem_region_fn mem_region_unmapped; nkeynes@10: nkeynes@968: static int mem_load(FILE *f); nkeynes@968: static void mem_save(FILE *f); nkeynes@15: struct dreamcast_module mem_module = nkeynes@736: { "MEM", mem_init, mem_reset, NULL, NULL, NULL, mem_save, mem_load }; nkeynes@15: nkeynes@10: struct mem_region mem_rgn[MAX_MEM_REGIONS]; nkeynes@10: struct mmio_region *io_rgn[MAX_IO_REGIONS]; nkeynes@10: struct mmio_region *P4_io[4096]; nkeynes@10: nkeynes@674: uint32_t num_io_rgns = 0, num_mem_rgns = 0; nkeynes@10: nkeynes@931: DEFINE_HOOK( mem_page_remapped_hook, mem_page_remapped_hook_t ); nkeynes@931: static void mem_page_remapped( sh4addr_t addr, mem_region_fn_t fn ) nkeynes@931: { nkeynes@931: CALL_HOOKS( mem_page_remapped_hook, addr, fn ); nkeynes@931: } nkeynes@931: nkeynes@931: /********************* The "unmapped" address space ********************/ nkeynes@931: /* Always reads as 0, writes have no effect */ nkeynes@931: int32_t FASTCALL unmapped_read_long( sh4addr_t addr ) nkeynes@931: { nkeynes@931: return 0; nkeynes@931: } nkeynes@931: void FASTCALL unmapped_write_long( sh4addr_t addr, uint32_t val ) nkeynes@931: { nkeynes@931: } nkeynes@931: void FASTCALL unmapped_read_burst( unsigned char *dest, sh4addr_t addr ) nkeynes@931: { nkeynes@931: memset( dest, 0, 32 ); nkeynes@931: } nkeynes@931: void FASTCALL unmapped_write_burst( sh4addr_t addr, unsigned char *src ) nkeynes@931: { nkeynes@931: } nkeynes@931: nkeynes@946: void FASTCALL unmapped_prefetch( sh4addr_t addr ) nkeynes@946: { nkeynes@946: /* No effect */ nkeynes@946: } nkeynes@946: nkeynes@931: struct mem_region_fn mem_region_unmapped = { nkeynes@931: unmapped_read_long, unmapped_write_long, nkeynes@931: unmapped_read_long, unmapped_write_long, nkeynes@931: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: unmapped_prefetch, unmapped_read_long }; nkeynes@931: nkeynes@10: void *mem_alloc_pages( int n ) nkeynes@10: { nkeynes@10: void *mem = mmap( NULL, n * 4096, nkeynes@736: PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@10: if( mem == MAP_FAILED ) { nkeynes@10: ERROR( "Memory allocation failure! (%s)", strerror(errno) ); nkeynes@10: return NULL; nkeynes@10: } nkeynes@10: return mem; nkeynes@10: } nkeynes@10: nkeynes@939: void mem_unprotect( void *region, uint32_t size ) nkeynes@939: { nkeynes@939: /* Force page alignment */ nkeynes@939: uintptr_t i = (uintptr_t)region; nkeynes@939: uintptr_t mask = ~(PAGE_SIZE-1); nkeynes@939: void *ptr = (void *)(i & mask); nkeynes@968: size_t len = (i & (PAGE_SIZE-1)) + size; nkeynes@939: len = (len + (PAGE_SIZE-1)) & mask; nkeynes@939: nkeynes@939: int status = mprotect( ptr, len, PROT_READ|PROT_WRITE|PROT_EXEC ); nkeynes@939: assert( status == 0 ); nkeynes@939: } nkeynes@10: nkeynes@10: void mem_init( void ) nkeynes@10: { nkeynes@930: int i; nkeynes@930: mem_region_fn_t *ptr; nkeynes@986: page_map = (sh4ptr_t *)mmap( NULL, sizeof(sh4ptr_t) * LXDREAM_PAGE_TABLE_ENTRIES, nkeynes@736: PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@10: if( page_map == MAP_FAILED ) { nkeynes@930: FATAL( "Unable to allocate page map! (%s)", strerror(errno) ); nkeynes@930: } nkeynes@930: memset( page_map, 0, sizeof(sh4ptr_t) * LXDREAM_PAGE_TABLE_ENTRIES ); nkeynes@930: nkeynes@986: ext_address_space = (mem_region_fn_t *) mmap( NULL, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES, nkeynes@930: PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@930: if( ext_address_space == MAP_FAILED ) { nkeynes@930: FATAL( "Unable to allocate external memory map (%s)", strerror(errno) ); nkeynes@10: } nkeynes@10: nkeynes@930: for( ptr = ext_address_space, i = LXDREAM_PAGE_TABLE_ENTRIES; i > 0; ptr++, i-- ) { nkeynes@930: *ptr = &mem_region_unmapped; nkeynes@930: } nkeynes@10: } nkeynes@10: nkeynes@10: void mem_reset( void ) nkeynes@10: { nkeynes@10: /* Restore all mmio registers to their initial settings */ nkeynes@10: int i, j; nkeynes@10: for( i=1; iports[j].id != NULL; j++ ) { nkeynes@10: if( io_rgn[i]->ports[j].def_val != UNDEFINED && nkeynes@736: io_rgn[i]->ports[j].def_val != *io_rgn[i]->ports[j].val ) { nkeynes@929: io_rgn[i]->fn.write_long( io_rgn[i]->ports[j].offset, nkeynes@736: io_rgn[i]->ports[j].def_val ); nkeynes@10: } nkeynes@10: } nkeynes@10: } nkeynes@10: } nkeynes@10: nkeynes@968: static void mem_save( FILE *f ) nkeynes@17: { nkeynes@934: int i, num_ram_regions = 0; nkeynes@17: uint32_t len; nkeynes@736: nkeynes@934: /* All RAM regions (ROM and non-memory regions don't need to be saved) nkeynes@934: * Flash is questionable - for now we save it too */ nkeynes@17: for( i=0; iid, f ); nkeynes@736: fwrite( &io_rgn[i]->base, sizeof( uint32_t ), 1, f ); nkeynes@736: len = 4096; nkeynes@736: fwrite( &len, sizeof(len), 1, f ); nkeynes@736: fwrite_gzip( io_rgn[i]->mem, len, 1, f ); nkeynes@17: } nkeynes@17: } nkeynes@17: nkeynes@968: static int mem_load( FILE *f ) nkeynes@17: { nkeynes@17: char tmp[64]; nkeynes@17: uint32_t len; nkeynes@18: uint32_t base, size; nkeynes@510: uint32_t flags; nkeynes@934: int i, j; nkeynes@934: int mem_region_loaded[MAX_MEM_REGIONS]; nkeynes@17: nkeynes@934: /* All RAM regions */ nkeynes@934: memset( mem_region_loaded, 0, sizeof(mem_region_loaded) ); nkeynes@18: fread( &len, sizeof(len), 1, f ); nkeynes@18: for( i=0; iid, tmp ) != 0 || nkeynes@736: base != io_rgn[i]->base || nkeynes@736: size != 4096 ) { nkeynes@736: ERROR( "Bad MMIO region %d %s", i, tmp ); nkeynes@736: return -1; nkeynes@736: } nkeynes@736: fread_gzip( io_rgn[i]->mem, size, 1, f ); nkeynes@18: } nkeynes@18: return 0; nkeynes@17: } nkeynes@17: nkeynes@50: int mem_save_block( const gchar *file, uint32_t start, uint32_t length ) nkeynes@19: { nkeynes@502: sh4ptr_t region; nkeynes@50: int len = 4096, total = 0; nkeynes@50: uint32_t addr = start; nkeynes@50: FILE *f = fopen(file,"w"); nkeynes@19: nkeynes@50: if( f == NULL ) nkeynes@736: return errno; nkeynes@736: nkeynes@50: while( total < length ) { nkeynes@736: region = mem_get_region(addr); nkeynes@736: len = 4096 - (addr & 0x0FFF); nkeynes@736: if( len > (length-total) ) nkeynes@736: len = (length-total); nkeynes@736: if( fwrite( region, len, 1, f ) != 1 ) { nkeynes@736: ERROR( "Unexpected error writing blocks: %d (%s)", len, strerror(errno) ); nkeynes@736: break; nkeynes@736: } nkeynes@736: nkeynes@736: addr += len; nkeynes@736: total += len; nkeynes@50: } nkeynes@50: fclose( f ); nkeynes@743: INFO( "Saved %d of %d bytes to %08X", total, length, start ); nkeynes@50: return 0; nkeynes@19: } nkeynes@19: nkeynes@19: int mem_load_block( const gchar *file, uint32_t start, uint32_t length ) nkeynes@19: { nkeynes@502: sh4ptr_t region; nkeynes@19: int len = 4096, total = 0; nkeynes@19: uint32_t addr = start; nkeynes@19: struct stat st; nkeynes@19: FILE *f = fopen(file,"r"); nkeynes@19: nkeynes@180: if( f == NULL ) { nkeynes@736: WARN( "Unable to load file '%s': %s", file, strerror(errno) ); nkeynes@736: return -1; nkeynes@180: } nkeynes@19: fstat( fileno(f), &st ); nkeynes@72: if( length == 0 || length == -1 || length > st.st_size ) nkeynes@736: length = st.st_size; nkeynes@736: nkeynes@19: while( total < length ) { nkeynes@736: region = mem_get_region(addr); nkeynes@736: len = 4096 - (addr & 0x0FFF); nkeynes@736: if( len > (length-total) ) nkeynes@736: len = (length-total); nkeynes@736: if( fread( region, len, 1, f ) != 1 ) { nkeynes@736: ERROR( "Unexpected error reading: %d (%s)", len, strerror(errno) ); nkeynes@736: break; nkeynes@736: } nkeynes@736: nkeynes@736: addr += len; nkeynes@736: total += len; nkeynes@19: } nkeynes@19: fclose( f ); nkeynes@19: INFO( "Loaded %d of %d bytes to %08X", total, length, start ); nkeynes@19: return 0; nkeynes@19: } nkeynes@19: nkeynes@10: struct mem_region *mem_map_region( void *mem, uint32_t base, uint32_t size, nkeynes@934: const char *name, mem_region_fn_t fn, int flags, nkeynes@934: uint32_t repeat_offset, uint32_t repeat_until ) nkeynes@10: { nkeynes@10: int i; nkeynes@10: mem_rgn[num_mem_rgns].base = base; nkeynes@10: mem_rgn[num_mem_rgns].size = size; nkeynes@10: mem_rgn[num_mem_rgns].flags = flags; nkeynes@10: mem_rgn[num_mem_rgns].name = name; nkeynes@10: mem_rgn[num_mem_rgns].mem = mem; nkeynes@929: mem_rgn[num_mem_rgns].fn = fn; nkeynes@946: fn->prefetch = unmapped_prefetch; nkeynes@975: fn->read_byte_for_write = fn->read_byte; nkeynes@10: num_mem_rgns++; nkeynes@10: nkeynes@146: do { nkeynes@930: for( i=0; i>LXDREAM_PAGE_BITS; i++ ) { nkeynes@934: if( mem != NULL ) { nkeynes@968: page_map[(base>>LXDREAM_PAGE_BITS)+i] = ((unsigned char *)mem) + (i<>LXDREAM_PAGE_BITS)+i] = fn; nkeynes@931: mem_page_remapped( base + (i<mem = mem_alloc_pages(2); nkeynes@796: io->save_mem = io->mem + LXDREAM_PAGE_SIZE; nkeynes@10: io->index = (struct mmio_port **)malloc(1024*sizeof(struct mmio_port *)); nkeynes@41: io->trace_flag = 0; nkeynes@10: memset( io->index, 0, 1024*sizeof(struct mmio_port *) ); nkeynes@10: for( i=0; io->ports[i].id != NULL; i++ ) { nkeynes@10: io->ports[i].val = (uint32_t *)(io->mem + io->ports[i].offset); nkeynes@10: *io->ports[i].val = io->ports[i].def_val; nkeynes@10: io->index[io->ports[i].offset>>2] = &io->ports[i]; nkeynes@10: } nkeynes@796: memcpy( io->save_mem, io->mem, LXDREAM_PAGE_SIZE ); nkeynes@10: if( (io->base & 0xFF000000) == 0xFF000000 ) { nkeynes@10: /* P4 area (on-chip I/O channels */ nkeynes@10: P4_io[(io->base&0x1FFFFFFF)>>19] = io; nkeynes@10: } else { nkeynes@674: page_map[io->base>>12] = (sh4ptr_t)(uintptr_t)num_io_rgns; nkeynes@930: ext_address_space[io->base>>12] = &io->fn; nkeynes@931: mem_page_remapped( io->base, &io->fn ); nkeynes@10: } nkeynes@10: io_rgn[num_io_rgns] = io; nkeynes@10: num_io_rgns++; nkeynes@10: } nkeynes@10: nkeynes@10: void register_io_regions( struct mmio_region **io ) nkeynes@10: { nkeynes@10: while( *io ) register_io_region( *io++ ); nkeynes@10: } nkeynes@10: nkeynes@934: gboolean mem_has_page( uint32_t addr ) nkeynes@10: { nkeynes@934: return ext_address_space[ (addr&0x1FFFFFFF)>>12 ] != &mem_region_unmapped; nkeynes@10: } nkeynes@10: nkeynes@502: sh4ptr_t mem_get_region( uint32_t addr ) nkeynes@10: { nkeynes@502: sh4ptr_t page = page_map[ (addr & 0x1FFFFFFF) >> 12 ]; nkeynes@490: if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */ nkeynes@10: return NULL; nkeynes@10: } else { nkeynes@10: return page+(addr&0xFFF); nkeynes@10: } nkeynes@10: } nkeynes@11: nkeynes@669: void mem_write_long( sh4addr_t addr, uint32_t value ) nkeynes@669: { nkeynes@934: ext_address_space[(addr&0x1FFFFFFF)>>12]->write_long(addr, value); nkeynes@156: } nkeynes@156: nkeynes@968: static struct mmio_region *mem_get_io_region_by_name( const gchar *name ) nkeynes@156: { nkeynes@586: int i; nkeynes@586: for( i=0; iid, name) == 0 ) { nkeynes@736: return io_rgn[i]; nkeynes@736: } nkeynes@586: } nkeynes@586: return NULL; nkeynes@156: } nkeynes@156: nkeynes@586: void mem_set_trace( const gchar *tracelist, gboolean flag ) nkeynes@586: { nkeynes@586: if( tracelist != NULL ) { nkeynes@736: gchar ** tracev = g_strsplit_set( tracelist, ",:; \t\r\n", 0 ); nkeynes@736: int i; nkeynes@736: for( i=0; tracev[i] != NULL; i++ ) { nkeynes@736: // Special case "all" - trace everything nkeynes@736: if( strcasecmp(tracev[i], "all") == 0 ) { nkeynes@736: int j; nkeynes@736: for( j=0; jtrace_flag = flag ? 1 : 0; nkeynes@736: } nkeynes@736: break; nkeynes@736: } nkeynes@736: struct mmio_region *region = mem_get_io_region_by_name( tracev[i] ); nkeynes@736: if( region == NULL ) { nkeynes@736: WARN( "Unknown IO region '%s'", tracev[i] ); nkeynes@736: } else { nkeynes@736: region->trace_flag = flag ? 1 : 0; nkeynes@736: } nkeynes@736: } nkeynes@736: g_strfreev( tracev ); nkeynes@586: } nkeynes@586: } nkeynes@586: