diff options
Diffstat (limited to 'hw/nvram/fw_cfg.c')
-rw-r--r-- | hw/nvram/fw_cfg.c | 73 |
1 files changed, 35 insertions, 38 deletions
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 73b0a813a7..a1d650d5a9 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -252,7 +252,8 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value) static int fw_cfg_select(FWCfgState *s, uint16_t key) { - int ret; + int arch, ret; + FWCfgEntry *e; s->cur_offset = 0; if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) { @@ -261,41 +262,45 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key) } else { s->cur_entry = key; ret = 1; - } - - trace_fw_cfg_select(s, key, ret); - return ret; -} - -static uint8_t fw_cfg_read(FWCfgState *s) -{ - int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); - FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - uint8_t ret; - - if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) - ret = 0; - else { + /* entry successfully selected, now run callback if present */ + arch = !!(key & FW_CFG_ARCH_LOCAL); + e = &s->entries[arch][key & FW_CFG_ENTRY_MASK]; if (e->read_callback) { - e->read_callback(e->callback_opaque, s->cur_offset); + e->read_callback(e->callback_opaque); } - ret = e->data[s->cur_offset++]; } - trace_fw_cfg_read(s, ret); + trace_fw_cfg_select(s, key, ret); return ret; } -static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size) { FWCfgState *s = opaque; + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); + FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : + &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; uint64_t value = 0; - unsigned i; - for (i = 0; i < size; ++i) { - value = (value << 8) | fw_cfg_read(s); + assert(size > 0 && size <= sizeof(value)); + if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) { + /* The least significant 'size' bytes of the return value are + * expected to contain a string preserving portion of the item + * data, padded with zeros on the right in case we run out early. + * In technical terms, we're composing the host-endian representation + * of the big endian interpretation of the fw_cfg string. + */ + do { + value = (value << 8) | e->data[s->cur_offset++]; + } while (--size && s->cur_offset < e->len); + /* If size is still not zero, we *did* run out early, so continue + * left-shifting, to add the appropriate number of padding zeros + * on the right. + */ + value <<= 8 * size; } + + trace_fw_cfg_read(s, value); return value; } @@ -338,7 +343,8 @@ static void fw_cfg_dma_transfer(FWCfgState *s) } arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); - e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; + e = (s->cur_entry == FW_CFG_INVALID) ? NULL : + &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; if (dma.control & FW_CFG_DMA_CTL_READ) { read = 1; @@ -371,10 +377,6 @@ static void fw_cfg_dma_transfer(FWCfgState *s) len = (e->len - s->cur_offset); } - if (e->read_callback) { - e->read_callback(e->callback_opaque, s->cur_offset); - } - /* If the access is not a read access, it will be a skip access, * tested before. */ @@ -451,12 +453,6 @@ static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, return is_write && size == 2; } -static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr, - unsigned size) -{ - return fw_cfg_read(opaque); -} - static void fw_cfg_comb_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -483,7 +479,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = { }; static const MemoryRegionOps fw_cfg_data_mem_ops = { - .read = fw_cfg_data_mem_read, + .read = fw_cfg_data_read, .write = fw_cfg_data_mem_write, .endianness = DEVICE_BIG_ENDIAN, .valid = { @@ -494,7 +490,7 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = { }; static const MemoryRegionOps fw_cfg_comb_mem_ops = { - .read = fw_cfg_comb_read, + .read = fw_cfg_data_read, .write = fw_cfg_comb_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid.accepts = fw_cfg_comb_valid, @@ -513,7 +509,8 @@ static void fw_cfg_reset(DeviceState *d) { FWCfgState *s = FW_CFG(d); - fw_cfg_select(s, 0); + /* we never register a read callback for FW_CFG_SIGNATURE */ + fw_cfg_select(s, FW_CFG_SIGNATURE); } /* Save restore 32 bit int as uint16_t |