Re: [Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
On Sun, 16 Dec 2018 at 06:20, Stefan Hajnoczi wrote: > > On Mon, Nov 26, 2018 at 05:43:59PM +, Peter Maydell wrote: > > On Mon, 26 Nov 2018 at 00:24, Steffen Görtz wrote: > > > What else is necessary to invalidate stale blocks? > > > > You need an AddressSpace that points to the MemoryRegion(s) > > you're altering, and you need to use the operations on > > the AddressSpace like address_space_write(). These will > > under the hood do the right thing with TB invalidation. > > I'm not sure about this. > I see nothing that invalidates TBs in the address_space_write() code for > MMIO memory regions (not RAM). Only the RAM case calls > invalidate_and_set_dirty(). Hmm, I think I see what you mean -- the problem is because your ram backed region is created with memory_region_init_rom_device(), which means that if you try to write to the MR via address_space_write() you get the guest access path which ends up in the MemoryRegionOps write function, whereas what we wanted was to access the MR via the write-host-RAM code path which updates the dirty state. > There are a few complications with this device: > > 1. Stores from the CPU are only honored when the NRF51_NVMC_CONFIG_WEN >write enable bit is set. NRF51_NVMC_ERASEPCRx and >NRF51_NVMC_ERASEALL commands use a separate erase enable bit >(NRF51_NVMC_CONFIG_EEN) and are therefore different from normal >writes. i.e. we want guest writes to the region to go via a write function so we can impose the right semantics. > 2. Stores from the CPU can only flip 1s to 0s (this is NOR flash). When >we erase a page of flash memory it must be set to 0xff (i.e. flip >0s to 1s). i.e. we have some other operations which need to perform writes directly on the underlying storage. > 3. nrf51_nvm.c:flash_write() does not mark the page dirty for live >migration. ...and this is the bug we need to avoid: any write to the underlying storage must update dirty status, both for migration and for TCG cached code invalidation. > My questions: > > 1. Is the current rom+mmio device approach okay or should it be modelled >differently? > > 2. Erase operations cannot use ordinary address_space_write() for the >reasons mentioned above. Should this device directly call >cpu_physical_memory_set_dirty_range() and tb_invalidate_phys_range()? I think that these functions are too low level for devices to be calling directly, and we should have something at the MemoryRegion or AddressSpace level to do what we need. Perhaps some API for "this is a rom_device MemoryRegion, create me a new MemoryRegion which is a pure RAM MemoryRegion view of the underlying storage". (The device could then put that into a private AddressSpace and use that for when it wants to do updates directly to storage.) Or perhaps we could just say "practically all uses of rom_device MemoryRegions will want to be able to write to the underlying storage, we should provide an API that handles flushing for them". Paolo, do you have a view? (Our only other users of rom_device are hw/block/pflash_cfi0[12].c; I think they get away with this because they mostly flip the device out of romd mode before updating the backing storage. That works but is very heavyweight, and is almost working by accident rather than design.) thanks -- PMM
Re: [Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
On Mon, Nov 26, 2018 at 05:43:59PM +, Peter Maydell wrote: > On Mon, 26 Nov 2018 at 00:24, Steffen Görtz wrote: > > > > Hi Peter, > > > > thank you for your remarks! > > > > >> +}; > > >> + > > >> +static uint64_t ficr_read(void *opaque, hwaddr offset > > > > > >> +value &= ~(NRF51_PAGE_SIZE - 1); > > >> +if (value < (s->flash_size - NRF51_PAGE_SIZE)) { > > >> +memset(s->storage + value / 4, 0xFF, NRF51_PAGE_SIZE); > > > > > > Can the guest try to execute from the flash storage? If so > > > then just updating the backing storage directly like this is > > > not sufficient to ensure that QEMU discards any now-stale > > > translated code blocks from the affected memory. > > > > What else is necessary to invalidate stale blocks? > > You need an AddressSpace that points to the MemoryRegion(s) > you're altering, and you need to use the operations on > the AddressSpace like address_space_write(). These will > under the hood do the right thing with TB invalidation. I'm not sure about this. The memory region looks like this: {parent_obj = {class = 0x565ee350, free = 0x0, Python Exception There is no member named keys.: properties = 0x5672f860, ref = 1, parent = 0x566620f0}, romd_mode = true, ram = false, subpage = false, readonly = false, nonvolatile = false, rom_device = true, flush_coalesced_mmio = false, global_locking = true, dirty_log_mask = 0 '\000', is_iommu = false, ram_block = 0x56768b40, owner = 0x566620f0, ops = 0x5615d360 , opaque = 0x566620f0, container = 0x0, size = 262144, addr = 0, destructor = 0x55893f00 , align = 2097152, terminates = true, ram_device = false, enabled = true, warning_printed = false, vga_logging_count = 0 '\000', alias = 0x0, alias_offset = 0, priority = 0, subregions = { tqh_first = 0x0, tqh_last = 0x56662778}, subregions_link = {tqe_next = 0x0, tqe_prev = 0x0}, coalesced = {tqh_first = 0x0, tqh_last = 0x56662798}, name = 0x568033d0 "nrf51_soc.flash", ioeventfd_nb = 0, ioeventfds = 0x0} I see nothing that invalidates TBs in the address_space_write() code for MMIO memory regions (not RAM). Only the RAM case calls invalidate_and_set_dirty(). There are a few complications with this device: 1. Stores from the CPU are only honored when the NRF51_NVMC_CONFIG_WEN write enable bit is set. NRF51_NVMC_ERASEPCRx and NRF51_NVMC_ERASEALL commands use a separate erase enable bit (NRF51_NVMC_CONFIG_EEN) and are therefore different from normal writes. 2. Stores from the CPU can only flip 1s to 0s (this is NOR flash). When we erase a page of flash memory it must be set to 0xff (i.e. flip 0s to 1s). 3. nrf51_nvm.c:flash_write() does not mark the page dirty for live migration. My questions: 1. Is the current rom+mmio device approach okay or should it be modelled differently? 2. Erase operations cannot use ordinary address_space_write() for the reasons mentioned above. Should this device directly call cpu_physical_memory_set_dirty_range() and tb_invalidate_phys_range()? signature.asc Description: PGP signature
Re: [Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
On Mon, 26 Nov 2018 at 00:24, Steffen Görtz wrote: > > Hi Peter, > > thank you for your remarks! > > >> +}; > >> + > >> +static uint64_t ficr_read(void *opaque, hwaddr offset > > > >> +value &= ~(NRF51_PAGE_SIZE - 1); > >> +if (value < (s->flash_size - NRF51_PAGE_SIZE)) { > >> +memset(s->storage + value / 4, 0xFF, NRF51_PAGE_SIZE); > > > > Can the guest try to execute from the flash storage? If so > > then just updating the backing storage directly like this is > > not sufficient to ensure that QEMU discards any now-stale > > translated code blocks from the affected memory. > > What else is necessary to invalidate stale blocks? You need an AddressSpace that points to the MemoryRegion(s) you're altering, and you need to use the operations on the AddressSpace like address_space_write(). These will under the hood do the right thing with TB invalidation. > >> +static void nrf51_nvm_reset(DeviceState *dev) > >> +{ > >> +NRF51NVMState *s = NRF51_NVM(dev); > >> + > >> +s->config = 0x00; > > > > Shouldn't uicr_content[] and storage be reset too ? > > > Storage and uicr_content should be retained during a device reset. If you want storage that lives over system reset, that is painful, because it means you need to back it with a block device (like we do for disks and for flash memory). (This is because QEMU's system reset is a hard-power-cycle; it should be equivalent to stopping QEMU entirely and then restarting it, so any persistent storage needs to be in a block device somewhere.) thanks -- PMM
Re: [Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
Hi Peter, thank you for your remarks! >> +}; >> + >> +static uint64_t ficr_read(void *opaque, hwaddr offset > >> +value &= ~(NRF51_PAGE_SIZE - 1); >> +if (value < (s->flash_size - NRF51_PAGE_SIZE)) { >> +memset(s->storage + value / 4, 0xFF, NRF51_PAGE_SIZE); > > Can the guest try to execute from the flash storage? If so > then just updating the backing storage directly like this is > not sufficient to ensure that QEMU discards any now-stale > translated code blocks from the affected memory. What else is necessary to invalidate stale blocks? >> + >> +static void nrf51_nvm_reset(DeviceState *dev) >> +{ >> +NRF51NVMState *s = NRF51_NVM(dev); >> + >> +s->config = 0x00; > > Shouldn't uicr_content[] and storage be reset too ? Storage and uicr_content should be retained during a device reset. Best, Steffen
Re: [Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
On 12 November 2018 at 21:42, Steffen Görtz wrote: > The nRF51 contains three regions of non-volatile memory (NVM): > - CODE (R/W): contains code > - FICR (R): Factory information like code size, chip id etc. > - UICR (R/W): Changeable configuration data. Lock bits, Code > protection configuration, Bootloader address, Nordic SoftRadio > configuration, Firmware configuration. > > Read and write access to the memories is managed by the > Non-volatile memory controller. > > Memory schema: > [ CPU ] -+- [ NVM, either FICR, UICR or CODE ] > | | > \- [ NVMC ] > > Signed-off-by: Steffen Görtz Hi; I have some comments on this patch, but they're mostly fairly minor. > --- > hw/nvram/Makefile.objs | 1 + > hw/nvram/nrf51_nvm.c | 378 +++ > include/hw/nvram/nrf51_nvm.h | 64 ++ > 3 files changed, 443 insertions(+) > create mode 100644 hw/nvram/nrf51_nvm.c > create mode 100644 include/hw/nvram/nrf51_nvm.h > > diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs > index a912d25391..3f978e6212 100644 > --- a/hw/nvram/Makefile.objs > +++ b/hw/nvram/Makefile.objs > @@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o > common-obj-y += chrp_nvram.o > common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o > obj-$(CONFIG_PSERIES) += spapr_nvram.o > +obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o > diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c > new file mode 100644 > index 00..b3702ebe5e > --- /dev/null > +++ b/hw/nvram/nrf51_nvm.c > @@ -0,0 +1,378 @@ > +/* > + * Nordic Semiconductor nRF51 non-volatile memory > + * > + * It provides an interface to erase regions in flash memory. > + * Furthermore it provides the user and factory information registers. > + * > + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf > + * > + * See nRF51 reference manual and product sheet sections: > + * + Non-Volatile Memory Controller (NVMC) > + * + Factory Information Configuration Registers (FICR) > + * + User Information Configuration Registers (UICR) > + * > + * Copyright 2018 Steffen Görtz > + * > + * This code is licensed under the GPL version 2 or later. See > + * the COPYING file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > +#include "exec/address-spaces.h" > +#include "hw/arm/nrf51.h" > +#include "hw/nvram/nrf51_nvm.h" > + > +/* > + * FICR Registers Assignments > + * CODEPAGESIZE 0x010 > + * CODESIZE 0x014 > + * CLENR00x028 > + * PPFC 0x02C > + * NUMRAMBLOCK 0x034 > + * SIZERAMBLOCKS 0x038 > + * SIZERAMBLOCK[0] 0x038 > + * SIZERAMBLOCK[1] 0x03C > + * SIZERAMBLOCK[2] 0x040 > + * SIZERAMBLOCK[3] 0x044 > + * CONFIGID 0x05C > + * DEVICEID[0] 0x060 > + * DEVICEID[1] 0x064 > + * ER[0] 0x080 > + * ER[1] 0x084 > + * ER[2] 0x088 > + * ER[3] 0x08C > + * IR[0] 0x090 > + * IR[1] 0x094 > + * IR[2] 0x098 > + * IR[3] 0x09C > + * DEVICEADDRTYPE0x0A0 > + * DEVICEADDR[0] 0x0A4 > + * DEVICEADDR[1] 0x0A8 > + * OVERRIDEEN0x0AC > + * NRF_1MBIT[0] 0x0B0 > + * NRF_1MBIT[1] 0x0B4 > + * NRF_1MBIT[2] 0x0B8 > + * NRF_1MBIT[3] 0x0BC > + * NRF_1MBIT[4] 0x0C0 > + * BLE_1MBIT[0] 0x0EC > + * BLE_1MBIT[1] 0x0F0 > + * BLE_1MBIT[2] 0x0F4 > + * BLE_1MBIT[3] 0x0F8 > + * BLE_1MBIT[4] 0x0FC > + */ > +static const uint32_t ficr_content[64] = { > +0x, 0x, 0x, 0x, 0x0400, > +0x0100, 0x, 0x, 0x0002, 0x2000, > +0x2000, 0x2000, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x0003, > +0x12345678, 0x9ABCDEF1, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x, 0x, > +0x, 0x, 0x, 0x > +}; > + > +static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size) > +{ > +assert(offset <= sizeof(ficr_content)); This should be "<", not "<=". If the offset is equal to the size of the array then the access will be off the end. > +return ficr_content[offset / 4]; > +} > + > +static void ficr_write(void *opaque, hwaddr offset, uint64_t value, > +unsigned int size) > +{ > +/* Intentionally do nothing */ > +} > +static uint64_t uicr_read(void *opaque, hwaddr offset, unsigned int size) > +{ > +NRF51NVMState *s =
[Qemu-devel] [PATCH v5 05/14] hw/nvram/nrf51_nvm: Add nRF51 non-volatile memories
The nRF51 contains three regions of non-volatile memory (NVM): - CODE (R/W): contains code - FICR (R): Factory information like code size, chip id etc. - UICR (R/W): Changeable configuration data. Lock bits, Code protection configuration, Bootloader address, Nordic SoftRadio configuration, Firmware configuration. Read and write access to the memories is managed by the Non-volatile memory controller. Memory schema: [ CPU ] -+- [ NVM, either FICR, UICR or CODE ] | | \- [ NVMC ] Signed-off-by: Steffen Görtz --- hw/nvram/Makefile.objs | 1 + hw/nvram/nrf51_nvm.c | 378 +++ include/hw/nvram/nrf51_nvm.h | 64 ++ 3 files changed, 443 insertions(+) create mode 100644 hw/nvram/nrf51_nvm.c create mode 100644 include/hw/nvram/nrf51_nvm.h diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index a912d25391..3f978e6212 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -5,3 +5,4 @@ common-obj-y += fw_cfg.o common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o +obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c new file mode 100644 index 00..b3702ebe5e --- /dev/null +++ b/hw/nvram/nrf51_nvm.c @@ -0,0 +1,378 @@ +/* + * Nordic Semiconductor nRF51 non-volatile memory + * + * It provides an interface to erase regions in flash memory. + * Furthermore it provides the user and factory information registers. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * + * See nRF51 reference manual and product sheet sections: + * + Non-Volatile Memory Controller (NVMC) + * + Factory Information Configuration Registers (FICR) + * + User Information Configuration Registers (UICR) + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "exec/address-spaces.h" +#include "hw/arm/nrf51.h" +#include "hw/nvram/nrf51_nvm.h" + +/* + * FICR Registers Assignments + * CODEPAGESIZE 0x010 + * CODESIZE 0x014 + * CLENR00x028 + * PPFC 0x02C + * NUMRAMBLOCK 0x034 + * SIZERAMBLOCKS 0x038 + * SIZERAMBLOCK[0] 0x038 + * SIZERAMBLOCK[1] 0x03C + * SIZERAMBLOCK[2] 0x040 + * SIZERAMBLOCK[3] 0x044 + * CONFIGID 0x05C + * DEVICEID[0] 0x060 + * DEVICEID[1] 0x064 + * ER[0] 0x080 + * ER[1] 0x084 + * ER[2] 0x088 + * ER[3] 0x08C + * IR[0] 0x090 + * IR[1] 0x094 + * IR[2] 0x098 + * IR[3] 0x09C + * DEVICEADDRTYPE0x0A0 + * DEVICEADDR[0] 0x0A4 + * DEVICEADDR[1] 0x0A8 + * OVERRIDEEN0x0AC + * NRF_1MBIT[0] 0x0B0 + * NRF_1MBIT[1] 0x0B4 + * NRF_1MBIT[2] 0x0B8 + * NRF_1MBIT[3] 0x0BC + * NRF_1MBIT[4] 0x0C0 + * BLE_1MBIT[0] 0x0EC + * BLE_1MBIT[1] 0x0F0 + * BLE_1MBIT[2] 0x0F4 + * BLE_1MBIT[3] 0x0F8 + * BLE_1MBIT[4] 0x0FC + */ +static const uint32_t ficr_content[64] = { +0x, 0x, 0x, 0x, 0x0400, +0x0100, 0x, 0x, 0x0002, 0x2000, +0x2000, 0x2000, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x0003, +0x12345678, 0x9ABCDEF1, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x, 0x, +0x, 0x, 0x, 0x +}; + +static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size) +{ +assert(offset <= sizeof(ficr_content)); +return ficr_content[offset / 4]; +} + +static void ficr_write(void *opaque, hwaddr offset, uint64_t value, +unsigned int size) +{ +/* Intentionally do nothing */ +} + +static const MemoryRegionOps ficr_ops = { +.read = ficr_read, +.write = ficr_write, +.impl.min_access_size = 4, +.impl.max_access_size = 4, +.endianness = DEVICE_LITTLE_ENDIAN +}; + +/* + * UICR Registers Assignments + * CLENR0 0x000 + * RBPCONF 0x004 + * XTALFREQ 0x008 + * FWID 0x010 + * BOOTLOADERADDR 0x014 + * NRFFW[0] 0x014 + * NRFFW[1] 0x018 + * NRFFW[2] 0x01C + * NRFFW[3] 0x020 + * NRFFW[4] 0x024 + * NRFFW[5] 0x028 + * NRFFW[6] 0x02C + * NRFFW[7] 0x030 + * NRFFW[8] 0x034 +