Hi,

On 2/14/20 1:47 PM, Andre Przywara wrote:
> On Fri, 7 Feb 2020 17:34:20 +0000
> Alexandru Elisei <alexandru.eli...@arm.com> wrote:
>
> Hi Alex,
>
> many thanks for having a look!
>
>> I'm going to do my best to review your patch :) I'll do it in chunks, 
>> because it's
>> pretty large, and definitely not trivial.
> OK, replying here, and having it mostly fixed already.
> Will wait for further replies before a re-post, unless you want to benefit 
> from the split MMIO function, which should make reviewing the state machine 
> easier. Just let me know.

I'll finish my review on this version of the patch, no need to post a v3.

>
>> On 2/7/20 12:19 PM, Andre Przywara wrote:
>>> From: Raphael Gault <raphael.ga...@arm.com>
>>>
>>> The EDK II UEFI firmware implementation requires some storage for the EFI
>>> variables, which is typically some flash storage.
>>> Since this is already supported on the EDK II side, we add a CFI flash
>>> emulation to kvmtool.
>>> This is backed by a file, specified via the --flash or -F command line
>>> option. Any flash writes done by the guest will immediately be reflected
>>> into this file (kvmtool mmap's the file).
>>>
>>> This implements a CFI flash using the "Intel/Sharp extended command
>>> set", as specified in:
>>> - JEDEC JESD68.01
>>> - JEDEC JEP137B
>>> - Intel Application Note 646
>>> Some gaps in those specs have been filled by looking at real devices and
>>> other implementations (QEMU, Linux kernel driver).
>>>
>>> At the moment this relies on DT to advertise the base address of the
>>> flash memory (mapped into the MMIO address space) and is only enabled
>>> for ARM/ARM64. The emulation itself is architecture agnostic, though.
>>>
>>> This is one missing piece toward a working UEFI boot with kvmtool on
>>> ARM guests, the other is to provide writable PCI BARs, which is WIP.
>>>
>>> Signed-off-by: Raphael Gault <raphael.ga...@arm.com>
>>> [Andre: rewriting and fixing]
>>> Signed-off-by: Andre Przywra <andre.przyw...@arm.com>
>>> ---
>>> Hi,
>>>
>>> an update addressing Will's comments. I added coarse grained locking
>>> to the MMIO handler, to prevent concurrent vCPU accesses from messing up
>>> the internal CFI flash state machine.
>>> I also folded the actual flash array read access into the MMIO handler
>>> and fixed the other small issues.
>>>
>>> Cheers,
>>> Andre
>>>
>>>  Makefile                          |   6 +
>>>  arm/include/arm-common/kvm-arch.h |   3 +
>>>  builtin-run.c                     |   2 +
>>>  hw/cfi_flash.c                    | 546 ++++++++++++++++++++++++++++++
>>>  include/kvm/kvm-config.h          |   1 +
>>>  include/kvm/util.h                |   5 +
>>>  6 files changed, 563 insertions(+)
>>>  create mode 100644 hw/cfi_flash.c
>>>
>>> diff --git a/Makefile b/Makefile
>>> index 3862112c..7ed6fb5e 100644
>>> --- a/Makefile
>>> +++ b/Makefile
>>> @@ -170,6 +170,7 @@ ifeq ($(ARCH), arm)
>>>     CFLAGS          += -march=armv7-a
>>>  
>>>     ARCH_WANT_LIBFDT := y
>>> +   ARCH_HAS_FLASH_MEM := y
>>>  endif
>>>  
>>>  # ARM64
>>> @@ -182,6 +183,7 @@ ifeq ($(ARCH), arm64)
>>>     ARCH_INCLUDE    += -Iarm/aarch64/include
>>>  
>>>     ARCH_WANT_LIBFDT := y
>>> +   ARCH_HAS_FLASH_MEM := y
>>>  endif
>>>  
>>>  ifeq ($(ARCH),mips)
>>> @@ -261,6 +263,10 @@ ifeq (y,$(ARCH_HAS_FRAMEBUFFER))
>>>     endif
>>>  endif
>>>  
>>> +ifeq (y,$(ARCH_HAS_FLASH_MEM))
>>> +   OBJS    += hw/cfi_flash.o
>>> +endif
>>> +
>>>  ifeq ($(call try-build,$(SOURCE_ZLIB),$(CFLAGS),$(LDFLAGS) -lz),y)
>>>     CFLAGS_DYNOPT   += -DCONFIG_HAS_ZLIB
>>>     LIBS_DYNOPT     += -lz
>>> diff --git a/arm/include/arm-common/kvm-arch.h 
>>> b/arm/include/arm-common/kvm-arch.h
>>> index b9d486d5..2bb085f4 100644
>>> --- a/arm/include/arm-common/kvm-arch.h
>>> +++ b/arm/include/arm-common/kvm-arch.h
>>> @@ -21,6 +21,9 @@
>>>  #define ARM_GIC_DIST_SIZE  0x10000
>>>  #define ARM_GIC_CPUI_SIZE  0x20000
>>>  
>>> +#define ARM_FLASH_MMIO_BASE        0x2000000               /* 32 MB */
>>> +#define KVM_FLASH_MMIO_BASE        ARM_FLASH_MMIO_BASE  
>> Each time I try to read the memory layout for ARM I get a headache. 
>> According to
>> my calculations, this falls right inside ARM_MMIO_AREA, right? Any particular
>> reason for choosing this address? Why not carve its own dedicate area, so we 
>> won't
>> run the highly unlikely risk that it will be overwritten, since it's in the 
>> MMIO
>> allocation area?
> The EDK2 build I used has the base address fixed at 32MB. So I just used this 
> address here. Sami is working on making this flexible as we speak, but it's 
> not easy due to some EDK-2 design issues.
> As an interim measure I would try to describe this using the existing MMIO 
> layout macros, to at least avoid overlaps with virtio-mmio.
> I actually might move that address to the beginning for now, as 32MB is 
> currently in the middle of the MMIO area.
> QEMU has that hardcoded (both in QEMU and EDK-2) as well, btw.
>  
>>> +
>>>  #define ARM_IOPORT_SIZE            (ARM_MMIO_AREA - ARM_IOPORT_AREA)
>>>  #define ARM_VIRTIO_MMIO_SIZE       (ARM_AXI_AREA - (ARM_MMIO_AREA + 
>>> ARM_GIC_SIZE))  
>> That's not correct anymore, because flash memory is in the ARM_MMIO_AREA.
> True, I will try to find the right place for this. Somewhat problematic is 
> the differing size, but we could just impose an upper limit on this.

