Platform Porting

On of the common questions of Codezero users is Is it hard to port Codezero to a new platform? and How to do that?

The answer is: Porting Codezero to new Platform (at least ARM based platform) is quite straightforward and this page is intended to give a starting point to people wanting to use possibilities offered by Codezero on the platform of their choice.

This page is describing only a Codezero hypervisor porting, so all paths are relative to codezero-meta/codezero directory.

Adding new platform to Configuration system

There are files that you must add some code to for a new platform so the building system knows how to handle it.

For start add a configuration symbol in Kconfig system so that new platform can be selected from make menuconfig menu.

--- a/src/platform/Kconfig
+++ b/src/platform/Kconfig
@@ -14,6 +14,10 @@ config HYP_PLAT_PANDA
        select HYP_HAVE_CPU_ARMV7
        select HYP_HAVE_SMP

+config HYP_PLAT_MYPLATOFORM
+       bool "MyPlatform"
+       select HYP_HAVE_CPU_ARMV7
+
 endchoice

and some handling for a new symbol:

--- a/include/usr/l4/includes.h
+++ b/include/usr/l4/includes.h
@@ -8,6 +8,8 @@
 #define CONFIG_HYP_PLATFORM vexpress
 #elif defined(CONFIG_HYP_PLAT_PANDA)
 #define CONFIG_HYP_PLATFORM panda
+#elif defined(CONFIG_HYP_PLAT_MYPLATFORM)
+#define CONFIG_HYP_PLATFORM myplatform
 #else
 #define CONFIG_HYP_ARCH other
 #endif

Platform specific code

Every platform requires their own handling of initialization, setup and platform features. In Codezero the platform specific code is generally kept in src/platform/ subdirectories.

Using existing platforms as a base, create new subdirectory for your platform and put the necessary code there. Then modify Kbuild files so that building system compiles and links the new files.

--- a/src/platform/Kbuild
+++ b/src/platform/Kbuild
@@ -5,3 +5,4 @@
 #obj-$(CONFIG_HYP_PLAT_VEXPRESS) += realview/
 obj-$(CONFIG_HYP_PLAT_VEXPRESS) += vexpress/
 obj-$(CONFIG_HYP_PLAT_PANDA)    += panda/
+obj-$(CONFIG_HYP_PLAT_MYPLATFORM)    += myplatform/

--- /dev/null
+++ b/src/platform/myplatform/Kbuild
@@ -0,0 +1,2 @@
+obj-y += irq.o
+obj-y += platform.o

Some platform specific parts that are worth explaining are:

void platform_init(void) — the main platform initialization function, that will be called at the early stages of hypervisor start. The src/platform/vexpress/platform.c implementation for Versatile Express board is a good example:

void platform_init(void)
{
        init_timer_osc();
        init_platform_console();
        init_platform_timer();
        init_platform_irq_controller();
        init_platform_devices();

#if defined (CONFIG_HYP_SMP)
        init_smp();
        scu_init();
#endif
}

struct platform_mem_regions platform_mem_regions — a structure, containing information about all physical memory space areas that are usable by kernel. Again from the src/platform/vexpress/platform.c implementation for Versatile Express we can present an example:

struct platform_mem_regions platform_mem_regions = {
        .nregions = 3,
        .mem_range = {
                [0] = {
                        .start = PLATFORM_PHYS_MEM_START,
                        .end = PLATFORM_PHYS_MEM_END,
                        .type = MEM_TYPE_RAM,
                },
                [1] = {
                        .start = PLATFORM_DEVICES_START,
                        .end = PLATFORM_DEVICES_END,
                        .type = MEM_TYPE_DEV,
                },
                [2] = {
                        .start = PLATFORM_DEVICES1_START,
                        .end = PLATFORM_DEVICES1_END,
                        .type = MEM_TYPE_DEV,
                },
        },
};

struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] — array describing all the IRQ chips that board has and gluing them with matching IRQ driver handling functions. From the src/platform/vexpress/irq.c:

struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = {
        [0] = {
                .name = "OMAP4 GIC",
                .level = 0,
                .cascade = IRQ_NIL,
                .start = 0,
                .end = IRQ_OFFSET + IRQS_MAX,
                .data = &gic_data[0],
                .ops = {
                        .init = gic_dummy_init,
                        .read_irq = gic_read_irq,
                        .ack_and_mask = gic_ack_and_mask,
                        .unmask = gic_unmask_irq,
                        .set_cpu = gic_set_target,
                },
        },
};

struct irq_desc irq_desc_array[IRQS_MAX] — array describing all the built-in IRQs that will be initialized during the platform initialization stage. From the src/platform/vexpress/irq.c:

struct irq_desc irq_desc_array[IRQS_MAX] = {
        [0 ... IRQ_TIMER2 - 1] = {
                /* TWL6030 Power management unit */
                .chip = &irq_chip_array[0],
                .handler = 0,
        },
        [IRQ_TIMER2] = {
                .chip = &irq_chip_array[0],
                .handler = platform_timer_handler,
        },

        [IRQ_TIMER2 + 1 ... IRQS_MAX - 1] = {
                /* TWL6030 Power management unit */
                .chip = &irq_chip_array[0],
                .handler = 0,
        },

};

void platform_read_uid(unsigned int (*id)[4]) — for platforms supporting some kind of unique hardware ID this function should copy unique id to array given as id. This is later exposed to the guest kernel, so it’s possible to generate persistent, but per-bard unique ids, like i.e. MAC addresses for virtual ethernet devices. For platforms not supporting this feature, it can be a dummy value.

Three drivers that Codezero needs

On each platform Codezero uses three board-specific drivers:

  • an IRQ controller driver for handling IRQs
  • a timer used for hypervisor preemptive scheduling
  • an uart driver for hypervisor console output

If you’re lucky some of the drivers for your platform may already exist or require only a little tuning.

You can find existing drivers source in:

  • src/driver/irq/ and include/l4/drivers/irq/
  • src/drivers/timer/ and include/l4/drivers/timer/
  • src/drvier/uart/ and include/l4/drivers/uart/

directories. Use this as a base for your new drivers. Make sure to update respective Kbuild files to build source file for your platform’s devices.

--- a/src/drivers/irq/Kbuild
+++ b/src/drivers/irq/Kbuild
@@ -1,4 +1,4 @@
 obj-$(CONFIG_HYP_PLAT_VEXPRESS) += gic.o
 obj-$(CONFIG_HYP_PLAT_PANDA)    += omap3_intc.o
+obj-$(CONFIG_HYP_PLAT_MYPLATFORM)    += gic.o

--- a/src/drivers/timer/Kbuild
+++ b/src/drivers/timer/Kbuild
@@ -2,3 +2,4 @@ obj-$(CONFIG_HYP_PLAT_BEAGLE)   += omap3.o
 obj-$(CONFIG_HYP_PLAT_PANDA)    += omap4.o
 obj-$(CONFIG_HYP_PLAT_VEXPRESS) += sp804.o
 obj-$(CONFIG_HYP_PLAT_TEGRA2)   += tegra2.o
+obj-$(CONFIG_HYP_PLAT_MYPLATFORM)   += myplatform.o

--- a/src/drivers/uart/Kbuild
+++ b/src/drivers/uart/Kbuild
@@ -2,3 +2,4 @@ obj-$(CONFIG_HYP_PLAT_TEGRA2)   += 8250.o
 obj-$(CONFIG_HYP_PLAT_PANDA)    += omap.o
 obj-$(CONFIG_HYP_PLAT_BEAGLE)   += omap.o
 obj-$(CONFIG_HYP_PLAT_VEXPRESS) += pl011.o
+obj-$(CONFIG_HYP_PLAT_MYPLATFORM) += myplatform.o

After the above steps, you can proceed to running and debugging your Codezero port on a new platform.

Comments are closed.