Patchwork [v8,2/8] ARM: Samsung exynos4210-based boards emulation

login
register
mail settings
Submitter Evgeny Voevodin
Date Jan. 19, 2012, 8:31 a.m.
Message ID <1326961896-25420-3-git-send-email-e.voevodin@samsung.com>
Download mbox | patch
Permalink /patch/136782/
State New
Headers show

Comments

Evgeny Voevodin - Jan. 19, 2012, 8:31 a.m.
Add initial support of NURI and SMDKC210 boards

Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
---
 Makefile.target     |    3 +-
 hw/exynos4210.c     |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/exynos4210.h     |   37 +++++++++
 hw/exynos4_boards.c |  143 ++++++++++++++++++++++++++++++++++++
 4 files changed, 384 insertions(+), 1 deletions(-)
 create mode 100644 hw/exynos4210.c
 create mode 100644 hw/exynos4_boards.c
Peter Maydell - Jan. 19, 2012, 12:19 p.m.
On 19 January 2012 08:31, Evgeny Voevodin <e.voevodin@samsung.com> wrote:

> +    /*
> +     * Secondary CPU startup code will be placed here.
> +     */
> +    memory_region_init_ram(&s->hack_mem, "exynos4210.hack", 0x1000);
> +    memory_region_add_subregion(system_mem, EXYNOS4210_SMP_BOOT_ADDR,
> +            &s->hack_mem);

I've been thinking about this 'hack' memory region, because I figured
out that we didn't actually need it on vexpress (or realview, though
I haven't submitted a patch to fix that yet). Basically the idea is that
we need to put the secondary CPU startup code somewhere where Linux
won't trash it before the secondary cores get started properly. However
this requirement exists also for the real hardware. So the right thing
to do here is identify where the real hardware's boot ROM puts the
secondary CPU holding pen code, and use the same thing. (On vexpress
this is in an area of SDRAM). What does the Exynos4 hardware/bootrom do
here?

> +    /*
> +     * Hack: Map SECOND_CPU_BOOTREG, because it is in PMU USER5 register.
> +     */
> +    memory_region_init_ram(&s->bootreg_mem, "exynos4210.bootreg", 0x4);
> +    memory_region_add_subregion(system_mem, EXYNOS4210_SECOND_CPU_BOOTREG,
> +            &s->bootreg_mem);

If this was modelled as an actual register in a device model with a reset
function we wouldn't have needed the code in arm_boot.c:do_cpu_reset()
that clears smp_bootreg_addr. I think that's an argument for implementing
this as an actual qdev device, however minimal (one that implements
exactly one register would do) rather than as a bit of RAM.

> +static QEMUMachine nuri_machine = {
> +        .name = "nuri",
> +        .desc = "Samsung NURI board (Exynos4210)",
> +        .init = nuri_init,
> +        .max_cpus = EXYNOS4210_MAX_CPUS,
> +};
> +
> +static QEMUMachine smdkc210_machine = {
> +        .name = "smdkc210",
> +        .desc = "Samsung SMDKC210 board (Exynos4210)",
> +        .init = smdkc210_init,
> +        .max_cpus = EXYNOS4210_MAX_CPUS,
> +};

Indentation in these is still wrong.