From this and the above, it kinda sounds like we a flexible memory layout for
kvmtool, doesn't it? ;-)

>
>>>  #define ARM_PCI_CFG_SIZE   (1ULL << 24)
>>> diff --git a/builtin-run.c b/builtin-run.c
>>> index f8dc6c72..df8c6741 100644
>>> --- a/builtin-run.c
>>> +++ b/builtin-run.c
>>> @@ -138,6 +138,8 @@ void kvm_run_set_wrapper_sandbox(void)
>>>                     "Kernel command line arguments"),               \
>>>     OPT_STRING('f', "firmware", &(cfg)->firmware_filename, "firmware",\
>>>                     "Firmware image to boot in virtual machine"),   \
>>> +   OPT_STRING('F', "flash", &(cfg)->flash_filename, "flash",\
>>> +                   "Flash image to present to virtual machine"),   \
>>>                                                                     \
>>>     OPT_GROUP("Networking options:"),                               \
>>>     OPT_CALLBACK_DEFAULT('n', "network", NULL, "network params",    \
>>> diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c
>>> new file mode 100644
>>> index 00000000..d7c0e7e8
>>> --- /dev/null
>>> +++ b/hw/cfi_flash.c
>>> @@ -0,0 +1,546 @@
>>> +#include <stdbool.h>
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/err.h>
>>> +#include <linux/sizes.h>
>>> +#include <linux/types.h>
>>> +
>>> +#include "kvm/kvm.h"
>>> +#include "kvm/kvm-arch.h"
>>> +#include "kvm/devices.h"
>>> +#include "kvm/fdt.h"
>>> +#include "kvm/mutex.h"
>>> +#include "kvm/util.h"
>>> +
>>> +/* The EDK2 driver hardcodes two 16-bit chips on a 32-bit bus. */
>>> +#define CFI_NR_FLASH_CHIPS                 2
>>> +
>>> +/* We always emulate a 32 bit bus width. */
>>> +#define CFI_BUS_WIDTH                              4
>>> +
>>> +/* The *effective* size of an erase block (over all chips) */
>>> +#define FLASH_BLOCK_SIZE                   SZ_64K
>>> +
>>> +#define PROGRAM_BUFF_SIZE_BITS                     7
>>> +#define PROGRAM_BUFF_SIZE                  (1U << PROGRAM_BUFF_SIZE_BITS)  
>> Just making sure this is not an off-by-one error. The buffer size is 2^7 = 
>> 128
>> words, which makes it 512 bytes, right?
> Looks like it ;-)
> The reason this is presented in this rather awkward way here is that we need 
> the number of bits to be presented in the CFI query structure later on.
> I will add a comment pointing out this is in units of "words" - after double 
> checking that it really is ;-)
>  
>>> +
>>> +/* CFI commands */
>>> +#define CFI_CMD_LOCK_BLOCK                 0x01
>>> +#define CFI_CMD_ALTERNATE_WORD_PROGRAM_SETUP       0x10
>>> +#define CFI_CMD_BLOCK_ERASE_SETUP          0x20
>>> +#define CFI_CMD_WORD_PROGRAM_SETUP         0x40
>>> +#define CFI_CMD_CLEAR_STATUS_REGISTER              0x50
>>> +#define CFI_CMD_LOCK_BLOCK_SETUP           0x60
>>> +#define CFI_CMD_READ_STATUS_REGISTER               0x70
>>> +#define CFI_CMD_READ_JEDEC                 0x90
>>> +#define CFI_CMD_READ_CFI_QUERY                     0x98
>>> +#define CFI_CMD_BUFFERED_PROGRAM_CONFIRM   0xd0
>>> +#define CFI_CMD_BLOCK_ERASE_CONFIRM                0xd0
>>> +#define CFI_CMD_UNLOCK_BLOCK                       0xd0
>>> +#define CFI_CMD_BUFFERED_PROGRAM_SETUP             0xe8
>>> +#define CFI_CMD_READ_ARRAY                 0xff
>>> +
>>> +/*
>>> + * CFI query table contents, as far as it is constant.
>>> + */
>>> +#define CFI_GEOM_OFFSET                            0x27
>>> +static u8 cfi_query_table[] = {
>>> +           /* offset 0x10: CFI query identification string */
>>> +   'Q', 'R', 'Y',          /* ID string */
>>> +   0x01, 0x00,             /* primary command set: Intel/Sharp extended */
>>> +   0x31, 0x00,             /* address of primary extended query table */
>>> +   0x00, 0x00,             /* alternative command set: unused */
>>> +   0x00, 0x00,             /* address of alternative extended query table*/
>>> +           /* offset 0x1b: system interface information */
>>> +   0x45,                   /* minimum Vcc voltage: 4.5V */
>>> +   0x55,                   /* maximum Vcc voltage: 5.5V */
>>> +   0x00,                   /* minimum Vpp voltage: 0.0V (unused) */
>>> +   0x00,                   /* maximum Vpp voltage: 0.0V *(unused) */
>>> +   0x01,                   /* timeout for single word program: 2 us */
>>> +   0x01,                   /* timeout for multi-byte program: 2 us */
>>> +   0x01,                   /* timeout for block erase: 2 ms */
>>> +   0x00,                   /* timeout for full chip erase: not supported */
>>> +   0x00,                   /* max timeout for single word program: 1x */
>>> +   0x00,                   /* max timeout for mulit-byte program: 1x */
>>> +   0x00,                   /* max timeout for block erase: 1x */
>>> +   0x00,                   /* max timeout for chip erase: not supported */
>>> +           /* offset 0x27: flash geometry information */
>>> +   0x00,                   /* size in power-of-2 bytes, filled later */
>>> +   0x06, 0x00,             /* interface description: 32 and 16 bits */
>>> +   PROGRAM_BUFF_SIZE_BITS + 1 - CFI_NR_FLASH_CHIPS, 0x00,
>>> +                           /* number of multi-byte writes */
>>> +   0x01,                   /* one erase block region */
>>> +   0x00, 0x00, 0x00, 0x00, /* number and size of erase blocks, filled */
>>> +           /* offset 0x31: Intel primary algorithm extended query table */
>>> +   'P', 'R', 'I',
>>> +   '1', '0',               /* version 1.0 */
>>> +   0xa0, 0x00, 0x00, 0x00, /* optional features: instant lock & pm-read */
>>> +   0x00,                   /* no functions after suspend */
>>> +   0x01, 0x00,             /* only lock bit supported */
>>> +   0x50,                   /* best Vcc value: 5.0V */
>>> +   0x00,                   /* best Vpp value: 0.0V (unused) */
>>> +   0x01,                   /* number of protection register fields */
>>> +   0x00, 0x00, 0x00, 0x00, /* protection field 1 description */
>>> +};
>>> +
>>> +
>>> +/*
>>> + * Those states represent a subset of the CFI flash state machine.
>>> + */
>>> +enum cfi_flash_state {
>>> +   READY,
>>> +   LOCK_SETUP,
>>> +   WP_SETUP,
>>> +   BP_SETUP,
>>> +   BP_LOAD,
>>> +   ERASE_SETUP,
>>> +};
>>> +
>>> +/*
>>> + * The device can be in several **Read** modes.
>>> + * We don't implement the asynchronous burst mode.
>>> + */
>>> +enum cfi_read_mode {
>>> +   READ_ARRAY,
>>> +   READ_STATUS,
>>> +   READ_DEVICE_ID,
>>> +   READ_QUERY,
>>> +};
>>> +
>>> +struct cfi_flash_device {
>>> +   struct device_header    dev_hdr;
>>> +   /* Protects the CFI state machine variables in this data structure. */
>>> +   struct mutex            mutex;
>>> +   u64                     base_addr;
>>> +   u32                     size;
>>> +
>>> +   void                    *flash_memory;
>>> +   u8                      program_buffer[PROGRAM_BUFF_SIZE * 4];  
>> You're multiplying by 4 because PROGRAM_BUFF_SIZE is the size of the buffer 
>> in
>> words, right?
> Yeah, I can use "sizeof(u32)" if that is better.
>
>>> +   unsigned long           *lock_bm;
>>> +   u64                     last_address;
>>> +   unsigned int            buff_written;
>>> +   unsigned int            program_length;
>>> +
>>> +   enum cfi_flash_state    state;
>>> +   enum cfi_read_mode      read_mode;
>>> +   u16                     rcr;
>>> +   u8                      sr;
>>> +};
>>> +
>>> +static int nr_erase_blocks(struct cfi_flash_device *sfdev)
>>> +{
>>> +   return sfdev->size / FLASH_BLOCK_SIZE;
>>> +}
>>> +
>>> +/*
>>> + * CFI queries always deal with one byte of information, possibly mirrored
>>> + * to other bytes on the bus. This is dealt with in the callers.
>>> + * The address provided is the one for 8-bit addressing, and would need to
>>> + * be adjusted for wider accesses.
>>> + */
>>> +static u8 read_cfi(struct cfi_flash_device *sfdev, u64 addr)
>>> +{
>>> +   if (addr < 0x10)                /* CFI information starts at 0x10 */
>>> +           return 0;
>>> +
>>> +   if (addr - 0x10 > sizeof(cfi_query_table)) {
>>> +           pr_debug("CFI query read access beyond the end of table");
>>> +           return 0;
>>> +   }
>>> +
>>> +   /* Fixup dynamic information in the geometry part of the table. */
>>> +   switch (addr) {
>>> +   case CFI_GEOM_OFFSET:           /* device size in bytes, power of two */
>>> +           return pow2_size(sfdev->size / CFI_NR_FLASH_CHIPS);
>>> +   case CFI_GEOM_OFFSET + 6:       /* number of erase blocks, minus one */
>>> +           return (nr_erase_blocks(sfdev) - 1) & 0xff;
>>> +   case CFI_GEOM_OFFSET + 7:
>>> +           return (nr_erase_blocks(sfdev) - 1) >> 8;
>>> +   case CFI_GEOM_OFFSET + 8:       /* erase block size, in units of 256 */
>>> +           return ((FLASH_BLOCK_SIZE / 256 ) / CFI_NR_FLASH_CHIPS) & 0xff;
>>> +   case CFI_GEOM_OFFSET + 9:
>>> +           return ((FLASH_BLOCK_SIZE / 256 ) / CFI_NR_FLASH_CHIPS) >> 8;
>>> +   }
>>> +
>>> +   return cfi_query_table[addr - 0x10];
>>> +}
>>> +
>>> +static bool block_is_locked(struct cfi_flash_device *sfdev, u64 addr)
>>> +{
>>> +   int block_nr = addr / FLASH_BLOCK_SIZE;
>>> +
>>> +   return test_bit(block_nr, sfdev->lock_bm);
>>> +}
>>> +
>>> +#define DEV_ID_MASK 0x7ff
>>> +static u16 read_dev_id(struct cfi_flash_device *sfdev, u64 addr)
>>> +{
>>> +   switch ((addr & DEV_ID_MASK) / CFI_BUS_WIDTH) {
>>> +   case 0x0:                               /* vendor ID */
>>> +           return 0x0000;
>>> +   case 0x1:                               /* device ID */
>>> +           return 0xffff;
>>> +   case 0x2:
>>> +           return block_is_locked(sfdev, addr & ~DEV_ID_MASK);
>>> +   case 0x5:
>>> +           return sfdev->rcr;
>>> +   default:                        /* Ignore the other entries. */
>>> +           return 0;
>>> +   }
>>> +}
>>> +
>>> +static void lock_block(struct cfi_flash_device *sfdev, u64 addr, bool lock)
>>> +{
>>> +   int block_nr = addr / FLASH_BLOCK_SIZE;
>>> +
>>> +   if (lock)
>>> +           set_bit(block_nr, sfdev->lock_bm);
>>> +   else
>>> +           clear_bit(block_nr, sfdev->lock_bm);
>>> +}
>>> +
>>> +static void word_program(struct cfi_flash_device *sfdev,
>>> +                    u64 addr, void *data, int len)
>>> +{
>>> +   if (block_is_locked(sfdev, addr)) {
>>> +           sfdev->sr |= 0x12;
>>> +           return;
>>> +   }
>>> +
>>> +   memcpy(sfdev->flash_memory + addr, data, len);
>>> +}
>>> +
>>> +/* Reset the program buffer state to prepare for follow-up writes. */
>>> +static void buffer_setup(struct cfi_flash_device *sfdev)
>>> +{
>>> +   memset(sfdev->program_buffer, 0, sizeof(sfdev->program_buffer));
>>> +   sfdev->last_address = ~0ULL;
>>> +   sfdev->buff_written = 0;
>>> +}
>>> +
>>> +static bool buffer_program(struct cfi_flash_device *sfdev,
>>> +                      u64 addr, void *buffer, int len)
>>> +{
>>> +   unsigned int buf_addr;
>>> +
>>> +   if (sfdev->buff_written >= sfdev->program_length)
>>> +           return false;
>>> +
>>> +   /*
>>> +    * The first word written into the buffer after the setup command
>>> +    * happens to be the base address for the buffer.
>>> +    * All subsequent writes need to be within this address and this
>>> +    * address plus the buffer size, so keep this value around.
>>> +    */
>>> +   if (sfdev->last_address == ~0ULL)
>>> +           sfdev->last_address = addr;
>>> +
>>> +   if (addr < sfdev->last_address)
>>> +           return false;
>>> +   buf_addr = addr - sfdev->last_address;
>>> +   if (buf_addr >= PROGRAM_BUFF_SIZE)
>>> +           return false;
>>> +
>>> +   memcpy(sfdev->program_buffer + buf_addr, buffer, len);
>>> +   sfdev->buff_written++;
>>> +
>>> +   return true;
>>> +}
>>> +
>>> +static void buffer_confirm(struct cfi_flash_device *sfdev)
>>> +{
>>> +   if (block_is_locked(sfdev, sfdev->last_address)) {
>>> +           sfdev->sr |= 0x12;
>>> +           return;
>>> +   }
>>> +   memcpy(sfdev->flash_memory + sfdev->last_address,
>>> +          sfdev->program_buffer,
>>> +          sfdev->buff_written * sizeof(u32));
>>> +}
>>> +
>>> +static void block_erase_confirm(struct cfi_flash_device *sfdev, u64 addr)
>>> +{
>>> +   if (block_is_locked(sfdev, addr)) {
>>> +           sfdev->sr |= 0x12;
>>> +           return;
>>> +   }
>>> +
>>> +   memset(sfdev->flash_memory + addr, 0xFF, FLASH_BLOCK_SIZE);
>>> +}
>>> +
>>> +static void cfi_flash_mmio(struct kvm_cpu *vcpu,
>>> +                      u64 addr, u8 *data, u32 len, u8 is_write,
>>> +                      void *context)
>>> +{
>>> +   struct cfi_flash_device *sfdev = context;
>>> +   u64 faddr = addr - sfdev->base_addr;
>>> +   u32 value;
>>> +
>>> +   if (!is_write) {
>>> +           u16 cfi_value = 0;
>>> +
>>> +           mutex_lock(&sfdev->mutex);
>>> +
>>> +           switch (sfdev->read_mode) {
>>> +           case READ_ARRAY:
>>> +                   /* just copy the requested bytes from the array */
>>> +                   memcpy(data, sfdev->flash_memory + faddr, len);
>>> +                   goto out_unlock;
>>> +           case READ_STATUS:
>>> +                   cfi_value = sfdev->sr;
>>> +                   break;
>>> +           case READ_DEVICE_ID:
>>> +                   cfi_value = read_dev_id(sfdev, faddr);
>>> +                   break;
>>> +           case READ_QUERY:
>>> +                   cfi_value = read_cfi(sfdev, faddr / CFI_BUS_WIDTH);
>>> +                   break;
>>> +           }
>>> +           switch (len) {
>>> +           case 1:
>>> +                   *data = cfi_value;
>>> +                   break;
>>> +           case 8: memset(data + 4, 0, 4);
>>> +                   /* fall-through */
>>> +           case 4:
>>> +                   if (CFI_NR_FLASH_CHIPS == 2)
>>> +                           memcpy(data + 2, &cfi_value, 2);
>>> +                   else
>>> +                           memset(data + 2, 0, 2);
>>> +                   /* fall-through */
>>> +           case 2:
>>> +                   memcpy(data, &cfi_value, 2);
>>> +                   break;
>>> +           default:
>>> +                   pr_debug("CFI flash: illegal access length %d for read 
>>> mode %d",
>>> +                            len, sfdev->read_mode);
>>> +                   break;
>>> +           }
>>> +
>>> +           goto out_unlock;
>>> +   }
>>> +
>>> +   if (len > 4) {
>>> +           pr_info("CFI flash: MMIO %d-bit write access not supported",
>>> +                    len * 8);
>>> +           return;
>>> +   }
>>> +
>>> +   memcpy(&value, data, len);
>>> +
>>> +   mutex_lock(&sfdev->mutex);
>>> +
>>> +   switch (sfdev->state) {
>>> +   case READY:                     /* handled below */
>>> +           break;
>>> +
>>> +   case LOCK_SETUP:
>>> +           switch (value & 0xff) {
>>> +           case CFI_CMD_LOCK_BLOCK:
>>> +                   lock_block(sfdev, faddr, true);
>>> +                   sfdev->read_mode = READ_STATUS;
>>> +                   break;
>>> +           case CFI_CMD_UNLOCK_BLOCK:
>>> +                   lock_block(sfdev, faddr, false);
>>> +                   sfdev->read_mode = READ_STATUS;
>>> +                   break;
>>> +           default:
>>> +                   sfdev->sr |= 0x30;
>>> +                   break;
>>> +           }
>>> +           sfdev->state = READY;
>>> +           goto out_unlock;
>>> +
>>> +   case WP_SETUP:
>>> +           word_program(sfdev, faddr, data, len);
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           sfdev->state = READY;
>>> +           goto out_unlock;
>>> +
>>> +   case BP_LOAD:
>>> +           if (buffer_program(sfdev, faddr, data, len))
>>> +                   goto out_unlock;
>>> +
>>> +           if ((value & 0xFF) == CFI_CMD_BUFFERED_PROGRAM_CONFIRM) {
>>> +                   buffer_confirm(sfdev);
>>> +                   sfdev->read_mode = READ_STATUS;
>>> +           } else {
>>> +                   pr_debug("CFI flash: BP_LOAD: expected CONFIRM(0xd0), 
>>> got 0x%x @ 0x%llx",
>>> +                            value, faddr);
>>> +                   sfdev->sr |= 0x10;
>>> +           }
>>> +           sfdev->state = READY;
>>> +           goto out_unlock;
>>> +
>>> +   case BP_SETUP:
>>> +           sfdev->program_length = (value & 0xffff) + 1;
>>> +           if (sfdev->program_length > PROGRAM_BUFF_SIZE / 4)
>>> +                   sfdev->program_length = PROGRAM_BUFF_SIZE / 4;
>>> +           sfdev->state = BP_LOAD;
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           goto out_unlock;
>>> +
>>> +   case ERASE_SETUP:
>>> +           if ((value & 0xff) == CFI_CMD_BLOCK_ERASE_CONFIRM)
>>> +                   block_erase_confirm(sfdev, faddr);
>>> +           else
>>> +                   sfdev->sr |= 0x30;
>>> +
>>> +           sfdev->state = READY;
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           goto out_unlock;
>>> +   }
>>> +
>>> +   /* write commands in READY state */
>>> +   switch (value & 0xFF) {
>>> +   case CFI_CMD_READ_JEDEC:
>>> +           sfdev->read_mode = READ_DEVICE_ID;
>>> +           break;
>>> +   case CFI_CMD_READ_STATUS_REGISTER:
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           break;
>>> +   case CFI_CMD_READ_CFI_QUERY:
>>> +           sfdev->read_mode = READ_QUERY;
>>> +           break;
>>> +   case CFI_CMD_CLEAR_STATUS_REGISTER:
>>> +           sfdev->sr = 0x80;
>>> +           break;
>>> +   case CFI_CMD_WORD_PROGRAM_SETUP:
>>> +   case CFI_CMD_ALTERNATE_WORD_PROGRAM_SETUP:
>>> +           sfdev->state = WP_SETUP;
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           break;
>>> +   case CFI_CMD_LOCK_BLOCK_SETUP:
>>> +           sfdev->state = LOCK_SETUP;
>>> +           break;
>>> +   case CFI_CMD_BLOCK_ERASE_SETUP:
>>> +           sfdev->state = ERASE_SETUP;
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           break;
>>> +   case CFI_CMD_BUFFERED_PROGRAM_SETUP:
>>> +           buffer_setup(sfdev);
>>> +           sfdev->state = BP_SETUP;
>>> +           sfdev->read_mode = READ_STATUS;
>>> +           break;
>>> +   case CFI_CMD_BUFFERED_PROGRAM_CONFIRM:
>>> +           pr_debug("CFI flash: unexpected confirm command 0xD0");
>>> +           break;
>>> +   default:
>>> +           pr_debug("CFI flash: unknown command 0x%x", value);
>>> +           /* fall through */  
>> Above (in the read case), you wrote it "fall-through".
> GCC has a list of allowed spellings, and both versions are in it ;-)
> But sure will fix this ...

I was commenting on the consistency. I don't have a preference for a particular
spelling.

>  
>>> +   case CFI_CMD_READ_ARRAY:
>>> +           sfdev->read_mode = READ_ARRAY;
>>> +           break;
>>> +   }
>>> +
>>> +out_unlock:
>>> +   mutex_unlock(&sfdev->mutex);
>>> +}  
>> The function is huge and complicated. How about splitting it into a read and 
>> write
>> function, at the very least?
> Good point. Looks like "write command in READY state" should be separate as 
> well, since it's only doing state transitions.

