aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Mammedov <imammedo@redhat.com>2014-10-31 16:38:36 +0000
committerMichael S. Tsirkin <mst@redhat.com>2014-11-23 12:11:30 +0200
commit92a37a04d6e034b73ea1ba4825ba4d5860f0a810 (patch)
treecc6fe60b9a307bd265332e64d41ef7cee8efa25a
parent34dde13685ebc2c07923f32ad69e40b27c0e0bb4 (diff)
pc: limit DIMM address and size to page aligned values
When running in KVM mode, kvm_set_phys_mem() will silently fail if registered MemoryRegion address/size is not page aligned. Causing memory hotplug failure in guest. Mapping non aligned MemoryRegion in TCG mode 'works', but sane guest OS still expects page aligned memory module and fails to initialize it if it's not aligned. So do not allow non aligned (i.e. valid) address/size values for DIMM to avoid either KVM failure or guest issues caused by it. Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--hw/i386/pc.c3
-rw-r--r--hw/mem/pc-dimm.c14
-rw-r--r--include/hw/mem/pc-dimm.h2
3 files changed, 16 insertions, 3 deletions
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 70ae3cfde6..33928b90fd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1556,6 +1556,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr = ddc->get_memory_region(dimm);
+ uint64_t align = TARGET_PAGE_SIZE;
uint64_t addr;
addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
@@ -1565,7 +1566,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base,
memory_region_size(&pcms->hotplug_memory),
- !addr ? NULL : &addr,
+ !addr ? NULL : &addr, align,
memory_region_size(mr), &local_err);
if (local_err) {
goto out;
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index a800ea7a9f..4944f0faf9 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -139,7 +139,7 @@ static int pc_dimm_built_list(Object *obj, void *opaque)
uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
uint64_t address_space_size,
- uint64_t *hint, uint64_t size,
+ uint64_t *hint, uint64_t align, uint64_t size,
Error **errp)
{
GSList *list = NULL, *item;
@@ -152,6 +152,18 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
goto out;
}
+ if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) {
+ error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes",
+ align);
+ goto out;
+ }
+
+ if (QEMU_ALIGN_UP(size, align) != size) {
+ error_setg(errp, "backend memory size must be multiple of 0x%"
+ PRIx64, align);
+ goto out;
+ }
+
assert(address_space_end > address_space_start);
object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list);
diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h
index 761eeef801..e1dcbbcd58 100644
--- a/include/hw/mem/pc-dimm.h
+++ b/include/hw/mem/pc-dimm.h
@@ -72,7 +72,7 @@ typedef struct PCDIMMDeviceClass {
uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
uint64_t address_space_size,
- uint64_t *hint, uint64_t size,
+ uint64_t *hint, uint64_t align, uint64_t size,
Error **errp);
int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);