On Thu, 20 Feb 2020 10:24:13 +0000 Alexandru Elisei <alexandru.eli...@arm.com> wrote:
Hi, > On 2/19/20 5:26 PM, Andre Przywara wrote: > > On Mon, 17 Feb 2020 17:20:43 +0000 > > Alexandru Elisei <alexandru.eli...@arm.com> wrote: > > > > Hi, > > > >> I guess the device hasn't been tested with Linux. This is what I'm getting > >> when > >> trying to boot a Linux guest using the command: > > It was actually developed with a Linux guest, because that's more verbatim > > and easier to debug. > > > > And I just tested this again with Linux and it worked for me: > > The flash image you provided is 2 MB. The flash image that I used is 10 MB (it > shows in the log that I sent). I guess you ran a different test. What I stated below ... I guess there is some miscommunication here: I tested with Linux, just not with odd flash sizes. Which I agree should be either handled correctly or denied by kvmtool. > > > [ 2.164992] physmap-flash 20000.flash: physmap platform flash device: > > [mem 0x00020000-0x0021ffff] > > [ 2.166539] 20000.flash: Found 2 x16 devices at 0x0 in 32-bit bank. > > Manufacturer ID 0x000000 Chip ID 0x00ffff > > ... > > # mtd_debug info /dev/mtd0 > > mtd.type = MTD_NORFLASH > > mtd.flags = MTD_CAP_NORFLASH > > mtd.size = 2097152 (2M) > > mtd.erasesize = 65536 (64K) > > mtd.writesize = 1 > > mtd.oobsize = 0 > > regions = 1 > > > > I think what you are seeing are problems when you give a non-power-of-2 > > sized flash image. The current patch does not really support this (since > > it's hardly a thing in the real world). I originally wanted to expand any > > "uneven" size to the next power-of-2, but this doesn't work easily with > > mmap. > > I would expect that if kvmtool allows the user to specify a non-power-of-2 > flash > image size, then it should know how to deal with it and not present a broken > device to a linux guest if that size is forbidden by the spec. Or it is > allowed by > the specification and kvmtool doesn't know how to deal with it? I don't know, I would guess physical flash has always a power-of-2 size, at least on a per-chip base. So the "spec" doesn't even consider the other case. > Instead of expanding the file provided by the user to fit a bigger flash, how > about you use the highest power of two size that is smaller than the flash > size? The original idea was to avoid cutting off the flash file, but this doesn't really work easily, or at least is not worth the effort. So I was suggesting the trimming you mentioned in the next sentence ;-) vvvvvvvvvvvvv > > So I now changed the code to downgrade, so you get 8MB with any file > > ranging from [8MB, 16MB(, for instance. > > That fixed the Linux problems with those files for me. > > > >> $ ./lkvm run -c4 -m4096 -k /path/to/kernel -d /path/to/disk -p > >> root="/dev/vda2" -F > >> flash.img > >> > >> [ 0.659167] physmap-flash 2000000.flash: physmap platform flash device: > >> [mem > >> 0x02000000-0x029fffff] > >> [ 0.660444] Number of erase regions: 1 > >> [ 0.661036] Primary Vendor Command Set: 0001 (Intel/Sharp Extended) > >> [ 0.661688] Primary Algorithm Table at 0031 > >> [ 0.662168] Alternative Vendor Command Set: 0000 (None) > >> [ 0.662711] No Alternate Algorithm Table > >> [ 0.663120] Vcc Minimum: 4.5 V > >> [ 0.663450] Vcc Maximum: 5.5 V > >> [ 0.663779] No Vpp line > >> [ 0.664039] Typical byte/word write timeout: 2 µs > >> [ 0.664590] Maximum byte/word write timeout: 2 µs > >> [ 0.665240] Typical full buffer write timeout: 2 µs > >> [ 0.665775] Maximum full buffer write timeout: 2 µs > >> [ 0.666373] Typical block erase timeout: 2 ms > >> [ 0.666828] Maximum block erase timeout: 2 ms > >> [ 0.667282] Chip erase not supported > >> [ 0.667659] Device size: 0x800000 bytes (8 MiB) > >> [ 0.668137] Flash Device Interface description: 0x0006 > >> [ 0.668697] - Unknown > >> [ 0.668963] Max. bytes in buffer write: 0x40 > >> [ 0.669407] Number of Erase Block Regions: 1 > >> [ 0.669865] Erase Region #0: BlockSize 0x8000 bytes, 160 blocks > >> [ 0.672299] 2000000.flash: Found 2 x16 devices at 0x0 in 32-bit bank. > >> Manufacturer ID 0x000000 Chip ID 0x00ffff > >> [ 0.681328] NOR chip too large to fit in mapping. Attempting to cope... > >> [ 0.682046] Intel/Sharp Extended Query Table at 0x0031 > >> [ 0.682645] Using buffer write method > >> [ 0.683031] Sum of regions (a00000) != total size of set of interleaved > >> chips > >> (1000000) > >> [ 0.683854] gen_probe: No supported Vendor Command Set found > >> [ 0.684441] physmap-flash 2000000.flash: map_probe failed > >> > >> I also defined DEBUG_CFI in drivers/mtd/chips/cfi_probe.c. > >> > >> The Flash Device Interface description that we provide is wrong, it should > >> 0x05. > >> More details below. > >> > >> 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 > >>> + > >>> #define ARM_IOPORT_SIZE (ARM_MMIO_AREA - ARM_IOPORT_AREA) > >>> #define ARM_VIRTIO_MMIO_SIZE (ARM_AXI_AREA - (ARM_MMIO_AREA + > >>> ARM_GIC_SIZE)) > >>> #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) > >>> + > >>> +/* 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 */ > >> I don't think this is correct. From Intel StrataFlash Embedded Memory (P30) > >> Family, table 34: > >> > >> ""n" such that n+1 specifies the bit field that represents the flash > >> device width > >> capabilities as described in the table". > > Yeah, seems to be correct, but it looks this Intel Strata document is the > > only place which details this encoding (which looks like being retrofit > > somehow). > > And I didn't really use this document, because it's a manufacturer data > > sheet and not a specification. > > The device is in the list of specification you provided in the commit message. Where? I only see JEP137B, JESD68-01 and Intel AN-646 in the list up there. > I > think it would make the reviewers' life a lot easier if you posted all the > documentation that you used, and drop documentation that you didn't. Where > did you > get the value 0x06 from? That was the only document from where I could infer > what > it means, maybe I didn't dig deep enough. > > > I will change it to 0x5, but for the records Linux worked even with 0x6 for > > me. > > I would say that in this case working != correct, because Linux 5.6-rc2 > defines > 0x05 as a x32/x16 interface, and 0x06 is undefined in the file that I > mentioned in > the previous reply. Did you check to see if the Linux driver recognized that > interface type? Maybe it changed between versions. I also tried 0x00,0x00 for > the > interface description and Linux also worked. I guess because the emulation doesn't really care about the access size (we use memcpy). > Either way, I followed the trail of breadcrumbs starting at the comment for > the > define and I found this in Common Flash Memory Interface Specification release > 2.0, Appendix: > > "Note: April 2000 -x16/x32 devices will be represented by hex value 0005h as > requested by Intel in order to make them more software friendly. Changes will > be > made to the CFI drivers so that a bit-wise switch is created to represent > different data widths. [..] For example, if we take the description for an > x16/x32 > device (0005h) and we convert that to binary we get 0101b. If we add one to > this > value we get a bit pattern that looks like this: 0110b. This bit-wise switch > indicates that the device a x16/x32 device". > > I guess the reason for the inconsistencies is that at some point it used to be > different, but it was changed because Intel requested it. Yeah, I found this one as well later. > >> If you want to advertise 32 and 16 bit write capabilities, it should be 5 > >> because > >> 5+1=6. This is also the value that the Linux kernel checks for (see > >> include/linux/mtd/cfi.h, define CFI_INTERFACE_X16_BY_X32_ASYNC"). 6 > >> actually means > >> 32, 16 and 8 bit accesses. > >> > >> This begs another question: why do we support both 16 and 32 bit accesses > >> instead > >> of supporting only 32 bit? > > Because we can, there is no reason to restrict this. I feel like we should > > be as capable as possible, especially since it's trivial to emulate. > > Makes sense. > > > > >>> + PROGRAM_BUFF_SIZE_BITS + 1 - CFI_NR_FLASH_CHIPS, 0x00, > >>> + /* number of multi-byte writes */ > >> Shouldn't the comment be maximum number of bytes in the write buffer? > > Yes, possibly. > > > >>> + 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 */ > >>> +}; > >> As an aside, I found it impossible to review the cfi_query_table array in > >> its > >> current form. This is how I wrote the array so I could read it. I also > >> took the > >> liberty to remove the offset when indexing the array, making read_cfi less > >> error > >> prone, in my opinion: > > Please don't post elaborate code sequences as a comment, especially not if > > it gets mangled (Thunderbird is annoyingly bad in this respect). > > I use Thunderbird and it showed fine for me, and I have sent large diffs > before in > replies and I got no complaints. I use the settings from > Documentation/process/email-clients.rst. Maybe it was a problem because (I guess) you copy&pasted the diff in? Because I see a "...skipping..." line in there, bogus line breaks and all tabs were converted into spaces. Or there was some cocky mail server in the queue ;-) Eventually I gave up with sending diffs other than for demonstration purposes through the Thunderbird editor because of various problems. As a workaround you could try to save the email, fix it up with a proper editor (or insert the patch there), then send it via git send-email. Or avoid sending patches this way at all ;-) I will send v3 tomorrow morning after double checking that I didn't miss a comment. Cheers, Andre > Thanks, > Alex > > I think I would have got what you mean by showing just one line ;-) > > > > Cheers, > > Andre > > > >> diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c > >> index d7c0e7e80d69..65a90e288be8 100644 > >> --- a/hw/cfi_flash.c > >> +++ b/hw/cfi_flash.c > >> @@ -46,45 +46,43 @@ > >> */ > >> #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, > >> + [0x10] = 'Q', 'R', 'Y', /* ID string */ > >> + [0x13] = 0x01, 0x00, /* primary command set: Intel/Sharp > >> extended */ > >> + [0x15] = 0x31, 0x00, /* address of primary extended query table > >> */ > >> + [0x17] = 0x00, 0x00, /* alternative command set: unused */ > >> + [0x19] = 0x00, 0x00, /* address of alternative extended query > >> table*/ > >> + /* System interface information */ > >> + [0x1b] = 0x45, /* minimum Vcc voltage: 4.5V */ > >> + [0x1c] = 0x55, /* maximum Vcc voltage: 5.5V */ > >> + [0x1d] = 0x00, /* minimum Vpp voltage: 0.0V (unused) */ > >> + [0x1e] = 0x00, /* maximum Vpp voltage: 0.0V *(unused) */ > >> + [0x1f] = 0x01, /* timeout for single word program: 2 us */ > >> + [0x20] = 0x01, /* timeout for multi-byte program: 2 us */ > >> + [0x21] = 0x01, /* timeout for block erase: 2 ms */ > >> + [0x22] = 0x00, /* timeout for full chip erase: not > >> supported */ > >> + [0x23] = 0x00, /* max timeout for single word program: 1x > >> */ > >> + [0x24] = 0x00, /* max timeout for mulit-byte program: 1x > >> */ > >> + [0x25] = 0x00, /* max timeout for block erase: 1x */ > >> + [0x26] = 0x00, /* max timeout for chip erase: not > >> supported */ > >> + /* Flash geometry information */ > >> + [0x27] = 0x00, /* size in power-of-2 bytes, filled later > >> */ > >> + [0x28] = 0x06, 0x00, /* interface description: 32 and 16 bits */ > >> + [0x2a] = 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 */ > >> + [0x2c] = 0x01, /* one erase block region */ > >> + [0x2d] = 0x00, 0x00, 0x00, 0x00, /* number and size of erase > >> blocks, filled */ > >> + /* Intel primary algorithm extended query table */ > >> + [0x31] = 'P', 'R', 'I', /* ID string */ > >> + [0x34] = '1', '0', /* version 1.0 */ > >> + [0x36] = 0xa0, 0x00, 0x00, 0x00, /* optional features: instant > >> lock & > >> pm-read */ > >> + [0x40] = 0x00, /* no functions after suspend */ > >> + [0x41] = 0x01, 0x00, /* only lock bit supported */ > >> ...skipping... > >> + [0x10] = 'Q', 'R', 'Y', /* ID string */ > >> + [0x13] = 0x01, 0x00, /* primary command set: Intel/Sharp > >> extended */ > >> + [0x15] = 0x31, 0x00, /* address of primary extended query table > >> */ > >> + [0x17] = 0x00, 0x00, /* alternative command set: unused */ > >> + [0x19] = 0x00, 0x00, /* address of alternative extended query > >> table*/ > >> + /* System interface information */ > >> + [0x1b] = 0x45, /* minimum Vcc voltage: 4.5V */ > >> + [0x1c] = 0x55, /* maximum Vcc voltage: 5.5V */ > >> + [0x1d] = 0x00, /* minimum Vpp voltage: 0.0V (unused) */ > >> + [0x1e] = 0x00, /* maximum Vpp voltage: 0.0V *(unused) */ > >> + [0x1f] = 0x01, /* timeout for single word program: 2 us */ > >> + [0x20] = 0x01, /* timeout for multi-byte program: 2 us */ > >> + [0x21] = 0x01, /* timeout for block erase: 2 ms */ > >> + [0x22] = 0x00, /* timeout for full chip erase: not > >> supported */ > >> + [0x23] = 0x00, /* max timeout for single word program: 1x > >> */ > >> + [0x24] = 0x00, /* max timeout for mulit-byte program: 1x > >> */ > >> + [0x25] = 0x00, /* max timeout for block erase: 1x */ > >> + [0x26] = 0x00, /* max timeout for chip erase: not > >> supported */ > >> + /* Flash geometry information */ > >> + [0x27] = 0x00, /* size in power-of-2 bytes, filled later > >> */ > >> + [0x28] = 0x06, 0x00, /* interface description: 32 and 16 bits */ > >> + [0x2a] = 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 */ > >> + [0x2c] = 0x01, /* one erase block region */ > >> + [0x2d] = 0x00, 0x00, 0x00, 0x00, /* number and size of erase > >> blocks, filled */ > >> + /* Intel primary algorithm extended query table */ > >> + [0x31] = 'P', 'R', 'I', /* ID string */ > >> + [0x34] = '1', '0', /* version 1.0 */ > >> + [0x36] = 0xa0, 0x00, 0x00, 0x00, /* optional features: instant > >> lock & > >> pm-read */ > >> + [0x40] = 0x00, /* no functions after suspend */ > >> + [0x41] = 0x01, 0x00, /* only lock bit supported */ > >> + [0x43] = 0x50, /* best Vcc value: 5.0V */ > >> + [0x43] = 0x00, /* best Vpp value: 0.0V (unused) */ > >> + [0x44] = 0x01, /* number of protection register fields */ > >> + [0x45] = 0x00, 0x00, 0x00, 0x00,/* protection field 1 description > >> */ > >> }; > >> > >> - > >> /* > >> * Those states represent a subset of the CFI flash state machine. > >> */ > >> @@ -141,10 +139,7 @@ static int nr_erase_blocks(struct cfi_flash_device > >> *sfdev) > >> */ > >> 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)) { > >> + if (addr > sizeof(cfi_query_table)) { > >> pr_debug("CFI query read access beyond the end of table"); > >> return 0; > >> } > >> @@ -163,7 +158,7 @@ static u8 read_cfi(struct cfi_flash_device *sfdev, u64 > >> addr) > >> return ((FLASH_BLOCK_SIZE / 256 ) / CFI_NR_FLASH_CHIPS) >> > >> 8; > >> } > >> > >> - return cfi_query_table[addr - 0x10]; > >> + return cfi_query_table[addr]; > >> } > >> > >> static bool block_is_locked(struct cfi_flash_device *sfdev, u64 addr) > >> > >> Thanks, > >> Alex > >>> + > >>> + > >>> +/* > >>> + * 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]; > >>> + 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 */ > >>> + case CFI_CMD_READ_ARRAY: > >>> + sfdev->read_mode = READ_ARRAY; > >>> + break; > >>> + } > >>> + > >>> +out_unlock: > >>> + mutex_unlock(&sfdev->mutex); > >>> +} > >>> + > >>> +#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", ®_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; > >>> + 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); > >>> + 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); > >>> +} > >>> + > >>> 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