diff --git a/include/gint/mmu.h b/include/gint/mmu.h index d85dc41..be1d12a 100644 --- a/include/gint/mmu.h +++ b/include/gint/mmu.h @@ -10,6 +10,7 @@ extern "C" { #endif #include +#include //--- // Unified interface @@ -40,6 +41,17 @@ void *mmu_uram(void); fx-9860G, and 512k on fx-CG 50. */ uint32_t mmu_uram_size(void); +/* mmu_is_rom(): Determine if an address points to ROM + + Checks whether the supplied pointer points to ROM or to a virtualized + portion of ROM. For the sake of efficiency, this function uses heuristics + about the structure of P0 rather than actually checking the TLB. + + This is useful during filesystem accesses because only data outside of ROM + can be written to files. Pointers for which this function returns true + cannot be used as a source for BFile_Write(). */ +bool mmu_is_rom(void const *ptr); + //--- // SH7705 TLB //--- diff --git a/src/fs/fugue/fugue.c b/src/fs/fugue/fugue.c index c7d1cd8..00185da 100644 --- a/src/fs/fugue/fugue.c +++ b/src/fs/fugue/fugue.c @@ -1,7 +1,9 @@ #include #include #include +#include #include +#include #include #include #include "fugue.h" @@ -26,18 +28,71 @@ ssize_t fugue_read(void *data0, void *buf, size_t size) return size; } +static void *temp_ram(size_t total_size, size_t *block_size) +{ + for(size_t candidate = 16384; candidate >= 64; candidate >>= 1) { + if(candidate > 64 && candidate >= 2 * total_size) + continue; + + size_t size = min(candidate, total_size); + void *ram = malloc(size); + if(ram) { + *block_size = size; + return ram; + } + } + + return NULL; +} + ssize_t fugue_write(void *data0, const void *buf, size_t size) { fugue_fd_t *data = data0; int fugue_fd = data->fd; - int rc = BFile_Write(fugue_fd, buf, size); - if(rc < 0) { - errno = bfile_error_to_errno(rc); - return -1; + /* The main concern of this function is that we cannot write from ROM. + If [buf] is in ROM then we have to copy it to RAM, preferably within + the limits of available heap memory. */ + if(mmu_is_rom(buf)) { + size_t alloc_size, written=0; + + void *ram = temp_ram(size, &alloc_size); + if(!ram) { + errno = ENOMEM; + return -1; + } + + while(written < size) { + size_t block_size = min(size - written, alloc_size); + memcpy(ram, buf + written, block_size); + + int rc = BFile_Write(fugue_fd, ram, block_size); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + written = -1; + break; + } + + written += rc; + data->pos += rc; + + /* Partial write */ + if(rc < (int)block_size) break; + } + + free(ram); + return written; + } + /* Otherwise, we can write normally */ + else { + int rc = BFile_Write(fugue_fd, buf, size); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + data->pos += rc; + return rc; } - data->pos += rc; - return rc; } off_t fugue_lseek(void *data0, off_t offset, int whence) diff --git a/src/mmu/mmu.c b/src/mmu/mmu.c index 90c0389..187eeb9 100644 --- a/src/mmu/mmu.c +++ b/src/mmu/mmu.c @@ -38,6 +38,21 @@ uint32_t mmu_uram_size(void) return size; } +/* mmu_is_rom(): Determine if an address points to ROM */ +bool mmu_is_rom(void const *ptr) +{ + uint32_t a = (uint32_t)ptr; + + if(a >= 0x80000000 && a < 0x88000000) + return true; + if(a >= 0xa0000000 && a < 0xa8000000) + return true; + if(a >= 0x00300000 && a < 0x00800000) + return true; + + return false; +} + //--- // SH7705 TLB //---