android: Add MIPS ranchu virtual board for Android emulation.
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 0a652f8..a9974dd 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,4 +1,5 @@
 obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
 obj-y += addr.o cputimer.o mips_int.o
 obj-$(CONFIG_FULONG) += mips_fulong2e.o
+obj-$(CONFIG_ANDROID) += mips_ranchu.o
 obj-y += gt64xxx_pci.o
diff --git a/hw/mips/mips_ranchu.c b/hw/mips/mips_ranchu.c
new file mode 100644
index 0000000..420d763
--- /dev/null
+++ b/hw/mips/mips_ranchu.c
@@ -0,0 +1,436 @@
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "net/net.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/config-file.h"
+#include "sysemu/char.h"
+#include "monitor/monitor.h"
+#include "hw/misc/android_pipe.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/intc/goldfish_pic.h"
+#include "hw/irq.h"
+
+#ifdef CONFIG_ANDROID
+#include "android/android.h"
+#endif  // CONFIG_ANDROID
+
+#define PHYS_TO_VIRT(x) ((x) | ~(target_ulong)0x7fffffff)
+
+#define MIPS_CPU_SAVE_VERSION 1
+#define MIPS_CPU_IRQ_BASE     8
+
+#define HIGHMEM_OFFSET    0x20000000
+#define GOLDFISH_IO_SPACE 0x1f000000
+
+#define VIRTIO_TRANSPORTS 16
+#define MAX_GF_TTYS       3
+
+#define TYPE_MIPS_RANCHU "mips-ranchu"
+#define MIPS_RANCHU(obj) OBJECT_CHECK(RanchuState, (obj), TYPE_MIPS_RANCHU)
+#define MIPS_RANCHU_REV "1"
+
+typedef struct {
+    SysBusDevice parent_obj;
+
+    qemu_irq *gfpic;
+} RanchuState;
+
+#define MAX_RAM_SIZE_MB 4079UL
+
+enum {
+    RANCHU_GF_PIC,
+    RANCHU_GF_TTY,
+    RANCHU_GF_TIMER,
+    RANCHU_GF_RTC,
+    RANCHU_GF_BATTERY,
+    RANCHU_GF_FB,
+    RANCHU_GF_EVDEV,
+    RANCHU_ANDROID_PIPE,
+    RANCHU_GF_AUDIO,
+    RANCHU_MMIO,
+};
+
+typedef struct DevMapEntry {
+    hwaddr base;
+    hwaddr size;
+    int irq;
+    const char* qemu_name;
+    const char* dt_name;
+    const char* dt_compatible;
+} DevMapEntry;
+
+static DevMapEntry devmap[] = {
+    [RANCHU_GF_PIC] =       { GOLDFISH_IO_SPACE, 0x1000, 0,
+        NULL, "goldfish_pic", "generic,goldfish-pic" },
+    /* ttyGF0 base address remains hardcoded in the kernel.
+     * Early printing (prom_putchar()) relies on finding device
+     * mapped on this address. DT can not be used at that early stage
+     * for acquiring the base address of the device in the kernel.
+     */
+    [RANCHU_GF_TTY] =       { GOLDFISH_IO_SPACE + 0x02000, 0x1000, 2,
+        "goldfish_tty", "goldfish_tty", "generic,goldfish-tty" },
+    /* repeats for a total of MAX_GF_TTYS */
+    [RANCHU_GF_TIMER] =     { GOLDFISH_IO_SPACE + 0x05000, 0x1000, 5,
+        "goldfish_timer", "goldfish_timer", "generic,goldfish-timer" },
+    [RANCHU_GF_RTC] =       { GOLDFISH_IO_SPACE + 0x06000, 0x1000, 6,
+        "goldfish_rtc", "goldfish_rtc", "generic,goldfish-rtc" },
+    [RANCHU_GF_BATTERY] =   { GOLDFISH_IO_SPACE + 0x07000, 0x1000, 7,
+        "goldfish_battery", "goldfish_battery", "generic,goldfish-battery" },
+    [RANCHU_GF_FB] =        { GOLDFISH_IO_SPACE + 0x08000, 0x0100, 8,
+        "goldfish_fb", "goldfish_fb", "generic,goldfish-fb" },
+    [RANCHU_GF_EVDEV] =     { GOLDFISH_IO_SPACE + 0x09000, 0x1000, 9,
+        "goldfish-events", "goldfish_events", "generic,goldfish-events-keypad" },
+    [RANCHU_ANDROID_PIPE] = { GOLDFISH_IO_SPACE + 0x0A000, 0x2000, 10,
+        "android_pipe", "android_pipe", "generic,android-pipe" },
+    [RANCHU_GF_AUDIO] =     { GOLDFISH_IO_SPACE + 0x0C000, 0x0100, 11,
+        "goldfish_audio", "goldfish_audio", "generic,goldfish-audio" },
+    [RANCHU_MMIO] =         { GOLDFISH_IO_SPACE + 0x10000, 0x0200, 16,
+        "virtio-mmio", "virtio_mmio", "virtio,mmio" },
+    /* ...repeating for a total of VIRTIO_TRANSPORTS, each of that size */
+};
+
+struct machine_params {
+    uint64_t kernel_entry;
+    uint64_t cmdline_ptr;
+    unsigned ram_size;
+    unsigned highmem_size;
+    MIPSCPU *cpu;
+} ranchu_params;
+
+static void main_cpu_reset(void* opaque1)
+{
+    struct machine_params *opaque = (struct machine_params *)opaque1;
+    MIPSCPU *cpu = opaque->cpu;
+
+    cpu_reset(CPU(cpu));
+
+    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[6] = opaque->highmem_size;/* a2 */
+    cpu->env.active_tc.gpr[7] = 0;                   /* a3 */
+
+}
+
+static void android_load_kernel(CPUMIPSState *env, int ram_size,
+                                const char *kernel_filename,
+                                const char *kernel_cmdline,
+                                const char *initrd_filename,
+                                void* fdt, int fdt_size)
+{
+    int initrd_size;
+    ram_addr_t initrd_offset;
+    uint64_t kernel_entry, kernel_low, kernel_high;
+    unsigned int cmdline;
+
+    /* Load the kernel.  */
+    if (!kernel_filename) {
+        fprintf(stderr, "Kernel image must be specified\n");
+        exit(1);
+    }
+
+    if (load_elf(kernel_filename, cpu_mips_kseg0_to_phys, NULL,
+        (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
+        (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) {
+        fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+        exit(1);
+    }
+
+    /* Load the DTB at the kernel_high address, that is the place where
+     * kernel with Appended DT support enabled will look for it
+     */
+    if (fdt) {
+        cpu_physical_memory_write(kernel_high, fdt, fdt_size);
+        kernel_high += fdt_size;
+    }
+
+    ranchu_params.kernel_entry = kernel_entry;
+
+    /* load initrd */
+    initrd_size = 0;
+    initrd_offset = 0;
+    if (initrd_filename) {
+        initrd_size = get_image_size (initrd_filename);
+        if (initrd_size > 0) {
+            initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+            if (initrd_offset + initrd_size > ram_size) {
+                fprintf(stderr,
+                    "qemu: memory too small for initial ram disk '%s'\n",
+                    initrd_filename);
+                exit(1);
+            }
+            initrd_size = load_image_targphys(initrd_filename,
+                                               initrd_offset,
+                                               ram_size - initrd_offset);
+        }
+
+        if (initrd_size == (target_ulong) -1) {
+            fprintf(stderr,
+                "qemu: could not load initial ram disk '%s'\n",
+                initrd_filename);
+            exit(1);
+        }
+    }
+
+    /* Store command line in top page of memory
+     * kernel will copy the command line to a local buffer
+     */
+    cmdline = ram_size - TARGET_PAGE_SIZE;
+    char kernel_cmd[1024];
+    if (initrd_size > 0)
+        sprintf (kernel_cmd, "%s rd_start=0x%" HWADDR_PRIx " rd_size=%li",
+                 kernel_cmdline,
+                 (hwaddr)PHYS_TO_VIRT(initrd_offset),
+                 (long int)initrd_size);
+    else
+        strcpy (kernel_cmd, kernel_cmdline);
+
+    /* Setup Highmem */
+    char kernel_cmd2[1024];
+    if (ranchu_params.highmem_size) {
+        sprintf (kernel_cmd2, "%s mem=%um@0x0 mem=%um@0x%x",
+                 kernel_cmd,
+                 GOLDFISH_IO_SPACE / (1024 * 1024),
+                 ranchu_params.highmem_size / (1024 * 1024),
+                 HIGHMEM_OFFSET);
+    } else {
+        strcpy (kernel_cmd2, kernel_cmd);
+    }
+
+    cpu_physical_memory_write(ram_size - TARGET_PAGE_SIZE,
+                              (void *)kernel_cmd2,
+                              strlen(kernel_cmd2) + 1);
+
+    ranchu_params.cmdline_ptr = PHYS_TO_VIRT(cmdline);
+}
+
+/* create_device(void* fdt, DevMapEntry* dev, qemu_irq* pic,
+ *               int num_devices, int is_virtio)
+ *
+ * In case of interrupt controller dev->irq stores
+ * dt handle previously referenced as interrupt-parent
+ *
+ * @fdt - Place where DT nodes will be stored
+ * @dev - Device information (base, irq, name)
+ * @pic - Interrupt controller parent. If NULL, 'intc' node assumed.
+ * @num_devices - If you want to allocate multiple continuous device mappings
+ */
+static void create_device(void* fdt, DevMapEntry* dev, qemu_irq* pic,
+                          int num_devices, int is_virtio)
+{
+    int i;
+
+    for (i = 0; i < num_devices; i++) {
+        hwaddr base = dev->base + i * dev->size;
+
+        char* nodename = g_strdup_printf("/%s@%" PRIx64, dev->dt_name, base);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop(fdt, nodename, "compatible",
+                         dev->dt_compatible,
+                         strlen(dev->dt_compatible) + 1);
+        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);
+            qemu_fdt_setprop_cell(fdt, nodename, "phandle", dev->irq);
+            qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 0x1);
+        } else {
+            qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
+                                   dev->irq + i + MIPS_CPU_IRQ_BASE);
+            if (is_virtio) {
+                /* Note that we have to create the transports in forwards order
+                 * so that command line devices are inserted lowest address first,
+                 * and then add dtb nodes in reverse order so that they appear in
+                 * the finished device tree lowest address first.
+                 */
+                sysbus_create_simple(dev->qemu_name,
+                    dev->base + (num_devices - i - 1) * dev->size,
+                    pic[dev->irq + num_devices - i - 1]);
+            } else {
+                sysbus_create_simple(dev->qemu_name, base, pic[dev->irq + i]);
+            }
+        }
+        g_free(nodename);
+    }
+}
+
+static void ranchu_init(MachineState *machine)
+{
+    MIPSCPU *cpu;
+    CPUMIPSState *env;
+    qemu_irq *goldfish_pic;
+    int i, fdt_size;
+    void *fdt;
+
+    DeviceState *dev = qdev_create(NULL, TYPE_MIPS_RANCHU);
+    RanchuState *s = MIPS_RANCHU(dev);
+
+    MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
+    MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
+
+    /* init CPUs */
+    if (machine->cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+        machine->cpu_model = "MIPS64R2-generic";
+#else
+        machine->cpu_model = "74Kf";
+#endif
+    }
+    cpu = cpu_mips_init(machine->cpu_model);
+    if (cpu == NULL) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+
+    env = &cpu->env;
+
+    register_savevm(NULL,
+                    "cpu",
+                    0,
+                    MIPS_CPU_SAVE_VERSION,
+                    cpu_save,
+                    cpu_load,
+                    env);
+
+    qemu_register_reset(main_cpu_reset, &ranchu_params);
+
+    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);
+
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    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);
+
+    ranchu_params.ram_size = MIN(ram_size, GOLDFISH_IO_SPACE);
+
+    /* 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(), HIGHMEM_OFFSET, ram_hi);
+        ranchu_params.highmem_size = ram_size - GOLDFISH_IO_SPACE;
+    }
+
+    ranchu_params.cpu = cpu;
+
+    cpu_mips_irq_init_cpu(env);
+    cpu_mips_clock_init(env);
+
+    /* Initialize Goldfish PIC */
+    s->gfpic = goldfish_pic = goldfish_interrupt_init(devmap[RANCHU_GF_PIC].base,
+                                            env->irq[2], env->irq[3]);
+    /* Alocate dt handle (label) for interrupt-parent */
+    devmap[RANCHU_GF_PIC].irq = qemu_fdt_alloc_phandle(fdt);
+
+    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", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "interrupt-parent", devmap[RANCHU_GF_PIC].irq);
+
+    /* Firmware node */
+    qemu_fdt_add_subnode(fdt, "/firmware");
+    qemu_fdt_add_subnode(fdt, "/firmware/android");
+    qemu_fdt_setprop_string(fdt, "/firmware/android", "compatible", "android,firmware");
+    qemu_fdt_setprop_string(fdt, "/firmware/android", "hardware", "ranchu");
+    qemu_fdt_setprop_string(fdt, "/firmware/android", "revision", MIPS_RANCHU_REV);
+
+    /* CPU node */
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_add_subnode(fdt, "/cpus/cpu@0");
+    qemu_fdt_setprop_string(fdt, "/cpus/cpu@0", "device_type", "cpu");
+    qemu_fdt_setprop_string(fdt, "/cpus/cpu@0", "compatible", "mti,5KEf");
+
+    /* Memory node */
+    qemu_fdt_add_subnode(fdt, "/memory");
+    qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory");
+    if (ram_size > GOLDFISH_IO_SPACE) {
+        qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg",
+                    1, 0, 2, GOLDFISH_IO_SPACE,
+                    1, HIGHMEM_OFFSET, 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);
+
+    /* Create 4 Goldfish TTYs */
+    create_device(fdt, &devmap[RANCHU_GF_TTY], goldfish_pic, MAX_GF_TTYS, 0);
+
+    /* Other Goldfish Platform devices */
+    for (i = RANCHU_GF_AUDIO; i >= RANCHU_GF_TIMER ; i--) {
+        create_device(fdt, &devmap[i], goldfish_pic, 1, 0);
+    }
+
+    /* Virtio MMIO devices */
+    create_device(fdt, &devmap[RANCHU_MMIO], goldfish_pic, VIRTIO_TRANSPORTS, 1);
+
+    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)
+{
+    return 0;
+}
+
+static void mips_ranchu_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = mips_ranchu_sysbus_device_init;
+}
+
+static const TypeInfo mips_ranchu_device = {
+    .name          = TYPE_MIPS_RANCHU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RanchuState),
+    .class_init    = mips_ranchu_class_init,
+};
+
+static QEMUMachine ranchu_machine = {
+    .name = "ranchu",
+    .desc = "Ranchu Virtual Machine for Android Emulator",
+    .init = ranchu_init,
+    .max_cpus = 1,
+};
+
+static void mips_ranchu_register_types(void)
+{
+    type_register_static(&mips_ranchu_device);
+}
+
+static void ranchu_machine_init(void)
+{
+    qemu_register_machine(&ranchu_machine);
+}
+
+type_init(mips_ranchu_register_types)
+machine_init(ranchu_machine_init);