nkeynes@10: /** nkeynes@561: * $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@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@502: sh4ptr_t *page_map = NULL; nkeynes@10: nkeynes@17: int mem_load(FILE *f); nkeynes@17: void mem_save(FILE *f); nkeynes@15: struct dreamcast_module mem_module = nkeynes@23: { "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@490: uintptr_t num_io_rgns = 0, num_mem_rgns = 0; nkeynes@10: nkeynes@10: void *mem_alloc_pages( int n ) nkeynes@10: { nkeynes@10: void *mem = mmap( NULL, n * 4096, nkeynes@488: 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@10: nkeynes@10: void mem_init( void ) nkeynes@10: { nkeynes@510: page_map = mmap( NULL, sizeof(sh4ptr_t) * PAGE_TABLE_ENTRIES, nkeynes@488: PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@10: if( page_map == MAP_FAILED ) { nkeynes@10: ERROR( "Unable to allocate page map! (%s)", strerror(errno) ); nkeynes@10: page_map = NULL; nkeynes@10: return; nkeynes@10: } nkeynes@10: nkeynes@510: memset( page_map, 0, sizeof(sh4ptr_t) * PAGE_TABLE_ENTRIES ); 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@10: io_rgn[i]->ports[j].def_val != *io_rgn[i]->ports[j].val ) { nkeynes@10: io_rgn[i]->io_write( io_rgn[i]->ports[j].offset, nkeynes@10: io_rgn[i]->ports[j].def_val ); nkeynes@10: } nkeynes@10: } nkeynes@10: } nkeynes@10: } nkeynes@10: nkeynes@17: void mem_save( FILE *f ) nkeynes@17: { nkeynes@17: int i; nkeynes@17: uint32_t len; nkeynes@17: nkeynes@17: /* All memory regions */ nkeynes@17: fwrite( &num_mem_rgns, sizeof(num_mem_rgns), 1, f ); nkeynes@17: for( i=0; iid, f ); nkeynes@17: fwrite( &io_rgn[i]->base, sizeof( uint32_t ), 1, f ); nkeynes@18: len = 4096; nkeynes@18: fwrite( &len, sizeof(len), 1, f ); nkeynes@477: fwrite_gzip( io_rgn[i]->mem, len, 1, f ); nkeynes@17: } nkeynes@17: } nkeynes@17: nkeynes@17: 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@17: int i; nkeynes@17: nkeynes@17: /* All memory regions */ nkeynes@18: fread( &len, sizeof(len), 1, f ); nkeynes@18: if( len != num_mem_rgns ) nkeynes@18: return -1; nkeynes@18: for( i=0; iid, tmp ) != 0 || nkeynes@18: base != io_rgn[i]->base || nkeynes@18: size != 4096 ) { nkeynes@18: ERROR( "Bad MMIO region %d %s", i, tmp ); nkeynes@18: return -1; nkeynes@18: } nkeynes@477: 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@50: return errno; nkeynes@50: nkeynes@50: while( total < length ) { nkeynes@50: region = mem_get_region(addr); nkeynes@50: len = 4096 - (addr & 0x0FFF); nkeynes@50: if( len > (length-total) ) nkeynes@50: len = (length-total); nkeynes@50: if( fwrite( region, len, 1, f ) != 1 ) { nkeynes@50: ERROR( "Unexpected error: %d %d", len, errno ); nkeynes@50: break; nkeynes@50: } nkeynes@50: nkeynes@50: addr += len; nkeynes@50: total += len; nkeynes@50: } nkeynes@50: fclose( f ); nkeynes@50: INFO( "Loaded %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@543: WARN( "Unable to load file '%s': %s", file, strerror(errno) ); nkeynes@180: return -1; nkeynes@180: } nkeynes@19: fstat( fileno(f), &st ); nkeynes@72: if( length == 0 || length == -1 || length > st.st_size ) nkeynes@19: length = st.st_size; nkeynes@19: nkeynes@19: while( total < length ) { nkeynes@19: region = mem_get_region(addr); nkeynes@19: len = 4096 - (addr & 0x0FFF); nkeynes@19: if( len > (length-total) ) nkeynes@19: len = (length-total); nkeynes@19: if( fread( region, len, 1, f ) != 1 ) { nkeynes@19: ERROR( "Unexpected error: %d %d", len, errno ); nkeynes@19: break; nkeynes@19: } nkeynes@19: nkeynes@19: addr += len; nkeynes@19: 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@422: const char *name, int flags, uint32_t repeat_offset, nkeynes@146: 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@10: num_mem_rgns++; nkeynes@10: nkeynes@146: do { nkeynes@146: for( i=0; i>PAGE_BITS; i++ ) nkeynes@146: page_map[(base>>PAGE_BITS)+i] = mem + (i<>PAGE_BITS ); nkeynes@10: nkeynes@146: mem_map_region( mem, base, size, name, MEM_FLAG_RAM, repeat_offset, repeat_until ); nkeynes@146: nkeynes@10: return mem; nkeynes@10: } nkeynes@10: nkeynes@543: gboolean mem_load_rom( const gchar *file, uint32_t base, uint32_t size, uint32_t crc, nkeynes@543: const gchar *region_name ) nkeynes@10: { nkeynes@502: sh4ptr_t mem; nkeynes@10: uint32_t calc_crc; nkeynes@488: int status; nkeynes@461: nkeynes@461: mem = mem_get_region(base); nkeynes@461: if( mem == NULL ) { nkeynes@488: mem = mmap( NULL, size, PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@461: if( mem == MAP_FAILED ) { nkeynes@461: ERROR( "Unable to allocate ROM memory: %s (%s)", file, strerror(errno) ); nkeynes@543: return FALSE; nkeynes@461: } nkeynes@461: mem_map_region( mem, base, size, file, MEM_FLAG_ROM, size, base ); nkeynes@461: } else { nkeynes@461: mprotect( mem, size, PROT_READ|PROT_WRITE ); nkeynes@10: } nkeynes@461: nkeynes@488: status = mem_load_block( file, base, size ); nkeynes@461: mprotect( mem, size, PROT_READ ); nkeynes@10: nkeynes@488: if( status == 0 ) { nkeynes@488: /* CRC check only if we loaded something */ nkeynes@502: calc_crc = crc32(0L, (sh4ptr_t)mem, size); nkeynes@488: if( calc_crc != crc ) { nkeynes@488: WARN( "Bios CRC Mismatch in %s: %08X (expected %08X)", nkeynes@488: file, calc_crc, crc); nkeynes@488: } nkeynes@543: return TRUE; nkeynes@10: } nkeynes@18: nkeynes@543: return FALSE; nkeynes@10: } nkeynes@10: nkeynes@502: sh4ptr_t mem_get_region_by_name( const char *name ) nkeynes@10: { nkeynes@10: int i; nkeynes@10: for( i=0; imem = mem_alloc_pages(2); nkeynes@10: io->save_mem = io->mem + 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@10: memcpy( io->save_mem, io->mem, 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@502: page_map[io->base>>12] = (sh4ptr_t )num_io_rgns; 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@10: int mem_has_page( uint32_t addr ) nkeynes@10: { nkeynes@502: sh4ptr_t page = page_map[ (addr & 0x1FFFFFFF) >> 12 ]; nkeynes@10: return page != NULL; nkeynes@10: } nkeynes@10: nkeynes@502: sh4ptr_t mem_get_page( uint32_t addr ) nkeynes@10: { nkeynes@502: sh4ptr_t page = page_map[ (addr & 0x1FFFFFFF) >> 12 ]; nkeynes@10: return page; 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@156: struct mmio_region *mem_get_io_region( uint32_t addr ) nkeynes@156: { nkeynes@156: if( addr > 0xFF000000 ) { nkeynes@156: return P4_io[(addr&0x00FFFFFF)>>12]; nkeynes@156: } nkeynes@502: sh4ptr_t page = page_map[(addr&0x1FFFFFFF)>>12]; nkeynes@490: if( ((uintptr_t)page) < MAX_IO_REGIONS ) { nkeynes@490: return io_rgn[(uintptr_t)page]; nkeynes@156: } else { nkeynes@156: return NULL; nkeynes@156: } nkeynes@156: } nkeynes@156: nkeynes@156: void mem_set_trace( uint32_t addr, int flag ) nkeynes@156: { nkeynes@156: struct mmio_region *region = mem_get_io_region(addr); nkeynes@156: if( region != NULL ) nkeynes@156: region->trace_flag = flag; nkeynes@156: } nkeynes@156: