diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 34b12a3..44b2537 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -38,6 +38,7 @@
 
 #include <libfdt.h>
 #include "hw/ppc/spapr_drc.h"
+#include "kvm_ppc.h"
 
 /* #define DEBUG_SPAPR */
 
@@ -240,6 +241,36 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
     target_ulong ret = RTAS_OUT_SUCCESS;
 
     switch (parameter) {
+    case RTAS_SYSPARM_PROCESSOR_MODULE_INFO: {
+        struct sPAPRRTASModuleInfo modinfo;
+        int i, size = sizeof(modinfo), offset = 0;
+
+        memset(&modinfo, 0, size);
+        if (kvmppc_rtas_get_module_info(&modinfo)) {
+            ret = RTAS_OUT_HW_ERROR;
+            break;
+        }
+
+        stw_be_phys(&address_space_memory, buffer+offset, size);
+        offset += 2;
+
+        stw_be_phys(&address_space_memory, buffer+offset, modinfo.module_types);
+        offset += 2;
+
+        for (i = 0; i < modinfo.module_types; i++) {
+            stw_be_phys(&address_space_memory, buffer+offset,
+                        modinfo.si[i].sockets);
+            offset += 2;
+            stw_be_phys(&address_space_memory, buffer+offset,
+                        modinfo.si[i].chips);
+            offset += 2;
+            stw_be_phys(&address_space_memory, buffer+offset,
+                        modinfo.si[i].cores_per_chip);
+            offset += 2;
+        }
+        break;
+    }
+
     case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: {
         char *param_val = g_strdup_printf("MaxEntCap=%d,"
                                           "DesMem=%llu,"
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 5baa906..f793465 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -463,6 +463,7 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
 /* RTAS ibm,get-system-parameter token values */
 #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS      20
 #define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE        42
+#define RTAS_SYSPARM_PROCESSOR_MODULE_INFO       43
 #define RTAS_SYSPARM_UUID                        48
 
 /* RTAS indicator/sensor types
@@ -646,4 +647,16 @@ int spapr_rng_populate_dt(void *fdt);
  */
 #define SPAPR_LMB_FLAGS_ASSIGNED 0x00000008
 
+#define SPAPR_MAX_MODULE_TYPES  1
+
+struct sPAPRRTASSocketInfo {
+    unsigned short sockets;
+    unsigned short chips;
+    unsigned short cores_per_chip;
+};
+struct sPAPRRTASModuleInfo {
+    unsigned short module_types;
+    struct sPAPRRTASSocketInfo si[SPAPR_MAX_MODULE_TYPES];
+};
+
 #endif /* !defined (__HW_SPAPR_H__) */
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index 9940a90..bbef78c 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -15,6 +15,7 @@
  */
 
 #include <dirent.h>
+#include <glob.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -2518,3 +2519,143 @@ int kvmppc_enable_hwrng(void)
 
     return kvmppc_enable_hcall(kvm_state, H_RANDOM);
 }
+
+/* Read an identifier from the file @path and add the identifier
+ * to the hash table @gt unless its already in the table.
+ */
+static int kvmppc_hash_file_contents(GHashTable *gt, char *path)
+{
+    uint32_t idx;
+
+    idx = kvmppc_read_int_dt(path);
+    if (idx == -1) {
+        return -1;
+    }
+
+    if (g_hash_table_contains(gt, GINT_TO_POINTER(idx))) {
+        return 0;
+    }
+
+    if (!g_hash_table_insert(gt, GINT_TO_POINTER(idx), NULL)) {
+        fprintf(stderr, "%s() Unable to add key %d\n", __func__, idx);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int kvmppc_glob_count_ids_dt(const char *pattern, int *count)
+{
+    int i, rc;
+    glob_t dtglob;
+    GHashTable *htbl;
+
+    rc = glob(pattern, GLOB_NOSORT, NULL, &dtglob);
+    if (rc) {
+        fprintf(stderr, "%s() glob(%s) returns %d, errno %d\n", __func__,
+                pattern, rc, errno);
+        return -1;
+    }
+
+    rc = -1;
+    htbl = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
+
+    for (i = 0; i < dtglob.gl_pathc; i++) {
+        if (kvmppc_hash_file_contents(htbl, dtglob.gl_pathv[i])) {
+            goto cleanup;
+        }
+    }
+
+    *count = g_hash_table_size(htbl);
+    rc = 0;
+
+cleanup:
+    globfree(&dtglob);
+    g_hash_table_remove_all(htbl);
+    g_hash_table_destroy(htbl);
+
+    return rc;
+}
+
+/* Each socket's (aka module's) id is contained in the 'ibm,hw-module-id'
+ * file in an "xscom" directory (/proc/device-tree/xscom*). Similarly each
+ * chip's id is contained in the 'ibm,chip-id' file in an xscom directory.
+ *
+ * Since a module can have more than one chip and a chip can have more than
+ * one core, there are likely to be duplicates in the module and chip ids
+ * i.e more than one xscom directory can contain the same module/chip id.
+ *
+ * Search the xscom directories and count the number of _UNIQUE_ modules
+ * and chips in the system.
+ *
+ * Return 0 if one or more modules and chips each are found. Return -1
+ * otherwise.
+ */
+static int kvmppc_count_sockets_chips_dt(int *num_sockets, int *num_chips)
+{
+    const char *chip_pattern = "/proc/device-tree/xscom@*/ibm,chip-id";
+    const char *module_pattern = "/proc/device-tree/xscom@*/ibm,hw-module-id";
+
+    if (kvmppc_glob_count_ids_dt(module_pattern, num_sockets)
+        || kvmppc_glob_count_ids_dt(chip_pattern, num_chips)) {
+        return -1;
+    }
+
+    if (*num_sockets == 0 || *num_chips == 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Each core in the system is represented by a directory with the prefix
+ * 'PowerPC,POWER' in directory /proc/device-tree/cpus/.  Process that
+ * directory and count the number of cores in the system.
+ *
+ * Return 0 if one or more cores are found. Return -1 otherwise.
+ */
+static int kvmppc_count_cores_dt(int *num_cores)
+{
+    int rc;
+    glob_t dtglob;
+    const char *cpus_pattern = "/proc/device-tree/cpus/PowerPC,POWER*";
+
+    rc = glob(cpus_pattern, GLOB_NOSORT, NULL, &dtglob);
+    if (rc) {
+        fprintf(stderr, "%s() glob(%s) returns %d, errno %d\n", __func__,
+                cpus_pattern, rc, errno);
+        return -1;
+    }
+
+    *num_cores = dtglob.gl_pathc;
+    globfree(&dtglob);
+
+    if (*num_cores == 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int kvmppc_rtas_get_module_info(struct sPAPRRTASModuleInfo *modinfo)
+{
+    int cores, chips, sockets;
+
+    if (kvmppc_count_sockets_chips_dt(&sockets, &chips)
+        || kvmppc_count_cores_dt(&cores)) {
+        return -1;
+    }
+
+    /* TODO: Although the interface allows multiple module types, Power
+     *       systems currently support only one module type per system -
+     *       either Single Chip Module(SCM) or Dual Chip Module(DCM),
+     *       but not both types in the same system. Hard code module_types
+     *       for now till we can determine it dynamically.
+     */
+    modinfo->module_types = 1;
+    modinfo->si[0].sockets = sockets;
+    modinfo->si[0].chips = chips;
+    modinfo->si[0].cores_per_chip = cores / chips;
+
+    return 0;
+}
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index 309cbe0..46a2768 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -10,6 +10,7 @@
 #define __KVM_PPC_H__
 
 #define TYPE_HOST_POWERPC_CPU "host-" TYPE_POWERPC_CPU
+#include "hw/ppc/spapr.h"
 
 #ifdef CONFIG_KVM
 
@@ -55,6 +56,7 @@ void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index,
                              target_ulong pte0, target_ulong pte1);
 bool kvmppc_has_cap_fixup_hcalls(void);
 int kvmppc_enable_hwrng(void);
+int kvmppc_rtas_get_module_info(struct sPAPRRTASModuleInfo *modinfo);
 
 #else
 
@@ -256,6 +258,12 @@ static inline int kvmppc_enable_hwrng(void)
 {
     return -1;
 }
+
+static inline int kvmppc_rtas_get_module_info(struct sPAPRRTASModuleInfo *mi)
+{
+    return -1;
+}
+
 #endif
 
 #ifndef CONFIG_KVM
