@@ -37,9 +37,11 @@ enum {
enum kvm_mem_type {
KVM_MEM_TYPE_RAM = 1 << 0,
KVM_MEM_TYPE_DEVICE = 1 << 1,
+ KVM_MEM_TYPE_RESERVED = 1 << 2,
KVM_MEM_TYPE_ALL = KVM_MEM_TYPE_RAM
| KVM_MEM_TYPE_DEVICE
+ | KVM_MEM_TYPE_RESERVED
};
struct kvm_ext {
@@ -115,6 +117,12 @@ static inline int kvm__register_dev_mem(struct kvm *kvm, u64 guest_phys,
KVM_MEM_TYPE_DEVICE);
}
+static inline int kvm__reserve_mem(struct kvm *kvm, u64 guest_phys, u64 size)
+{
+ return kvm__register_mem(kvm, guest_phys, size, NULL,
+ KVM_MEM_TYPE_RESERVED);
+}
+
int kvm__register_mmio(struct kvm *kvm, u64 phys_addr, u64 phys_addr_len, bool coalesce,
void (*mmio_fn)(struct kvm_cpu *vcpu, u64 addr, u8 *data, u32 len, u8 is_write, void *ptr),
void *ptr);
@@ -150,6 +158,8 @@ static inline const char *kvm_mem_type_to_string(enum kvm_mem_type type)
return "RAM";
case KVM_MEM_TYPE_DEVICE:
return "device";
+ case KVM_MEM_TYPE_RESERVED:
+ return "reserved";
}
return "???";
@@ -177,18 +177,55 @@ int kvm__exit(struct kvm *kvm)
}
core_exit(kvm__exit);
-/*
- * Note: KVM_SET_USER_MEMORY_REGION assumes that we don't pass overlapping
- * memory regions to it. Therefore, be careful if you use this function for
- * registering memory regions for emulating hardware.
- */
int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
void *userspace_addr, enum kvm_mem_type type)
{
struct kvm_userspace_memory_region mem;
+ struct kvm_mem_bank *merged = NULL;
struct kvm_mem_bank *bank;
int ret;
+ /* Check for overlap */
+ list_for_each_entry(bank, &kvm->mem_banks, list) {
+ u64 bank_end = bank->guest_phys_addr + bank->size - 1;
+ u64 end = guest_phys + size - 1;
+ if (guest_phys > bank_end || end < bank->guest_phys_addr)
+ continue;
+
+ /* Merge overlapping reserved regions */
+ if (bank->type == KVM_MEM_TYPE_RESERVED &&
+ type == KVM_MEM_TYPE_RESERVED) {
+ bank->guest_phys_addr = min(bank->guest_phys_addr, guest_phys);
+ bank->size = max(bank_end, end) - bank->guest_phys_addr + 1;
+
+ if (merged) {
+ /*
+ * This is at least the second merge, remove
+ * previous result.
+ */
+ list_del(&merged->list);
+ free(merged);
+ }
+
+ guest_phys = bank->guest_phys_addr;
+ size = bank->size;
+ merged = bank;
+
+ /* Keep checking that we don't overlap another region */
+ continue;
+ }
+
+ pr_err("%s region [%llx-%llx] would overlap %s region [%llx-%llx]",
+ kvm_mem_type_to_string(type), guest_phys, guest_phys + size - 1,
+ kvm_mem_type_to_string(bank->type), bank->guest_phys_addr,
+ bank->guest_phys_addr + bank->size - 1);
+
+ return -EINVAL;
+ }
+
+ if (merged)
+ return 0;
+
bank = malloc(sizeof(*bank));
if (!bank)
return -ENOMEM;
@@ -199,18 +236,21 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
bank->size = size;
bank->type = type;
- mem = (struct kvm_userspace_memory_region) {
- .slot = kvm->mem_slots++,
- .guest_phys_addr = guest_phys,
- .memory_size = size,
- .userspace_addr = (unsigned long)userspace_addr,
- };
+ if (type != KVM_MEM_TYPE_RESERVED) {
+ mem = (struct kvm_userspace_memory_region) {
+ .slot = kvm->mem_slots++,
+ .guest_phys_addr = guest_phys,
+ .memory_size = size,
+ .userspace_addr = (unsigned long)userspace_addr,
+ };
- ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
- if (ret < 0)
- return -errno;
+ ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ if (ret < 0)
+ return -errno;
+ }
list_add(&bank->list, &kvm->mem_banks);
+
return 0;
}