-- PMM
Evgeny Voevodin - Jan. 19, 2012, 1:15 p.m.
On 01/19/2012 04:19 PM, Peter Maydell wrote:
> On 19 January 2012 08:31, Evgeny Voevodin<e.voevodin@samsung.com>  wrote:
>
>> +    /*
>> +     * Secondary CPU startup code will be placed here.
>> +     */
>> +    memory_region_init_ram(&s->hack_mem, "exynos4210.hack", 0x1000);
>> +    memory_region_add_subregion(system_mem, EXYNOS4210_SMP_BOOT_ADDR,
>> +&s->hack_mem);
> I've been thinking about this 'hack' memory region, because I figured
> out that we didn't actually need it on vexpress (or realview, though
> I haven't submitted a patch to fix that yet). Basically the idea is that
> we need to put the secondary CPU startup code somewhere where Linux
> won't trash it before the secondary cores get started properly. However
> this requirement exists also for the real hardware. So the right thing
> to do here is identify where the real hardware's boot ROM puts the
> secondary CPU holding pen code, and use the same thing. (On vexpress
> this is in an area of SDRAM). What does the Exynos4 hardware/bootrom do
> here?
>
In our case it is not a hack. Secondary CPU boot loader resides in ROM 
memory.
And we have mapped a region in the ROM to place our boot-loader code there.
Here all is correct.
>> +    /*
>> +     * Hack: Map SECOND_CPU_BOOTREG, because it is in PMU USER5 register.
>> +     */
>> +    memory_region_init_ram(&s->bootreg_mem, "exynos4210.bootreg", 0x4);
>> +    memory_region_add_subregion(system_mem, EXYNOS4210_SECOND_CPU_BOOTREG,
>> +&s->bootreg_mem);
> If this was modelled as an actual register in a device model with a reset
> function we wouldn't have needed the code in arm_boot.c:do_cpu_reset()
> that clears smp_bootreg_addr. I think that's an argument for implementing
> this as an actual qdev device, however minimal (one that implements
> exactly one register would do) rather than as a bit of RAM.
>
Boot reg could be in the RAM on other platforms and code which is 
monitoring this address
is awaiting a non-zero value here. If we would not clear this region, wrong
boot address for secondary CPU will be read. So, code to clear this region
should be at reset function anyway.
I think that it is a matter of current board how to manage boot address -
map a region or implement a device.

>> +static QEMUMachine nuri_machine = {
>> +        .name = "nuri",
>> +        .desc = "Samsung NURI board (Exynos4210)",
>> +        .init = nuri_init,
>> +        .max_cpus = EXYNOS4210_MAX_CPUS,
>> +};
>> +
>> +static QEMUMachine smdkc210_machine = {
>> +        .name = "smdkc210",
>> +        .desc = "Samsung SMDKC210 board (Exynos4210)",
>> +        .init = smdkc210_init,
>> +        .max_cpus = EXYNOS4210_MAX_CPUS,
>> +};
> Indentation in these is still wrong.
>
Sorry, will fix.
> -- PMM
>
Andreas Färber - Jan. 19, 2012, 1:17 p.m.
Evgeny,

Am 19.01.2012 09:31, schrieb Evgeny Voevodin:
> Add initial support of NURI and SMDKC210 boards
> 
> Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>

Could you please supply examples of how to use these? Whether for the
Release Notes or as part of the commit message (or both).

What's the NURI board? I don't see it listed here:
http://www.samsung.com/global/business/semiconductor/products/mobilesoc/Exynos/platform.html

Also, what's missing for emulation of the exynos4210-based Origen board?
The SDHC controller that depends on the VMState changes?

Thanks,
Andreas
Peter Maydell - Jan. 19, 2012, 1:26 p.m.
On 19 January 2012 13:15, Evgeny Voevodin <e.voevodin@samsung.com> wrote:
> On 01/19/2012 04:19 PM, Peter Maydell wrote:
>>
>> On 19 January 2012 08:31, Evgeny Voevodin<e.voevodin@samsung.com>  wrote:
>>
>>> +    /*
>>> +     * Secondary CPU startup code will be placed here.
>>> +     */
>>> +    memory_region_init_ram(&s->hack_mem, "exynos4210.hack", 0x1000);
>>> +    memory_region_add_subregion(system_mem, EXYNOS4210_SMP_BOOT_ADDR,
>>> +&s->hack_mem);
>>
>> I've been thinking about this 'hack' memory region, because I figured
>> out that we didn't actually need it on vexpress (or realview, though
>> I haven't submitted a patch to fix that yet). Basically the idea is that
>> we need to put the secondary CPU startup code somewhere where Linux
>> won't trash it before the secondary cores get started properly. However
>> this requirement exists also for the real hardware. So the right thing
>> to do here is identify where the real hardware's boot ROM puts the
>> secondary CPU holding pen code, and use the same thing. (On vexpress
>> this is in an area of SDRAM). What does the Exynos4 hardware/bootrom do
>> here?
>>
> In our case it is not a hack. Secondary CPU boot loader resides in ROM
> memory.
> And we have mapped a region in the ROM to place our boot-loader code there.
> Here all is correct.

If this is ROM then (a) don't model it as RAM (b) don't call it "hack"
if it's actually justifiable (c) justify it in a comment.