Good idea.

Thanks,
Alex
>
>>> +
>>> +#ifdef CONFIG_HAS_LIBFDT
>>> +static void generate_cfi_flash_fdt_node(void *fdt,
>>> +                                   struct device_header *dev_hdr,
>>> +                                   void (*generate_irq_prop)(void *fdt,
>>> +                                                             u8 irq,
>>> +                                                           enum irq_type))
>>> +{
>>> +   struct cfi_flash_device *sfdev;
>>> +   u64 reg_prop[2];
>>> +
>>> +   sfdev = container_of(dev_hdr, struct cfi_flash_device, dev_hdr);
>>> +   reg_prop[0] = cpu_to_fdt64(sfdev->base_addr);
>>> +   reg_prop[1] = cpu_to_fdt64(sfdev->size);
>>> +
>>> +   _FDT(fdt_begin_node(fdt, "flash"));
>>> +   _FDT(fdt_property_cell(fdt, "bank-width", CFI_BUS_WIDTH));
>>> +   _FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
>>> +   _FDT(fdt_property_cell(fdt, "#size-cells", 0x1));
>>> +   _FDT(fdt_property_string(fdt, "compatible", "cfi-flash"));
>>> +   _FDT(fdt_property_string(fdt, "label", "System-firmware"));
>>> +   _FDT(fdt_property(fdt, "reg", &reg_prop, sizeof(reg_prop)));
>>> +   _FDT(fdt_end_node(fdt));
>>> +}
>>> +#else
>>> +#define generate_cfi_flash_fdt_node NULL
>>> +#endif
>>> +
>>> +static struct cfi_flash_device *create_flash_device_file(struct kvm *kvm,
>>> +                                                    const char *filename)
>>> +{
>>> +   struct cfi_flash_device *sfdev;
>>> +   struct stat statbuf;  
>> Here you're using "buf" as shorthand for "buffer", but at the top of the file
>> (PROGRAM_BUFF_*) you use "buff".
> I guess because one was written by me, the other by Raphael ;-)
> Will consolidate this.
>
>>> +   unsigned int value;
>>> +   int ret;
>>> +   int fd;
>>> +
>>> +   fd = open(filename, O_RDWR);
>>> +   if (fd < 0)
>>> +           return ERR_PTR(-errno);
>>> +   if (fstat(fd, &statbuf) < 0) {
>>> +           close(fd);
>>> +           return ERR_PTR(-errno);
>>> +   }
>>> +
>>> +   sfdev = malloc(sizeof(struct cfi_flash_device));
>>> +   if (!sfdev) {
>>> +           close(fd);
>>> +           return ERR_PTR(-ENOMEM);
>>> +   }
>>> +
>>> +   sfdev->size = (statbuf.st_size + 4095) & ~0xfffUL;
>>> +   sfdev->flash_memory = mmap(NULL, statbuf.st_size,
>>> +                              PROT_READ | PROT_WRITE, MAP_SHARED,
>>> +                              fd, 0);
>>> +   if (sfdev->flash_memory == MAP_FAILED) {
>>> +           close(fd);
>>> +           free(sfdev);
>>> +           return ERR_PTR(-errno);
>>> +   }
>>> +   sfdev->base_addr = KVM_FLASH_MMIO_BASE;
>>> +   sfdev->state = READY;
>>> +   sfdev->read_mode = READ_ARRAY;
>>> +   sfdev->sr = 0x80;
>>> +   sfdev->rcr = 0xbfcf;
>>> +
>>> +   value = roundup(nr_erase_blocks(sfdev), BITS_PER_LONG) / 8;
>>> +   sfdev->lock_bm = malloc(value);
>>> +   memset(sfdev->lock_bm, 0, value);
>>> +
>>> +   sfdev->dev_hdr.bus_type = DEVICE_BUS_MMIO;
>>> +   sfdev->dev_hdr.data = generate_cfi_flash_fdt_node;
>>> +   mutex_init(&sfdev->mutex);
>>> +   ret = device__register(&sfdev->dev_hdr);
>>> +   if (ret) {
>>> +           free(sfdev->flash_memory);  
>> That's strange, I wrote a quick test for this and free'ing a a file-backed 
>> mmap'ed
>> memory resulted in a segmentation fault. Did you mean munmap?
> Ouch, indeed. Looks like a leftover from the original code, which was using 
> malloc().
>
>> Also, if mmap'ing
>> the flash fails, you close the file descriptor, which you don't do here. To 
>> be
>> honest, I think the best approach would be to add all the cleaning upĀ  after 
>> the
>> return statement and a series of labels to jump to depending where you got an
>> error (similar to virtio__pci_init).
> Yeah, it looks much better now that way.
>
>>> +           free(sfdev);
>>> +           return ERR_PTR(ret);
>>> +   }
>>> +
>>> +   ret = kvm__register_mmio(kvm,
>>> +                            sfdev->base_addr, sfdev->size,
>>> +                            false, cfi_flash_mmio, sfdev);
>>> +   if (ret) {
>>> +           device__unregister(&sfdev->dev_hdr);
>>> +           free(sfdev->flash_memory);
>>> +           free(sfdev);
>>> +           return ERR_PTR(ret);
>>> +   }
>>> +
>>> +   return sfdev;
>>> +}
>>> +
>>> +static int flash__init(struct kvm *kvm)
>>> +{
>>> +   struct cfi_flash_device *sfdev;
>>> +
>>> +   if (!kvm->cfg.flash_filename)
>>> +           return 0;
>>> +
>>> +   sfdev = create_flash_device_file(kvm, kvm->cfg.flash_filename);
>>> +   if (IS_ERR(sfdev))
>>> +           return PTR_ERR(sfdev);
>>> +
>>> +   return 0;
>>> +}
>>> +dev_init(flash__init);
>>> diff --git a/include/kvm/kvm-config.h b/include/kvm/kvm-config.h
>>> index a052b0bc..f4a8b831 100644
>>> --- a/include/kvm/kvm-config.h
>>> +++ b/include/kvm/kvm-config.h
>>> @@ -35,6 +35,7 @@ struct kvm_config {
>>>     const char *vmlinux_filename;
>>>     const char *initrd_filename;
>>>     const char *firmware_filename;
>>> +   const char *flash_filename;
>>>     const char *console;
>>>     const char *dev;
>>>     const char *network;
>>> diff --git a/include/kvm/util.h b/include/kvm/util.h
>>> index 4ca7aa93..5c37f0b7 100644
>>> --- a/include/kvm/util.h
>>> +++ b/include/kvm/util.h
>>> @@ -104,6 +104,11 @@ static inline unsigned long 
>>> roundup_pow_of_two(unsigned long x)
>>>     return x ? 1UL << fls_long(x - 1) : 0;
>>>  }
>>>  
>>> +static inline int pow2_size(unsigned long x)
>>> +{
>>> +   return (sizeof(x) * 8) - __builtin_clzl(x - 1);
>>> +}  
>> For the life of me I can't understand what this function is supposed to do. 
>> Also,
>> from the gcc online docs:
> The idea is to determine the "number of address bits needed to cover x bytes 
> of memory", which is something that is well known on actual hardware. I will 
> add a comment.
>  
>> "Returns the number of leading 0-bits in x, starting at the most significant 
>> bit
>> position. If xis 0, the result is undefined."
>>
>> you might want to add a special case for x == 1.
> Good point, although in our case the input value is always at least 2048. But 
> 0 isn't covered as well and also I moved this to generic code, so will fix it.
>
> Cheers,
> Andre
>
>> Thanks,
>> Alex
>>> +
>>>  struct kvm;
>>>  void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size);
>>>  void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, 
>>> u64 size);  
_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to