diff --git a/hw/display/sm501.c b/hw/display/sm501.c index d5e165daef..cacb6c87ec 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -571,6 +571,25 @@ static uint32_t get_local_mem_size_index(uint32_t size) return index; } +static void set_new_local_mem_size_index(SM501State *s, uint32_t idx) +{ + /* + * Update local_mem_size_index on guest write. We don't allow this + * to be set to larger than the actual RAM size. (The guest will + * still read back the SYSTEM_CONTROL.Size bits that it wrote.) + */ + if (idx < ARRAY_SIZE(sm501_mem_local_size) && + sm501_mem_local_size[idx] <= memory_region_size(&s->local_mem_region)) { + s->local_mem_size_index = idx; + return; + } + qemu_log_mask(LOG_GUEST_ERROR, + "sm501: Guest set DRAM_CONTROL.Size to 0x%x but " + "local memory is not that large\n", + idx); + /* Don't change the effective size, leave it as whatever it was */ +} + static ram_addr_t get_fb_addr(SM501State *s, int crt) { return (crt ? s->dc_crt_fb_addr : s->dc_panel_fb_addr) & 0x3FFFFF0; @@ -990,7 +1009,7 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, ret = 0x050100A0; break; case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; + ret = (s->dram_control & 0x07F1E7C0); break; case SM501_ARBTRTN_CONTROL: ret = s->arbitration_control; @@ -1049,8 +1068,7 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, s->gpio_63_32_control = value & 0xFF80FFFF; break; case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* TODO : check validity of size change */ + set_new_local_mem_size_index(s, (value >> 13) & 0x7); s->dram_control &= 0x80000000; s->dram_control |= value & 0x7FFFFFC3; break;