>> If this was modelled as an actual register in a device model with a reset
>> function we wouldn't have needed the code in arm_boot.c:do_cpu_reset()
>> that clears smp_bootreg_addr. I think that's an argument for implementing
>> this as an actual qdev device, however minimal (one that implements
>> exactly one register would do) rather than as a bit of RAM.
>>
> Boot reg could be in the RAM on other platforms and code which is monitoring
> this address is awaiting a non-zero value here. If we would not clear this
> region, wrong boot address for secondary CPU will be read. So, code to clear
> this region should be at reset function anyway.

On *what* other platforms? On realview/vexpress the smp_bootreg_addr is
a device register so is cleared at reset. Highbank has its own complicated
thing (which is going to be implemented via some hook code). Basically
having code in do_cpu_reset() is doing something hardware doesn't do.
On hardware we will either (if the location is a device) clear the value
as part of that device's reset, or (if it is actually RAM) clear the value
as part of the code executed by one of the CPUs as it boots.

> I think that it is a matter of current board how to manage boot address -
> map a region or implement a device.

Mapping a RAM region in the model when the hardware implements the
device is drifting away from modelling what the hardware actually
does. When that drift means we end up needing workarounds in
generic code like arm_boot.c that's a cause for concern and a
reason to avoid that drift.

-- PMM
Kyungmin Park - Jan. 20, 2012, 7:18 a.m.
On 1/19/12, Andreas Färber <afaerber@suse.de> wrote:
> Evgeny,
>
> Am 19.01.2012 09:31, schrieb Evgeny Voevodin:
>> Add initial support of NURI and SMDKC210 boards
>>
>> Signed-off-by: Evgeny Voevodin <e.voevodin@samsung.com>
>
> Could you please supply examples of how to use these? Whether for the
> Release Notes or as part of the commit message (or both).
>
> What's the NURI board? I don't see it listed here:

The Nuri board based on s5pc210 (aka exynos4210) is mobile reference board.
As SMDKC210 is SoC reference board. nuri board contains mobile
specific devices, e.g., NFC, CP, and so on. Of course these device
drivers are not mainlined at kernel.

Now chip vendor in this case System LSI uses the SMDK board, but nuri
and other reference board are used at other division. In case of Nuri
it's used at mobile division.

> http://www.samsung.com/global/business/semiconductor/products/mobilesoc/Exynos/platform.html
>
> Also, what's missing for emulation of the exynos4210-based Origen board?
SMDK board is used at System Lsi.
Nuri board is used at Mobile
Origen board is used at Linaro mainly.

Now Russia Research are work on Nuri board. so no need to support
Origen board at here. It's not their task. maybe you can add origen
board support.

Thank you,
Kyungmin Park
> The SDHC controller that depends on the VMState changes?
>
> Thanks,
> Andreas
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
>
>

Patch

