diff options
Diffstat (limited to 'hw/cfi_flash.c')
-rw-r--r-- | hw/cfi_flash.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c index 3c76c04a..7faecdfb 100644 --- a/hw/cfi_flash.c +++ b/hw/cfi_flash.c @@ -8,6 +8,7 @@ #include "kvm/kvm.h" #include "kvm/kvm-arch.h" +#include "kvm/kvm-cpu.h" #include "kvm/devices.h" #include "kvm/fdt.h" #include "kvm/mutex.h" @@ -139,6 +140,7 @@ struct cfi_flash_device { enum cfi_flash_state state; enum cfi_read_mode read_mode; u8 sr; + bool is_mapped; }; static int nr_erase_blocks(struct cfi_flash_device *sfdev) @@ -437,6 +439,43 @@ static void cfi_flash_write(struct cfi_flash_device *sfdev, u16 command, } } +/* + * If we are in ARRAY_READ mode, we can map the flash array directly + * into the guest, just as read-only. This greatly improves read + * performance, and avoids problems with exits due to accesses from + * load instructions without syndrome information (on ARM). + * Also it could allow code to be executed XIP in there. + */ +static int map_flash_memory(struct kvm *kvm, struct cfi_flash_device *sfdev) +{ + int ret; + + ret = kvm__register_mem(kvm, sfdev->base_addr, sfdev->size, + sfdev->flash_memory, + KVM_MEM_TYPE_RAM | KVM_MEM_TYPE_READONLY); + if (!ret) + sfdev->is_mapped = true; + + return ret; +} + +/* + * Any write access changing the read mode would need to bring us back to + * "trap everything", as the CFI query read need proper handholding. + */ +static int unmap_flash_memory(struct kvm *kvm, struct cfi_flash_device *sfdev) +{ + int ret; + + ret = kvm__destroy_mem(kvm, sfdev->base_addr, sfdev->size, + sfdev->flash_memory); + + if (!ret) + sfdev->is_mapped = false; + + return ret; +} + static void cfi_flash_mmio(struct kvm_cpu *vcpu, u64 addr, u8 *data, u32 len, u8 is_write, void *context) @@ -467,6 +506,12 @@ static void cfi_flash_mmio(struct kvm_cpu *vcpu, cfi_flash_write(sfdev, value & 0xffff, faddr, data, len); + /* Adjust our mapping status accordingly. */ + if (!sfdev->is_mapped && sfdev->read_mode == READ_ARRAY) + map_flash_memory(vcpu->kvm, sfdev); + else if (sfdev->is_mapped && sfdev->read_mode != READ_ARRAY) + unmap_flash_memory(vcpu->kvm, sfdev); + mutex_unlock(&sfdev->mutex); } @@ -543,6 +588,8 @@ static struct cfi_flash_device *create_flash_device_file(struct kvm *kvm, sfdev->read_mode = READ_ARRAY; sfdev->sr = CFI_STATUS_READY; + map_flash_memory(kvm, sfdev); + value = roundup(nr_erase_blocks(sfdev), BITS_PER_LONG) / 8; sfdev->lock_bm = malloc(value); memset(sfdev->lock_bm, 0, value); |