diff options
author | Miodrag Dinic <miodrag.dinic@imgtec.com> | 2015-02-20 17:26:02 +0100 |
---|---|---|
committer | Miodrag Dinic <miodrag.dinic@imgtec.com> | 2015-03-10 12:15:23 +0100 |
commit | 7fa62cff5d1bf5067b9f4f92fd63272356a5b9ff (patch) | |
tree | 8d6577031a6f25c65e4e5af90e5b71dc2886a5bb | |
parent | 055e9546a4a6d86654b99b13177dbba909412d77 (diff) | |
download | qemu-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.c | 55 | ||||
-rw-r--r-- | target-mips/helper.c | 181 |
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); |