diff --git a/Makefile.target b/Makefile.target
index 4ac257e..6199d44 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -339,7 +339,8 @@  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
-obj-arm-y += exynos4210_gic.o exynos4210_combiner.o
+obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
+obj-arm-y += exynos4_boards.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
new file mode 100644
index 0000000..82755db
--- /dev/null
+++ b/hw/exynos4210.c
@@ -0,0 +1,202 @@ 
+/*
+ *  Samsung exynos4210 SoC emulation
+ *
+ *  Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *    Evgeny Voevodin <e.voevodin@samsung.com>
+ *    Igor Mitsyanko  <i.mitsyanko@samsung.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "boards.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exynos4210.h"
+
+#define EXYNOS4210_CHIPID_ADDR         0x10000000
+
+/* External GIC */
+#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR    0x10480000
+#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR   0x10490000
+
+/* Combiner */
+#define EXYNOS4210_EXT_COMBINER_BASE_ADDR   0x10440000
+#define EXYNOS4210_INT_COMBINER_BASE_ADDR   0x10448000
+
+static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43,
+                                    0x09, 0x00, 0x00, 0x00 };
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+        unsigned long ram_size)
+{
+    qemu_irq cpu_irq[4];
+    int n;
+    Exynos4210State *s = g_new(Exynos4210State, 1);
+    qemu_irq *irq_table;
+    qemu_irq *irqp;
+    qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS];
+    unsigned long mem_size;
+    DeviceState *dev;
+    SysBusDevice *busdev;
+
+    for (n = 0; n < smp_cpus; n++) {
+        s->env[n] = cpu_init("cortex-a9");
+        if (!s->env[n]) {
+            fprintf(stderr, "Unable to find CPU %d definition\n", n);
+            exit(1);
+        }
+        /* Create PIC controller for each processor instance */
+        irqp = arm_pic_init_cpu(s->env[n]);
+
+        /*
+         * Get GICs gpio_in cpu_irq to connect a combiner to them later.
+         * Use only IRQ for a while.
+         */
+        cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+    }
+
+    /*** IRQs ***/
+
+    s->irq_table = exynos4210_init_irq(&s->irqs);
+    irq_table = s->irq_table;
+
+    /* IRQ Gate */
+    dev = qdev_create(NULL, "exynos4210.irq_gate");
+    qdev_init_nofail(dev);
+    /* Get IRQ Gate input in gate_irq */
+    for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) {
+        gate_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+    busdev = sysbus_from_qdev(dev);
+    /* Connect IRQ Gate output to cpu_irq */
+    for (n = 0; n < smp_cpus; n++) {
+        sysbus_connect_irq(busdev, n, cpu_irq[n]);
+    }
+
+    /* Private memory region and Internal GIC */
+    dev = qdev_create(NULL, "a9mpcore_priv");
+    qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR);
+    for (n = 0; n < smp_cpus; n++) {
+        sysbus_connect_irq(busdev, n, gate_irq[n * 2]);
+    }
+    for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) {
+        s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* External GIC */
+    dev = qdev_create(NULL, "exynos4210.gic");
+    qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    /* Map CPU interface */
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR);
+    /* Map Distributer interface */
+    sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR);
+    for (n = 0; n < smp_cpus; n++) {
+        sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]);
+    }
+    for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) {
+        s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* Internal Interrupt Combiner */
+    dev = qdev_create(NULL, "exynos4210.combiner");
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+        sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]);
+    }
+    exynos4210_combiner_get_gpioin(&s->irqs, dev, 0);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR);
+
+    /* External Interrupt Combiner */
+    dev = qdev_create(NULL, "exynos4210.combiner");
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) {
+        sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]);
+    }
+    exynos4210_combiner_get_gpioin(&s->irqs, dev, 1);
+    sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR);
+    qdev_prop_set_uint32(dev, "external", 1);
+
+    /* Initialize board IRQs. */
+    exynos4210_init_board_irqs(&s->irqs);
+
+    /*** Memory ***/
+
+    /* Chip-ID and OMR */
+    memory_region_init_ram_ptr(&s->chipid_mem, "exynos4210.chipid",
+            sizeof(chipid_and_omr), chipid_and_omr);
+    memory_region_set_readonly(&s->chipid_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR,
+                                &s->chipid_mem);
+
+    /* Internal ROM */
+    memory_region_init_ram(&s->irom_mem, "exynos4210.irom",
+                           EXYNOS4210_IROM_SIZE);
+    memory_region_set_readonly(&s->irom_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
+                                &s->irom_mem);
+    /* mirror of 0x0 – 0x10000 */
+    memory_region_init_alias(&s->irom_alias_mem, "exynos4210.irom_alias",
+            &s->irom_mem, 0, EXYNOS4210_IROM_SIZE);
+    memory_region_set_readonly(&s->irom_alias_mem, true);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR,
+            &s->irom_alias_mem);
+
+    /* Internal RAM */
+    memory_region_init_ram(&s->iram_mem, "exynos4210.iram",
+                           EXYNOS4210_IRAM_SIZE);
+    vmstate_register_ram_global(&s->iram_mem);
+    memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
+                                &s->iram_mem);
+
+    /* DRAM */
+    mem_size = ram_size;
+    if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
+        memory_region_init_ram(&s->dram1_mem, "exynos4210.dram1",
+                mem_size - EXYNOS4210_DRAM_MAX_SIZE);
+        vmstate_register_ram_global(&s->dram1_mem);
+        memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
+                &s->dram1_mem);
+        mem_size = EXYNOS4210_DRAM_MAX_SIZE;
+    }
+    memory_region_init_ram(&s->dram0_mem, "exynos4210.dram0", mem_size);
+    vmstate_register_ram_global(&s->dram0_mem);
+    memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
+            &s->dram0_mem);
+
+    /*
+     * Secondary CPU startup code will be placed here.
+     */
+    memory_region_init_ram(&s->hack_mem, "exynos4210.hack", 0x1000);
+    memory_region_add_subregion(system_mem, EXYNOS4210_SMP_BOOT_ADDR,
+            &s->hack_mem);
+
+    /*
+     * Hack: Map SECOND_CPU_BOOTREG, because it is in PMU USER5 register.
+     */
+    memory_region_init_ram(&s->bootreg_mem, "exynos4210.bootreg", 0x4);
+    memory_region_add_subregion(system_mem, EXYNOS4210_SECOND_CPU_BOOTREG,
+            &s->bootreg_mem);
+
+    return s;
+}
diff --git a/hw/exynos4210.h b/hw/exynos4210.h
index cef264b..a68900d 100644
--- a/hw/exynos4210.h
+++ b/hw/exynos4210.h
@@ -31,6 +31,25 @@ 
 
 #define EXYNOS4210_MAX_CPUS                 2
 
