@@ -24,6 +24,8 @@
* THE SOFTWARE.
*
*/
+#include <sys/vfs.h>
+
#include "sysemu.h"
#include "hw.h"
#include "elf.h"
@@ -88,6 +90,122 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
return qirq;
}
+#define HUGETLBFS_MAGIC 0x958458f6
+
+static long getrampagesize(void)
+{
+ struct statfs fs;
+ int ret;
+
+ if (!mem_path) {
+ /* guest RAM is backed by normal anonymous pages */
+ return getpagesize();
+ }
+
+ do {
+ ret = statfs(mem_path, &fs);
+ } while (ret != 0 && errno == EINTR);
+
+ if (ret != 0) {
+ fprintf(stderr, "Couldn't statfs() memory path: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ if (fs.f_type != HUGETLBFS_MAGIC) {
+ /* Explicit mempath, but it's ordinary pages */
+ return getpagesize();
+ }
+
+ /* It's hugepage, return the huge page size */
+ return fs.f_bsize;
+}
+
+static size_t create_page_sizes_prop(uint32_t *prop, size_t maxsize)
+{
+ int cells;
+ target_ulong ram_page_size = getrampagesize();
+ int i, j;
+
+ if (!kvm_enabled()) {
+ /* For the supported CPUs in emulation, we support just 4k and
+ * 16MB pages, with the usual encodings. This is the default
+ * set the guest will assume if we don't specify anything */
+ return 0;
+ }
+
+ cells = kvmppc_read_segment_page_sizes(prop, maxsize / sizeof(uint32_t));
+ if (cells < 0) {
+ fprintf(stderr, "Error reading host's "
+ "ibm,segment-page-sizes property\n");
+ exit(1);
+ }
+
+ if (cells == 0) {
+ /* Host specifies no pagesizes, so use the architected ones */
+ uint32_t def_page_sizes[] = {0xc, 0x0, 0x1, 0xc, 0x0, /* 4kB */
+ 0x18, 0x100, 0x1, 0x18, 0x0, }; /* 16MB */
+
+ assert(maxsize >= sizeof(def_page_sizes));
+
+ memcpy(prop, def_page_sizes, sizeof(def_page_sizes));
+ cells = sizeof(def_page_sizes) / sizeof(def_page_sizes[0]);
+ }
+
+ /* Filter based on pagesize backing RAM */
+ i = j = 0;
+ while (i < cells) {
+ uint32_t baseshift, slbenc, numsizes, k, n;
+
+ if ((i + 3) >= cells) {
+ fprintf(stderr, "Malformed ibm,segment-page-sizes on host\n");
+ exit(1);
+ }
+
+ baseshift = be32_to_cpu(prop[i++]);
+ slbenc = be32_to_cpu(prop[i++]);
+ numsizes = be32_to_cpu(prop[i++]);
+
+ if ((i + numsizes*2) >= cells) {
+ fprintf(stderr, "Malformed ibm,segment-page-sizes on host\n");
+ exit(1);
+ }
+
+ /* Too big, skip */
+ if ((1UL << baseshift) > ram_page_size) {
+ i += numsizes*2;
+ continue;
+ }
+
+ n = 0;
+ for (k = 0; k < numsizes; k++) {
+ uint32_t shift = be32_to_cpu(prop[i + k*2]);
+
+ if ((1UL << shift) <= ram_page_size) {
+ n++;
+ }
+ }
+
+ prop[j++] = cpu_to_be32(baseshift);
+ prop[j++] = cpu_to_be32(slbenc);
+ prop[j++] = cpu_to_be32(n);
+
+ for (k = 0; k < numsizes; k++) {
+ uint32_t shift = be32_to_cpu(prop[i++]);
+ uint32_t hashenc = be32_to_cpu(prop[i++]);
+
+ if ((1UL << shift) <= ram_page_size) {
+ prop[j++] = cpu_to_be32(shift);
+ prop[j++] = cpu_to_be32(hashenc);
+ }
+ }
+ }
+
+ assert(i == cells);
+
+ return j * sizeof(uint32_t);
+}
+
static void *spapr_create_fdt_skel(const char *cpu_model,
target_phys_addr_t rma_size,
target_phys_addr_t initrd_base,
@@ -189,6 +307,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
kvmppc_read_int_cpu_dt("clock-frequency") : 1000000000;
uint32_t vmx = kvm_enabled() ? kvmppc_read_int_cpu_dt("ibm,vmx") : 0;
uint32_t dfp = kvm_enabled() ? kvmppc_read_int_cpu_dt("ibm,dfp") : 0;
+ uint32_t page_sizes_prop[15];
+ size_t page_sizes_prop_size;
if ((index % smt) != 0) {
continue;
@@ -251,6 +371,13 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property_cell(fdt, "ibm,dfp", dfp)));
}
+ page_sizes_prop_size = create_page_sizes_prop(page_sizes_prop,
+ sizeof(page_sizes_prop));
+ if (page_sizes_prop_size) {
+ _FDT((fdt_property(fdt, "ibm,segment-page-sizes",
+ page_sizes_prop, page_sizes_prop_size)));
+ }
+
_FDT((fdt_end_node(fdt)));
}
@@ -689,6 +689,49 @@ uint64_t kvmppc_read_int_cpu_dt(const char *propname)
return 0;
}
+/* Read a CPU node property from the host device tree that's a single
+ * integer (32-bit or 64-bit). Returns 0 if anything goes wrong
+ * (can't find or open the property, or doesn't understand the
+ * format) */
+int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells)
+{
+ char buf[PATH_MAX];
+ FILE *f;
+ int ncells;
+
+ if (kvmppc_find_cpu_dt(buf, sizeof(buf))) {
+ return -1;
+ }
+
+ strncat(buf, "/ibm,segment-page-sizes", sizeof(buf) - strlen(buf));
+
+ f = fopen(buf, "rb");
+ if (!f) {
+ if (errno == -ENOENT) {
+ /* If missing, assume defaults */
+ return 0;
+ }
+ return -1;
+ }
+
+ ncells = fread(prop, sizeof(uint32_t), maxcells, f);
+ if (ncells == maxcells) {
+ uint32_t tmp;
+ int n;
+
+ n = fread(&tmp, sizeof(tmp), 1, f);
+ if ((n != 0) || !feof(f)) {
+ fclose(f);
+ /* Not enough space provided for the result */
+ return -1;
+ }
+ }
+
+ fclose(f);
+
+ return ncells;
+}
+
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
{
uint32_t *hc = (uint32_t*)buf;
@@ -15,6 +15,7 @@ void kvmppc_init(void);
uint32_t kvmppc_get_tbfreq(void);
uint64_t kvmppc_read_int_cpu_dt(const char *propname);
+int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells);
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
int kvmppc_set_interrupt(CPUState *env, int irq, int level);
void kvmppc_set_papr(CPUState *env);
@@ -35,6 +36,11 @@ static inline uint64_t kvmppc_read_int_cpu_dt(const char *propname)
return 0;
}
+static inline int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells)
+{
+ return -1;
+}
+
static inline int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
{
return -1;