aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiodrag Dinic <miodrag.dinic@imgtec.com>2015-02-20 17:26:02 +0100
committerMiodrag Dinic <miodrag.dinic@imgtec.com>2015-03-10 12:15:23 +0100
commit7fa62cff5d1bf5067b9f4f92fd63272356a5b9ff (patch)
tree8d6577031a6f25c65e4e5af90e5b71dc2886a5bb
parent055e9546a4a6d86654b99b13177dbba909412d77 (diff)
downloadqemu-android-7fa62cff5d1bf5067b9f4f92fd63272356a5b9ff.tar.gz
hw/mips: Ranchu - Support for memory up to 4 GB
So far, this machine was limited to 496 MB + 16 MB IO space. This commit adds support for installing memory up to 4080 MB + 16 MB of IO space. Fast TLB optimisation was updated to allow running mips32r2/mips32r6 kernels with HIGHMEM support and 64-bit r2/r6 kernels. Change-Id: I0d99cb1fc59319bf9d4c257f80cce99aca2811f3
-rw-r--r--hw/mips/mips_ranchu.c55
-rw-r--r--target-mips/helper.c181
2 files changed, 178 insertions, 58 deletions
diff --git a/hw/mips/mips_ranchu.c b/hw/mips/mips_ranchu.c
index 5db26ee9b6..9490502e55 100644
--- a/hw/mips/mips_ranchu.c
+++ b/hw/mips/mips_ranchu.c
@@ -41,6 +41,8 @@ typedef struct {
qemu_irq *gfpic;
} RanchuState;
+#define MAX_RAM_SIZE_MB 4079UL
+
enum {
RANCHU_GF_PIC,
RANCHU_GF_TTY,
@@ -95,7 +97,6 @@ static DevMapEntry devmap[] = {
struct machine_params {
uint64_t kernel_entry;
- uint64_t ram_size;
uint64_t cmdline_ptr;
MIPSCPU *cpu;
} ranchu_params;
@@ -109,7 +110,7 @@ static void main_cpu_reset(void* opaque1)
cpu->env.active_tc.PC = opaque->kernel_entry;
cpu->env.active_tc.gpr[4] = opaque->cmdline_ptr; /* a0 */
- cpu->env.active_tc.gpr[5] = opaque->ram_size; /* a1 */
+ cpu->env.active_tc.gpr[5] = 0; /* a1 */
cpu->env.active_tc.gpr[6] = 0; /* a2 */
cpu->env.active_tc.gpr[7] = 0; /* a3 */
@@ -286,7 +287,7 @@ static void create_device(void* fdt, DevMapEntry* dev, qemu_irq* pic,
qemu_fdt_setprop(fdt, nodename, "compatible",
dev->dt_compatible,
strlen(dev->dt_compatible) + 1);
- qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", 1, base, 1, dev->size);
+ qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", 1, base, 2, dev->size);
if (pic == NULL) {
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
@@ -323,7 +324,8 @@ static void ranchu_init(MachineState *machine)
DeviceState *dev = qdev_create(NULL, TYPE_MIPS_RANCHU);
RanchuState *s = MIPS_RANCHU(dev);
- MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
/* init CPUs */
if (machine->cpu_model == NULL) {
@@ -351,9 +353,11 @@ static void ranchu_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, &ranchu_params);
- /* avoid overlap of ram and IO regs */
- if (ram_size > GOLDFISH_IO_SPACE)
- ram_size = GOLDFISH_IO_SPACE;
+ if (ram_size > (MAX_RAM_SIZE_MB << 20)) {
+ fprintf(stderr, "qemu: Too much memory for this machine. "
+ "RAM size reduced to %lu MB\n", MAX_RAM_SIZE_MB);
+ ram_size = MAX_RAM_SIZE_MB << 20;
+ }
fdt = create_device_tree(&fdt_size);
@@ -362,11 +366,19 @@ static void ranchu_init(MachineState *machine)
exit(1);
}
- memory_region_init_ram(ram, NULL, "ranchu.ram", ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(get_system_memory(), 0, ram);
+ memory_region_init_ram(ram_lo, NULL, "ranchu_low.ram",
+ MIN(ram_size, GOLDFISH_IO_SPACE), &error_abort);
+ vmstate_register_ram_global(ram_lo);
+ memory_region_add_subregion(get_system_memory(), 0, ram_lo);
+
+ /* post IO hole, if there is enough RAM */
+ if (ram_size > GOLDFISH_IO_SPACE) {
+ memory_region_init_ram(ram_hi, NULL, "ranchu_high.ram",
+ ram_size - GOLDFISH_IO_SPACE, &error_abort);
+ vmstate_register_ram_global(ram_hi);
+ memory_region_add_subregion(get_system_memory(), 0x20000000, ram_hi);
+ }
- ranchu_params.ram_size = ram_size;
ranchu_params.cpu = cpu;
cpu_mips_irq_init_cpu(env);
@@ -381,7 +393,7 @@ static void ranchu_init(MachineState *machine)
qemu_fdt_setprop_string(fdt, "/", "model", "ranchu");
qemu_fdt_setprop_string(fdt, "/", "compatible", "mti,goldfish");
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1);
- qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/", "interrupt-parent", devmap[RANCHU_GF_PIC].irq);
/* CPU node */
@@ -393,7 +405,13 @@ static void ranchu_init(MachineState *machine)
/* Memory node */
qemu_fdt_add_subnode(fdt, "/memory");
qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory");
- qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", 1, 0, 1, ram_size);
+ if (ram_size > GOLDFISH_IO_SPACE) {
+ qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg",
+ 1, 0, 2, GOLDFISH_IO_SPACE,
+ 1, 0x20000000, 2, ram_size - GOLDFISH_IO_SPACE);
+ } else {
+ qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", 1, 0, 2, ram_size);
+ }
/* Create goldfish_pic controller node in dt */
create_device(fdt, &devmap[RANCHU_GF_PIC], NULL, 1, 0);
@@ -411,10 +429,13 @@ static void ranchu_init(MachineState *machine)
initialize_console_and_adb();
- android_load_kernel(env, ram_size, machine->kernel_filename,
- machine->kernel_cmdline,
- machine->initrd_filename,
- fdt, fdt_size);
+ qemu_fdt_dumpdtb(fdt, fdt_size);
+
+ android_load_kernel(env, MIN(ram_size, GOLDFISH_IO_SPACE),
+ machine->kernel_filename,
+ machine->kernel_cmdline,
+ machine->initrd_filename,
+ fdt, fdt_size);
}
static int mips_ranchu_sysbus_device_init(SysBusDevice *sysbusdev)
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 75005cfb2d..7c5d405a27 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -319,6 +319,7 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
#define KERNEL_PGD_C0_CONTEXT (1 << 1)
#define KERNEL_HUGE_TLB (1 << 2)
#define KERNEL_RIXI (1 << 3)
+#define KERNEL_HIGHMEM (1 << 4)
/* TLB maintenance PTE software flags.
*
@@ -371,12 +372,63 @@ static struct {
int softshift;
} linux_pte_info = {0};
+static inline target_ulong cpu_debug_translate_address(CPUMIPSState *env, target_ulong address) {
+
+#if defined(TARGET_MIPS64)
+ if (!(linux_pte_info.config & KERNEL_64_BIT))
+ address = (int32_t)address;
+#endif
+
+ if (address <= USEG_LIMIT) {
+ /* useg */
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ return address & 0xFFFFFFFF;
+ } else {
+ return address;
+ }
+#if defined(TARGET_MIPS64)
+ } else if (address < 0x4000000000000000ULL) {
+ return address;
+ } else if (address < 0x8000000000000000ULL) {
+ return address;
+ } else if (address < 0xC000000000000000ULL) {
+ /* xkphys */
+ if ((address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
+ return address & env->PAMask;
+ } else {
+ return address;
+ }
+ } else if (address < 0xFFFFFFFF80000000ULL) {
+ /* xkseg */
+ return address;
+#endif
+ } else if (address < (int32_t)KSEG1_BASE) {
+ /* kseg0 */
+ return address - (int32_t)KSEG0_BASE;
+ } else if (address < (int32_t)KSEG2_BASE) {
+ /* kseg1 */
+ return address - (int32_t)KSEG1_BASE;
+ } else
+ return address;
+}
+
+static inline target_ulong get_mtc0_entrylo_mask(const CPUMIPSState *env)
+{
+#if defined(TARGET_MIPS64)
+ return env->PAMask >> 6;
+#else
+ return (env->PAMask >> 6) & 0x3FFFFFFF;
+#endif
+}
+
static inline void pagetable_walk32(CPUState *cs,
target_ulong pgd_addr, target_ulong vaddr,
target_ulong *entrylo0, target_ulong *entrylo1,
target_ulong *sw_pte_lo0, target_ulong *sw_pte_lo1)
{
target_ulong ptw_phys, pt_addr, index;
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
#if defined(TARGET_MIPS64)
/* workaround when running a 32bit
@@ -385,29 +437,63 @@ static inline void pagetable_walk32(CPUState *cs,
vaddr = (uint32_t)vaddr;
#endif
- ptw_phys = pgd_addr & 0x1fffffffUL; /* Assume pgd is in KSEG0/KSEG1 */
/* 32bit PTE lookup */
+ ptw_phys = cpu_debug_translate_address(env, pgd_addr);
index = (vaddr >> 22) << 2; /* Use bits 31..22 to index pgd */
ptw_phys += index;
pt_addr = ldl_phys(cs->as, ptw_phys);
- ptw_phys = pt_addr & 0x1fffffffUL; /* Assume pgt is in KSEG0/KSEG1 */
+ ptw_phys = cpu_debug_translate_address(env, pt_addr);
index = ((vaddr >> 13) & 0x1ff) << 3; /* Use bits 21..13 to index pgt */
ptw_phys += index;
/* Get the entrylo values from pgt */
- *entrylo0 = ldl_phys(cs->as, ptw_phys);
- if (sw_pte_lo0) {
- *sw_pte_lo0 = *entrylo0 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
- }
- *entrylo0 >>= linux_pte_info.softshift;
+ if (linux_pte_info.config & KERNEL_RIXI) {
+ target_ulong mask = ~(-1 << linux_pte_info.softshift);
- *entrylo1 = ldl_phys(cs->as, ptw_phys + 4);
- if (sw_pte_lo1) {
- *sw_pte_lo1 = *entrylo1 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
+ *entrylo0 = ldl_phys(cs->as, ptw_phys);
+ if (sw_pte_lo0) {
+ if (linux_pte_info.config & KERNEL_HIGHMEM)
+ *sw_pte_lo0 = *entrylo0 & ~(-1 << (linux_pte_info.softshift + 4));
+ else
+ *sw_pte_lo0 = *entrylo0 & ~(-1 << (linux_pte_info.softshift));
+ }
+ if (linux_pte_info.config & KERNEL_HIGHMEM)
+ *entrylo0 = (*entrylo0) >> 4;
+ *entrylo0 = ((*entrylo0 & mask) << (32 - linux_pte_info.softshift)) |
+ ((uint32_t)*entrylo0 >> linux_pte_info.softshift);
+ *entrylo0 = (*entrylo0 & get_mtc0_entrylo_mask(env)) |
+ ((*entrylo0 & (env->CP0_PageGrain & (3u << CP0PG_XIE))) <<
+ (CP0EnLo_XI - 30));
+
+ *entrylo1 = ldl_phys(cs->as, ptw_phys + 4);
+ if (sw_pte_lo1) {
+ if (linux_pte_info.config & KERNEL_HIGHMEM)
+ *sw_pte_lo1 = *entrylo1 & ~(-1 << (linux_pte_info.softshift + 4));
+ else
+ *sw_pte_lo1 = *entrylo1 & ~(-1 << (linux_pte_info.softshift));
+ }
+ if (linux_pte_info.config & KERNEL_HIGHMEM)
+ *entrylo1 = (*entrylo1) >> 4;
+ *entrylo1 = ((*entrylo1 & mask) << (32 - linux_pte_info.softshift)) |
+ ((uint32_t)*entrylo1 >> linux_pte_info.softshift);
+ *entrylo1 = (*entrylo1 & get_mtc0_entrylo_mask(env)) |
+ ((*entrylo1 & (env->CP0_PageGrain & (3u << CP0PG_XIE))) <<
+ (CP0EnLo_XI - 30));
+ } else {
+ *entrylo0 = ldl_phys(cs->as, ptw_phys);
+ if (sw_pte_lo0) {
+ *sw_pte_lo0 = *entrylo0 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
+ }
+ *entrylo0 >>= linux_pte_info.softshift;
+
+ *entrylo1 = ldl_phys(cs->as, ptw_phys + 4);
+ if (sw_pte_lo1) {
+ *sw_pte_lo1 = *entrylo1 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
+ }
+ *entrylo1 >>= linux_pte_info.softshift;
}
- *entrylo1 >>= linux_pte_info.softshift;
}
static inline void pagetable_walk64(CPUState *cs,
@@ -417,22 +503,21 @@ static inline void pagetable_walk64(CPUState *cs,
{
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
-
target_ulong ptw_phys, pt_addr, index;
- pgd_addr = pgd_addr & 0x1fffffffUL;
+ pgd_addr = cpu_debug_translate_address(env, pgd_addr);
index = ((uint64_t)vaddr >> 0x1b) & 0x1ff8;
pgd_addr += index;
- pgd_addr = ldl_phys(cs->as, pgd_addr);
+ pgd_addr = ldq_phys(cs->as, pgd_addr);
- ptw_phys = pgd_addr & 0x1fffffffUL;
+ ptw_phys = cpu_debug_translate_address(env, pgd_addr);
index = ((uint64_t)vaddr >> 0x12) & 0xff8;
ptw_phys += index;
- pt_addr = ldl_phys(cs->as, ptw_phys);
+ pt_addr = ldq_phys(cs->as, ptw_phys);
- ptw_phys = pt_addr & 0x1fffffffUL;
+ ptw_phys = cpu_debug_translate_address(env, pt_addr);
index = (((vaddr & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
((vaddr & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9)) & 0xff0;
ptw_phys += index;
@@ -440,32 +525,32 @@ static inline void pagetable_walk64(CPUState *cs,
if (linux_pte_info.config & KERNEL_RIXI) {
target_ulong mask = ~(-1 << linux_pte_info.softshift);
- *entrylo0 = ldl_phys(cs->as, ptw_phys);
+ *entrylo0 = ldq_phys(cs->as, ptw_phys);
if (sw_pte_lo0) {
*sw_pte_lo0 = *entrylo0 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
}
*entrylo0 = ((*entrylo0 & mask) << (64 - linux_pte_info.softshift)) |
((uint64_t)*entrylo0 >> linux_pte_info.softshift);
- *entrylo0 = (*entrylo0 & 0x3FFFFFFF) |
+ *entrylo0 = (*entrylo0 & get_mtc0_entrylo_mask(env)) |
(*entrylo0 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32));
- *entrylo1 = ldl_phys(cs->as, ptw_phys + 8);
+ *entrylo1 = ldq_phys(cs->as, ptw_phys + 8);
if (sw_pte_lo1) {
*sw_pte_lo1 = *entrylo1 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
}
*entrylo1 = ((*entrylo1 & mask) << (64 - linux_pte_info.softshift)) |
((uint64_t)*entrylo1 >> linux_pte_info.softshift);
- *entrylo1 = (*entrylo1 & 0x3FFFFFFF) |
+ *entrylo1 = (*entrylo1 & get_mtc0_entrylo_mask(env)) |
(*entrylo1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32));
} else {
/* Get the entrylo values from pgt */
- *entrylo0 = ldl_phys(cs->as, ptw_phys);
+ *entrylo0 = ldq_phys(cs->as, ptw_phys);
if (sw_pte_lo0) {
*sw_pte_lo0 = *entrylo0 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
}
*entrylo0 >>= linux_pte_info.softshift;
- *entrylo1 = ldl_phys(cs->as, ptw_phys + 8);
+ *entrylo1 = ldq_phys(cs->as, ptw_phys + 8);
if (sw_pte_lo1) {
*sw_pte_lo1 = *entrylo1 & ((target_ulong)(1 << linux_pte_info.softshift) - 1);
}
@@ -498,12 +583,33 @@ static inline target_ulong cpu_mips_get_pgd(CPUState *cs, target_long bad_vaddr)
uint32_t mask;
} lui, lw, srl;
} handlers[] = {
- /* 2.6.29+ 32-bit Kernel */
+ /* 3.10+ 32-bit R6 Kernel */
+ {
+ KERNEL_RIXI,
+ {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
+ {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
+ {0x2c, 0x003ad002, 0xfffff83f} /* 0x003ad082 : ror k0,k0,#shift */
+ },
+ /* 3.10+ 32-bit R6 Kernel */
+ {
+ KERNEL_RIXI | KERNEL_HIGHMEM,
+ {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
+ {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
+ {0x34, 0x003ad002, 0xfffff83f} /* 0x003ad082 : ror k0,k0,#shift */
+ },
+ /* 3.10+ 32-bit R6 Kernel */
+ {
+ KERNEL_RIXI | KERNEL_HIGHMEM,
+ {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
+ {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
+ {0x38, 0x003ad002, 0xfffff83f} /* 0x003ad082 : ror k0,k0,#shift */
+ },
+ /* 3.10+ 32-bit R2 Kernel */
{
0,
{0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */
{0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */
- {0x34, 0x001ad002, 0xfffff83f} /* 0x001ad002 : srl k0,k0,#shift */
+ {0x2c, 0x001ad002, 0xfffff83f} /* 0x001ad002 : srl k0,k0,#shift */
},
/* 3.10+ 64-bit R2 Kernel */
{
@@ -557,20 +663,13 @@ static inline target_ulong cpu_mips_get_pgd(CPUState *cs, target_long bad_vaddr)
linux_pte_info.pagetable_walk = &pagetable_walk32;
}
- if (config & KERNEL_PGD_C0_CONTEXT) {
- /* swapper_pg_dir address */
- address = (lui_ins & 0xffff) << 16;
- linux_pte_info.swapper_pg_dir = address;
- } else {
- address = (lui_ins & 0xffff) << 16;
- linux_pte_info.swapper_pg_dir = address;
+ /* swapper_pg_dir address */
+ address = (int32_t)((lui_ins & 0xffff) << 16);
+ linux_pte_info.swapper_pg_dir = cpu_debug_translate_address(env, address);
+
+ if (!(config & KERNEL_PGD_C0_CONTEXT)) {
address += (((int32_t)(lw_ins & 0xffff)) << 16) >> 16;
- if (address >= 0x80000000 && address < 0xa0000000)
- address -= 0x80000000;
- else if (address >= 0xa0000000 && address <= 0xc0000000)
- address -= 0xa0000000;
- else
- cpu_abort(cs, "pgd_current_p not in KSEG0/KSEG1\n");
+ address = cpu_debug_translate_address(env, address);
}
linux_pte_info.state = USEFASTTLB;
@@ -745,10 +844,10 @@ hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
int prot, ret;
#if defined(TARGET_MIPS64)
- if (!(linux_pte_info.config & KERNEL_64_BIT) &&
- (linux_pte_info.state == USEFASTTLB))
- addr = ((int64_t)addr << 32) >> 32;
+ if (!(linux_pte_info.config & KERNEL_64_BIT))
+ addr = (int32_t)addr;
#endif
+
ret = get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT);
if (ret != TLBRET_MATCH) {
target_ulong pgd_addr = cpu_mips_get_pgd(cs, addr);