+#define EXYNOS4210_DRAM0_BASE_ADDR          0x40000000
+#define EXYNOS4210_DRAM1_BASE_ADDR          0xa0000000
+#define EXYNOS4210_DRAM_MAX_SIZE            0x60000000  /* 1.5 GB */
+
+#define EXYNOS4210_IROM_BASE_ADDR           0x00000000
+#define EXYNOS4210_IROM_SIZE                0x00010000  /* 64 KB */
+#define EXYNOS4210_IROM_MIRROR_BASE_ADDR    0x02000000
+#define EXYNOS4210_IROM_MIRROR_SIZE         0x00010000  /* 64 KB */
+
+#define EXYNOS4210_IRAM_BASE_ADDR           0x02020000
+#define EXYNOS4210_IRAM_SIZE                0x00020000  /* 128 KB */
+
+/* Secondary CPU startup code is in IROM memory */
+#define EXYNOS4210_SMP_BOOT_ADDR            EXYNOS4210_IROM_BASE_ADDR
+#define EXYNOS4210_BASE_BOOT_ADDR           EXYNOS4210_DRAM0_BASE_ADDR
+/* Secondary CPU polling address to get loader start from */
+#define EXYNOS4210_SECOND_CPU_BOOTREG       0x10020814
+#define EXYNOS4210_SMP_PRIVATE_BASE_ADDR    0x10500000
+
 /*
  * exynos4210 IRQ subsystem stub definitions.
  */
@@ -60,6 +79,24 @@  typedef struct Exynos4210Irq {
     qemu_irq board_irqs[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ];
 } Exynos4210Irq;
 
+typedef struct Exynos4210State {
+    CPUState * env[EXYNOS4210_MAX_CPUS];
+    Exynos4210Irq irqs;
+    qemu_irq *irq_table;
+
+    MemoryRegion chipid_mem;
+    MemoryRegion iram_mem;
+    MemoryRegion irom_mem;
+    MemoryRegion irom_alias_mem;
+    MemoryRegion dram0_mem;
+    MemoryRegion dram1_mem;
+    MemoryRegion hack_mem;
+    MemoryRegion bootreg_mem;
+} Exynos4210State;
+
+Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
+        unsigned long ram_size);
+
 /* Initialize exynos4210 IRQ subsystem stub */
 qemu_irq *exynos4210_init_irq(Exynos4210Irq *env);
 
diff --git a/hw/exynos4_boards.c b/hw/exynos4_boards.c
new file mode 100644
index 0000000..5eb59c7
--- /dev/null
+++ b/hw/exynos4_boards.c
@@ -0,0 +1,143 @@ 
+/*
+ *  Samsung exynos4 SoC based boards emulation
+ *
+ *  Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *    Maksim Kozlov <m.kozlov@samsung.com>
+ *    Evgeny Voevodin <e.voevodin@samsung.com>
+ *    Igor Mitsyanko  <i.mitsyanko@samsung.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "exec-memory.h"
+#include "exynos4210.h"
+#include "boards.h"
+
+#undef DEBUG
+
+//#define DEBUG
+
+#ifdef DEBUG
+    #undef PRINT_DEBUG
+    #define  PRINT_DEBUG(fmt, args...) \
+        do { \
+            fprintf(stderr, "  [%s:%d]   "fmt, __func__, __LINE__, ##args); \
+        } while (0)
+#else
+    #define  PRINT_DEBUG(fmt, args...)  do {} while (0)
+#endif
+
+typedef enum exynos4_board_type {
+    EXYNOS4_BOARD_NURI,
+    EXYNOS4_BOARD_SMDKC210,
+    EXYNOS4_NUM_OF_BOARDS
+} exynos4_board_type;
+
+static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = 0xD33,
+    [EXYNOS4_BOARD_SMDKC210] = 0xB16,
+};
+
+static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = EXYNOS4210_SECOND_CPU_BOOTREG,
+    [EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG,
+};
+
+static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = {
+    [EXYNOS4_BOARD_NURI]     = 0x40000000,
+    [EXYNOS4_BOARD_SMDKC210] = 0x40000000,
+};
+
+static struct arm_boot_info exynos4_board_binfo = {
+    .loader_start     = EXYNOS4210_BASE_BOOT_ADDR,
+    .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR,
+};
+
+static Exynos4210State *exynos4_boards_init_common(
+        const char *kernel_filename,
+        const char *kernel_cmdline,
+        const char *initrd_filename,
+        exynos4_board_type board_type)
+{
+    exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
+    exynos4_board_binfo.board_id = exynos4_board_id[board_type];
+    exynos4_board_binfo.smp_bootreg_addr =
+            exynos4_board_smp_bootreg_addr[board_type];
+    exynos4_board_binfo.nb_cpus = smp_cpus;
+    exynos4_board_binfo.kernel_filename = kernel_filename;
+    exynos4_board_binfo.initrd_filename = initrd_filename;
+    exynos4_board_binfo.kernel_cmdline = kernel_cmdline;
+    exynos4_board_binfo.smp_priv_base = EXYNOS4210_SMP_PRIVATE_BASE_ADDR;
+
+    PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n"
+            " kernel_filename: %s\n"
+            " kernel_cmdline: %s\n"
+            " initrd_filename: %s\n",
+            exynos4_board_ram_size[board_type] / 1048576,
+            exynos4_board_ram_size[board_type],
+            kernel_filename,
+            kernel_cmdline,
+            initrd_filename);
+
+    return exynos4210_init(get_system_memory(),
+            exynos4_board_ram_size[board_type]);
+}
+
+static void nuri_init(ram_addr_t ram_size,
+        const char *boot_device,
+        const char *kernel_filename, const char *kernel_cmdline,
+        const char *initrd_filename, const char *cpu_model)
+{
+    exynos4_boards_init_common(kernel_filename, kernel_cmdline,
+            initrd_filename, EXYNOS4_BOARD_NURI);
+
+    arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static void smdkc210_init(ram_addr_t ram_size,
+        const char *boot_device,
+        const char *kernel_filename, const char *kernel_cmdline,
+        const char *initrd_filename, const char *cpu_model)
+{
+    exynos4_boards_init_common(kernel_filename, kernel_cmdline,
+            initrd_filename, EXYNOS4_BOARD_SMDKC210);
+
+    arm_load_kernel(first_cpu, &exynos4_board_binfo);
+}
+
+static QEMUMachine nuri_machine = {
+        .name = "nuri",
+        .desc = "Samsung NURI board (Exynos4210)",
+        .init = nuri_init,
+        .max_cpus = EXYNOS4210_MAX_CPUS,
+};
+
+static QEMUMachine smdkc210_machine = {
+        .name = "smdkc210",
+        .desc = "Samsung SMDKC210 board (Exynos4210)",
+        .init = smdkc210_init,
+        .max_cpus = EXYNOS4210_MAX_CPUS,
+};
+
+static void exynos4_machine_init(void)
+{
+    qemu_register_machine(&nuri_machine);
+    qemu_register_machine(&smdkc210_machine);
+}
+
+machine_init(exynos4_machine_init);