diff --git a/os/run/vmm.run b/os/run/vmm.run new file mode 100644 index 0000000000..39a1af756f --- /dev/null +++ b/os/run/vmm.run @@ -0,0 +1,39 @@ +# +# \brief Virtual-machine monitor demo +# \author Stefan Kalkowski +# \date 2012-06-25 +# + +if {![have_spec trustzone]} { + puts "\nThe VMM support base-hw for Versatile Express with TrustZone support only\n" + exit 0 +} + +build "core init server/vmm" +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init vmm linux initrd.gz" diff --git a/os/src/server/vmm/README b/os/src/server/vmm/README new file mode 100644 index 0000000000..6afb8fc340 --- /dev/null +++ b/os/src/server/vmm/README @@ -0,0 +1,17 @@ +This is a small example virtual machine monitor, that uses the base-hw kernel +as TrustZone micro-hypervisor on the ARM Versatile Express CT A9x4 platform. +The VMM configures TrustZone Protection Controller and Address Space Controller +in a way, that allows a guest to access nearly all devices, and the DDR-RAM. +Only few resources needed by the kernel (timer, SRAM) aren't accessable by the +virtual-machine. + +Moreover, the VMM prepares the guest memory with a Linux image, and ramdisk, +and boots it. For the Linux guest to work properly a small patch, and tweaked +configuration is needed. Please checkout the following branch to test it: + + https://github.com/skalk/linux/tree/vexpress-tz + +To build linux do: + +! make ARCH=arm CROSS_COMPILE= vexpress_tz_defconfig +! make ARCH=arm CROSS_COMPILE= \ No newline at end of file diff --git a/os/src/server/vmm/include/atag.h b/os/src/server/vmm/include/atag.h new file mode 100644 index 0000000000..03fec3d46d --- /dev/null +++ b/os/src/server/vmm/include/atag.h @@ -0,0 +1,193 @@ +/** + * \brief Arm boot descriptor tags (ATAGs). + * \author Stefan Kalkowski + * \date 2012-07-30 + * + * Based on the code example of Vincent Sanders (published under BSD licence): + * http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _SRC__SERVER__VMM__INCLUDE__ATAG_H_ +#define _SRC__SERVER__VMM__INCLUDE__ATAG_H_ + +#include +#include + +class Atag { + + private: + + enum atags { + ATAG_NONE = 0x00000000, + ATAG_CORE = 0x54410001, + ATAG_MEM = 0x54410002, + ATAG_VIDEOTEXT = 0x54410003, + ATAG_RAMDISK = 0x54410004, + ATAG_INITRD2 = 0x54420005, + ATAG_SERIAL = 0x54410006, + ATAG_REVISION = 0x54410007, + ATAG_VIDEOLFB = 0x54410008, + ATAG_CMDLINE = 0x54410009, + }; + + struct atag_header { + Genode::uint32_t size; + Genode::uint32_t tag; + }; + + struct atag_core { + Genode::uint32_t flags; + Genode::uint32_t pagesize; + Genode::uint32_t rootdev; + }; + + struct atag_mem { + Genode::uint32_t size; + Genode::uint32_t start; + }; + + struct atag_videotext { + Genode::uint8_t x; + Genode::uint8_t y; + Genode::uint16_t video_page; + Genode::uint8_t video_mode; + Genode::uint8_t video_cols; + Genode::uint16_t video_ega_bx; + Genode::uint8_t video_lines; + Genode::uint8_t video_isvga; + Genode::uint16_t video_points; + }; + + struct atag_ramdisk { + Genode::uint32_t flags; + Genode::uint32_t size; + Genode::uint32_t start; + }; + + struct atag_initrd2 { + Genode::uint32_t start; + Genode::uint32_t size; + }; + + struct atag_serialnr { + Genode::uint32_t low; + Genode::uint32_t high; + }; + + struct atag_revision { + Genode::uint32_t rev; + }; + + struct atag_videolfb { + Genode::uint16_t lfb_width; + Genode::uint16_t lfb_height; + Genode::uint16_t lfb_depth; + Genode::uint16_t lfb_linelength; + Genode::uint32_t lfb_base; + Genode::uint32_t lfb_size; + Genode::uint8_t red_size; + Genode::uint8_t red_pos; + Genode::uint8_t green_size; + Genode::uint8_t green_pos; + Genode::uint8_t blue_size; + Genode::uint8_t blue_pos; + Genode::uint8_t rsvd_size; + Genode::uint8_t rsvd_pos; + }; + + struct atag_cmdline { + char cmdline[1]; + }; + + struct atag { + struct atag_header hdr; + union { + struct atag_core core; + struct atag_mem mem; + struct atag_videotext videotext; + struct atag_ramdisk ramdisk; + struct atag_initrd2 initrd2; + struct atag_serialnr serialnr; + struct atag_revision revision; + struct atag_videolfb videolfb; + struct atag_cmdline cmdline; + } u; + }; + + struct atag *_params; /* used to point at the current tag */ + + inline void _next() { + _params = ((struct atag *)((Genode::uint32_t *)(_params) + + _params->hdr.size)); } + + template + Genode::size_t _size() { + return ((sizeof(struct atag_header) + sizeof(TAG)) >> 2); } + + public: + + Atag(void* base) : _params((struct atag *)base) + { + _params->hdr.tag = ATAG_CORE; + _params->hdr.size = _size(); + _params->u.core.flags = 1; + _params->u.core.pagesize = 0x1000; + _params->u.core.rootdev = 0; + _next(); + } + + void setup_ramdisk_tag(Genode::size_t size) + { + _params->hdr.tag = ATAG_RAMDISK; + _params->hdr.size = _size(); + _params->u.ramdisk.flags = 0; + _params->u.ramdisk.size = size; + _params->u.ramdisk.start = 0; + _next(); + } + + void setup_initrd2_tag(Genode::addr_t start, Genode::size_t size) + { + _params->hdr.tag = ATAG_INITRD2; + _params->hdr.size = _size(); + _params->u.initrd2.start = start; + _params->u.initrd2.size = size; + _next(); + } + + void setup_mem_tag(Genode::addr_t start, Genode::size_t len) + { + _params->hdr.tag = ATAG_MEM; + _params->hdr.size = _size(); + _params->u.mem.start = start; + _params->u.mem.size = len; + _next(); + } + + void setup_cmdline_tag(const char * line) + { + int len = Genode::strlen(line); + if(!len) + return; + + _params->hdr.tag = ATAG_CMDLINE; + _params->hdr.size = (sizeof(struct atag_header) + len + 1 + 4) >> 2; + Genode::strncpy(_params->u.cmdline.cmdline, line, len + 1); + _next(); + } + + void setup_end_tag(void) + { + _params->hdr.tag = ATAG_NONE; + _params->hdr.size = 0; + } +}; + +#endif /* _SRC__SERVER__VMM__INCLUDE__ATAG_H_ */ diff --git a/os/src/server/vmm/include/bp_147.h b/os/src/server/vmm/include/bp_147.h new file mode 100644 index 0000000000..82fc9f9198 --- /dev/null +++ b/os/src/server/vmm/include/bp_147.h @@ -0,0 +1,130 @@ +/* + * \brief Driver for the Trustzone Protection Controller BP147 + * \author Stefan Kalkowski + * \date 2012-07-04 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BASE_HW__SRC__SERVER__VMM__BP_147_H_ +#define _BASE_HW__SRC__SERVER__VMM__BP_147_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + + class Bp_147 : Mmio + { + private: + + /** + * Secure RAM Region Size Register + */ + struct Tzpcr0size : public Register<0x00, 32> + { + struct R0size : Bitfield<0,10> { }; + }; + + /** + * Decode Protection 0 Registers + */ + template + struct Tzpcdecprot0 : public Register + { + struct Pl341_apb : Register::template Bitfield<0,1> {}; + struct Pl354_apb : Register::template Bitfield<1,1> {}; + struct Scc : Register::template Bitfield<2,1> {}; + struct Dual_timer : Register::template Bitfield<4,1> {}; + struct Watchdog : Register::template Bitfield<5,1> {}; + struct Tzpc : Register::template Bitfield<6,1> {}; + struct Pl351_apb : Register::template Bitfield<7,1> {}; + struct Fast_pl301_apb : Register::template Bitfield<9,1> {}; + struct Slow_pl301_apb : Register::template Bitfield<10,1> {}; + struct Dmc_tzasc : Register::template Bitfield<12,1> {}; + struct Nmc_tzasc : Register::template Bitfield<12,1> {}; + struct Smc_tzasc : Register::template Bitfield<13,1> {}; + struct Debug_apb_phs : Register::template Bitfield<14,1> {}; + }; + + /** + * Decode Protection 1 Registers + */ + template + struct Tzpcdecprot1 : public Register + { + struct External_axi_slave_port + : Register::template Bitfield<0,1> {}; + /* SMC access */ + struct Pl354_axi + : Register::template Bitfield<1,1> {}; + struct Pl351_axi + : Register::template Bitfield<2,1> {}; + struct Entire_apb + : Register::template Bitfield<3,1> {}; + struct Pl111_configuration_port + : Register::template Bitfield<4,1> {}; + struct Axi_ram + : Register::template Bitfield<5,1> {}; + /* DDR RAM access */ + struct Pl341_axi + : Register::template Bitfield<6,1> {}; + /* ACP access */ + struct Cortexa9_coherency_port + : Register::template Bitfield<8,1> {}; + struct Entire_slow_axi_system + : Register::template Bitfield<9,1> {}; + }; + + /** + * Decode Protection 2 Registers + */ + template + struct Tzpcdecprot2 : public Register + { + struct External_master_tz : Register::template Bitfield<0,1> {}; + struct Dap_tz_override : Register::template Bitfield<1,1> {}; + struct Pl111_master_tz : Register::template Bitfield<2,1> {}; + struct Dmc_tzasc_lockdown : Register::template Bitfield<3,1> {}; + struct Nmc_tzasc_lockdown : Register::template Bitfield<4,1> {}; + struct Smc_tzasc_lockdown : Register::template Bitfield<5,1> {}; + }; + + struct Tzpcdecprot0stat : Tzpcdecprot0<0x800> {}; + struct Tzpcdecprot0set : Tzpcdecprot0<0x804> {}; + struct Tzpcdecprot0clr : Tzpcdecprot0<0x808> {}; + struct Tzpcdecprot1stat : Tzpcdecprot1<0x80c> {}; + struct Tzpcdecprot1set : Tzpcdecprot1<0x810> {}; + struct Tzpcdecprot1clr : Tzpcdecprot1<0x814> {}; + struct Tzpcdecprot2stat : Tzpcdecprot2<0x818> {}; + struct Tzpcdecprot2set : Tzpcdecprot2<0x81c> {}; + struct Tzpcdecprot2clr : Tzpcdecprot2<0x820> {}; + + public: + + Bp_147(addr_t const base) : Mmio(base) + { + /** + * Configure TZPC to allow non-secure AXI signals to + * Static Memory Controller (SMC), + * Dynamic Memory Controller (DMC), + * Accelerator Coherency Port (ACP), and + * PL111 configuration registers + */ + write( + Tzpcdecprot1set::Pl341_axi::bits(1) | + Tzpcdecprot1set::Pl354_axi::bits(1) | + Tzpcdecprot1set::Cortexa9_coherency_port::bits(1) | + Tzpcdecprot1set::Pl111_configuration_port::bits(1)); + } + }; + +} + +#endif /* _BASE_HW__SRC__SERVER__VMM__BP_147_H_ */ diff --git a/os/src/server/vmm/include/sp810.h b/os/src/server/vmm/include/sp810.h new file mode 100644 index 0000000000..d5e87224ce --- /dev/null +++ b/os/src/server/vmm/include/sp810.h @@ -0,0 +1,45 @@ +/* + * \brief Driver for the SP810 system controller + * \author Stefan Kalkowski + * \date 2012-09-21 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BASE_HW__SRC__SERVER__VMM__810_H_ +#define _BASE_HW__SRC__SERVER__VMM__810_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + + class Sp810 : Mmio + { + private: + + struct Ctrl : public Register<0, 32> + { + struct Timer0_enable : Bitfield<15,1> {}; + struct Timer1_enable : Bitfield<17,1> {}; + }; + + public: + + Sp810(addr_t const base) : Mmio(base) {} + + bool timer0() { return read(); } + bool timer1() { return read(); } + + void enable_timer0() { write(1); } + void enable_timer1() { write(1); } + }; +} + +#endif /* _BASE_HW__SRC__SERVER__VMM__SP810_H_ */ diff --git a/os/src/server/vmm/include/sys_reg.h b/os/src/server/vmm/include/sys_reg.h new file mode 100644 index 0000000000..ff11129dbe --- /dev/null +++ b/os/src/server/vmm/include/sys_reg.h @@ -0,0 +1,104 @@ +/* + * \brief Driver for the Motherboard Express system registers + * \author Stefan Kalkowski + * \date 2012-09-21 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BASE_HW__SRC__SERVER__VMM__SYS_REG_H_ +#define _BASE_HW__SRC__SERVER__VMM__SYS_REG_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + + class Sys_reg : Mmio + { + private: + + struct Sys_mci : public Register<0x48, 32> {}; + + struct Sys_24mhz : public Register<0x5c, 32> {}; + + struct Sys_misc : public Register<0x60, 32> {}; + + struct Sys_cfg_data : public Register<0xa0, 32, true> {}; + + struct Sys_cfg_ctrl : public Register<0xa4, 32, true> + { + struct Device : Bitfield<0,12> { }; + struct Position : Bitfield<12,4> { }; + struct Site : Bitfield<16,2> { }; + struct Function : Bitfield<20,6> { }; + struct Write : Bitfield<30,1> { }; + struct Start : Bitfield<31,1> { }; + }; + + struct Sys_cfg_stat : public Register<0xa8, 32> + { + struct Complete : Bitfield<0,1> { }; + struct Error : Bitfield<1,1> { }; + }; + + public: + + Sys_reg(addr_t const base) : Mmio(base) {} + + uint32_t counter() { return read(); } + + uint32_t misc_flags() { return read(); } + + void osc1(uint32_t mhz) + { + write(0); + write(mhz); + write(Sys_cfg_ctrl::Device::bits(1) | + Sys_cfg_ctrl::Site::bits(1) | + Sys_cfg_ctrl::Function::bits(1) | + Sys_cfg_ctrl::Write::bits(1) | + Sys_cfg_ctrl::Start::bits(1)); + while (!read()) ; + } + + void dvi_source(uint32_t site) + { + if (site > 2) { + PERR("Invalid site value %u ignored", site); + return; + } + write(0); + write(site); + write(Sys_cfg_ctrl::Site::bits(1) | + Sys_cfg_ctrl::Function::bits(0x7) | + Sys_cfg_ctrl::Write::bits(1) | + Sys_cfg_ctrl::Start::bits(1)); + while (!read()) ; + } + + void dvi_mode(uint32_t mode) + { + if (mode > 4) { + PERR("Invalid dvi mode %u ignored", mode); + return; + } + write(0); + write(mode); + write(Sys_cfg_ctrl::Function::bits(0xb) | + Sys_cfg_ctrl::Write::bits(1) | + Sys_cfg_ctrl::Start::bits(1)); + while (!read()) ; + } + + uint32_t mci_status() { return read(); } + }; +} + +#endif /* _BASE_HW__SRC__SERVER__VMM__SYS_REG_H_ */ diff --git a/os/src/server/vmm/include/tsc_380.h b/os/src/server/vmm/include/tsc_380.h new file mode 100644 index 0000000000..c095c9c5dc --- /dev/null +++ b/os/src/server/vmm/include/tsc_380.h @@ -0,0 +1,212 @@ +/* + * \brief Driver for the CoreLink Trustzone Address Space Controller TSC-380 + * \author Stefan Kalkowski + * \date 2012-07-04 + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _BASE_HW__SRC__SERVER__VMM__TSC_380_H_ +#define _BASE_HW__SRC__SERVER__VMM__TSC_380_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + + class Tsc_380 : Mmio + { + private: + + enum { + REGION0_REG_OFF = 0x100, + REGION1_REG_OFF = 0x110, + REGION2_REG_OFF = 0x120, + REGION3_REG_OFF = 0x130, + REGION4_REG_OFF = 0x140, + REGION5_REG_OFF = 0x150, + REGION6_REG_OFF = 0x160, + REGION7_REG_OFF = 0x170, + REGION8_REG_OFF = 0x180, + REGION9_REG_OFF = 0x190, + REGION10_REG_OFF = 0x1a0, + REGION11_REG_OFF = 0x1b0, + REGION12_REG_OFF = 0x1c0, + REGION13_REG_OFF = 0x1d0, + REGION14_REG_OFF = 0x1e0, + REGION15_REG_OFF = 0x1f0, + + REGION_LOW_OFF = 0x0, + REGION_HIGH_OFF = 0x4, + REGION_ATTR_OFF = 0x8, + }; + + /** + * Configuration register + */ + struct Config : public Register<0, 32> + { + struct Region_number : Bitfield<0,4> { }; + struct Address_width : Bitfield<8,6> { }; + }; + + struct Irq_status : public Register<0x10, 32> + { + struct Status : Bitfield<0,1> {}; + struct Overrun : Bitfield<1,1> {}; + }; + + struct Irq_clear : public Register<0x14, 32> + { + struct Status : Bitfield<0,1> {}; + struct Overrun : Bitfield<1,1> {}; + }; + + /** + * Fail address low register + */ + struct Fail_low : public Register<0x20, 32> { }; + + template + struct Region_low : public Register + { + enum { MASK = ~0UL << 15 }; + }; + + template + struct Region_high : public Register { }; + + template + struct Region_attr : public Register + { + struct Enable : + Register::template Bitfield<0, 1> { }; + struct Size : + Register::template Bitfield<1, 6> + { + enum { + SZ_32K = 14, + SZ_64K, + SZ_128K, + SZ_256K, + SZ_512K, + SZ_1M, + SZ_2M, + SZ_4M, + SZ_8M, + SZ_16M, + SZ_32M, + SZ_64M, + SZ_128M, + SZ_256M, + SZ_512M, + SZ_1G, + }; + }; + struct Subreg0 : + Register::template Bitfield<8, 1> { }; + struct Subreg1 : + Register::template Bitfield<9, 1> { }; + struct Subreg2 : + Register::template Bitfield<10, 1> { }; + struct Subreg3 : + Register::template Bitfield<11, 1> { }; + struct Subreg4 : + Register::template Bitfield<12, 1> { }; + struct Subreg5 : + Register::template Bitfield<13, 1> { }; + struct Subreg6 : + Register::template Bitfield<14, 1> { }; + struct Subreg7 : + Register::template Bitfield<15, 1> { }; + struct Normal_write : + Register::template Bitfield<28, 1> { }; + struct Normal_read : + Register::template Bitfield<29, 1> { }; + struct Secure_write : + Register::template Bitfield<30, 1> { }; + struct Secure_read : + Register::template Bitfield<31, 1> { }; + }; + + typedef Region_low<0x100> Region0_low; + + public: + + Tsc_380(addr_t const base) : Mmio(base) + { + /* Access to AACI, MMCI, KMI0/1 */ + write >(0x10000000); + write >(0x10008000); + write::Enable>(1); + write::Size>(Region_attr::Size::SZ_32K); + write::Normal_read>(1); + write::Normal_write>(1); + write::Secure_read>(1); + write::Secure_write>(1); + write::Subreg0>(1); + write::Subreg1>(1); + write::Subreg2>(1); + write::Subreg3>(1); + + /* Access to UART3, and WDT */ + write >(0x10008000); + write >(0x10010000); + write::Enable>(1); + write::Size>(Region_attr::Size::SZ_32K); + write::Normal_read>(1); + write::Normal_write>(1); + write::Secure_read>(1); + write::Secure_write>(1); + write::Subreg0>(1); + write::Subreg1>(1); + write::Subreg2>(1); + write::Subreg3>(1); + write::Subreg5>(1); + write::Subreg6>(1); + + /* Access to SP804, and RTC */ + write >(0x10010000); + write >(0x10018000); + write::Enable>(1); + write::Size>(Region_attr::Size::SZ_32K); + write::Normal_read>(1); + write::Normal_write>(1); + write::Secure_read>(1); + write::Secure_write>(1); + write::Subreg0>(1); + write::Subreg3>(1); + write::Subreg4>(1); + write::Subreg5>(1); + write::Subreg6>(1); + + /* Access to Ethernet and USB */ + write >(0x4e000000); + write >(0x50000000); + write::Enable>(1); + write::Size>(Region_attr::Size::SZ_32M); + write::Normal_read>(1); + write::Normal_write>(1); + write::Secure_read>(1); + write::Secure_write>(1); + + /* clear interrupts */ + write(0x3); + } + + void* last_failed_access() { + void *ret = (void*) read(); + write(0x3); + return ret; + } + }; + +} + +#endif /* _BASE_HW__SRC__SERVER__VMM__TSC_380_H_ */ diff --git a/os/src/server/vmm/main.cc b/os/src/server/vmm/main.cc new file mode 100644 index 0000000000..f53d82138b --- /dev/null +++ b/os/src/server/vmm/main.cc @@ -0,0 +1,359 @@ +/* + * \brief Virtual Machine Monitor + * \author Stefan Kalkowski + * \date 2012-06-25 + */ + +/* + * Copyright (C) 2008-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include +#include +#include + +namespace Genode { + + class Ram { + + private: + + addr_t _base; + size_t _size; + addr_t _local; + + public: + + Ram(addr_t addr, size_t sz) + : _base(addr), _size(sz), _local(0) { } + + addr_t base() { return _base; } + size_t size() { return _size; } + addr_t local() { return _local; } + + void attach(Dataspace_capability cap) { + _local = (addr_t) env()->rm_session()->attach(cap); } + }; + + + class Vm { + + private: + + enum { + ATAG_OFFSET = 0x100, + INITRD_OFFSET = 0x800000 + }; + + Vm_connection _vm_con; + Rom_connection _elf_rom; + Rom_connection _initrd_rom; + const char* _cmdline; + size_t _initrd_size; + Cpu_state_modes *_state; + Ram _ram; + Io_mem_connection _ram_iomem; + + void _load_elf() + { + /* attach ELF locally */ + addr_t elf_addr = env()->rm_session()->attach(_elf_rom.dataspace()); + + /* setup ELF object and read program entry pointer */ + Elf_binary elf((addr_t)elf_addr); + _state->ip = elf.entry(); + if (!elf.valid()) { + PWRN("Invalid elf binary!"); + return; + } + + Elf_segment seg; + for (unsigned n = 0; (seg = elf.get_segment(n)).valid(); ++n) { + if (seg.flags().skip) continue; + + addr_t addr = (addr_t)seg.start(); + size_t size = seg.mem_size(); + + if (addr < _ram.base() || + (addr + size) > (_ram.base() + _ram.size())) { + PWRN("Elf binary doesn't fit into RAM"); + return; + } + + void *base = (void*) (_ram.local() + (addr - _ram.base())); + addr_t laddr = elf_addr + seg.file_offset(); + + /* copy contents */ + memcpy(base, (void *)laddr, seg.file_size()); + + /* if writeable region potentially fill with zeros */ + if (size > seg.file_size() && seg.flags().w) + memset((void *)((addr_t)base + seg.file_size()), + 0, size - seg.file_size()); + } + + /* detach ELF */ + env()->rm_session()->detach((void*)elf_addr); + } + + void _load_initrd() + { + addr_t addr = env()->rm_session()->attach(_initrd_rom.dataspace()); + memcpy((void*)(_ram.local() + INITRD_OFFSET), + (void*)addr, _initrd_size); + env()->rm_session()->detach((void*)addr); + } + + void _prepare_atag() + { + Atag tag((void*)(_ram.local() + ATAG_OFFSET)); + tag.setup_mem_tag(_ram.base(), _ram.size()); + tag.setup_cmdline_tag(_cmdline); + tag.setup_initrd2_tag(_ram.base() + INITRD_OFFSET, _initrd_size); + tag.setup_end_tag(); + } + + public: + + Vm(const char *kernel, const char *initrd, const char *cmdline, + addr_t ram_base, size_t ram_size) + : _elf_rom(kernel), + _initrd_rom(initrd), + _cmdline(cmdline), + _initrd_size(Dataspace_client(_initrd_rom.dataspace()).size()), + _state((Cpu_state_modes*)env()->rm_session()->attach(_vm_con.cpu_state())), + _ram(ram_base, ram_size), + _ram_iomem(ram_base, ram_size) + { + memset((void*)_state, 0, sizeof(Cpu_state_modes)); + _ram.attach(_ram_iomem.dataspace()); + } + + void start(Signal_context_capability sig_cap) + { + _load_elf(); + _load_initrd(); + _prepare_atag(); + _state->cpsr = 0x93; /* SVC mode and IRQs disabled */ + _state->r[1] = 2272; /* MACH_TYPE vexpress board */ + _state->r[2] = _ram.base() + ATAG_OFFSET; /* ATAG addr */ + _vm_con.exception_handler(sig_cap); + } + + void run() { _vm_con.run(); } + + void dump() + { + const char * const modes[] = + { "und", "svc", "abt", "irq", "fiq" }; + const char * const exc[] = + { "reset", "undefined", "smc", "pf_abort", + "data_abort", "irq", "fiq" }; + + printf("Cpu state:\n"); + for (unsigned i = 0; i<13; i++) + printf(" r%x = %08lx\n", i, _state->r[i]); + printf(" sp = %08lx\n", _state->sp); + printf(" lr = %08lx\n", _state->lr); + printf(" ip = %08lx\n", _state->ip); + printf(" cpsr = %08lx\n", _state->cpsr); + for (unsigned i = 0; + i < Cpu_state_modes::Mode_state::MAX; i++) { + printf(" sp_%s = %08lx\n", modes[i], _state->mode[i].sp); + printf(" lr_%s = %08lx\n", modes[i], _state->mode[i].lr); + printf(" spsr_%s = %08lx\n", modes[i], _state->mode[i].spsr); + } + printf(" exception = %s\n", exc[_state->cpu_exception]); + } + + Cpu_state_modes *state() const { return _state; } + }; + + + class Vmm : public Genode::Thread<8192> + { + private: + + enum Hypervisor_calls { + SP810_ENABLE = 1, + CPU_ID, + SYS_COUNTER, + MISC_FLAGS, + SYS_CTRL, + MCI_STATUS + }; + + Io_mem_connection _tsc_io_mem; + Io_mem_connection _tpc_io_mem; + Io_mem_connection _sys_io_mem; + Io_mem_connection _sp810_io_mem; + + Tsc_380 _tsc; + Bp_147 _tpc; + Sys_reg _sys; + Sp810 _sp810; + + Vm *_vm; + + void _sys_ctrl() + { + enum { + OSC1 = 0xc0110001, + DVI_SRC = 0xc0710000, + DVI_MODE = 0xc0b00000 + }; + + uint32_t ctrl = _vm->state()->r[2]; + uint32_t data = _vm->state()->r[0]; + + switch(ctrl) { + case OSC1: + _sys.osc1(data); + break; + case DVI_SRC: + _sys.dvi_source(data); + break; + case DVI_MODE: + _sys.dvi_mode(data); + break; + default: + PWRN("Access violation to sys configuration ctrl=%ux", ctrl); + _vm->dump(); + } + } + + void _handle_hypervisor_call() + { + switch (_vm->state()->r[1]) { + case SP810_ENABLE: + _sp810.enable_timer0(); + _sp810.enable_timer1(); + break; + case CPU_ID: + _vm->state()->r[0] = 0x0c000191; // Coretile A9 ID + break; + case SYS_COUNTER: + _vm->state()->r[0] = _sys.counter(); + break; + case MISC_FLAGS: + _vm->state()->r[0] = _sys.misc_flags(); + break; + case SYS_CTRL: + _sys_ctrl(); + break; + case MCI_STATUS: + _vm->state()->r[0] = _sys.mci_status(); + break; + default: + PERR("Unknown hypervisor call!"); + _vm->dump(); + } + } + + bool _handle_data_abort() + { + PWRN("Vm tried to access %p which isn't allowed", + _tsc.last_failed_access()); + _vm->dump(); + return false; + } + + bool _handle_vm() + { + switch (_vm->state()->cpu_exception) { + case Cpu_state::DATA_ABORT: + if (!_handle_data_abort()) { + PERR("Could not handle data-abort will exit!"); + return false; + } + break; + case Cpu_state::SUPERVISOR_CALL: + _handle_hypervisor_call(); + break; + default: + PERR("Curious exception occured"); + _vm->dump(); + return false; + } + return true; + } + + protected: + + void entry() + { + Signal_receiver sig_rcv; + Signal_context sig_cxt; + Signal_context_capability sig_cap(sig_rcv.manage(&sig_cxt)); + _vm->start(sig_cap); + while (true) { + _vm->run(); + Signal s = sig_rcv.wait_for_signal(); + if (s.context() != &sig_cxt) { + PWRN("Invalid context"); + continue; + } + if (!_handle_vm()) + return; + } + }; + + public: + + Vmm(addr_t tsc_base, addr_t tpc_base, + addr_t sys_base, addr_t sp810_base, + Vm *vm) + : _tsc_io_mem(tsc_base, 0x1000), + _tpc_io_mem(tpc_base, 0x1000), + _sys_io_mem(sys_base, 0x1000), + _sp810_io_mem(sp810_base, 0x1000), + _tsc((addr_t)env()->rm_session()->attach(_tsc_io_mem.dataspace())), + _tpc((addr_t)env()->rm_session()->attach(_tpc_io_mem.dataspace())), + _sys((addr_t)env()->rm_session()->attach(_sys_io_mem.dataspace())), + _sp810((addr_t)env()->rm_session()->attach(_sp810_io_mem.dataspace())), + _vm(vm) { } + }; +} + + +int main() +{ + enum { + SYS_VEA9X4_BASE = 0x10000000, + SP810_VEA9X4_BASE = 0x10001000, + TPC_VEA9X4_BASE = 0x100e6000, + TSC_VEA9X4_BASE = 0x100ec000, + MAIN_MEM_START = 0x80000000, + MAIN_MEM_SIZE = 0x10000000 + }; + + static const char* cmdline = "console=ttyAMA0,38400n8 root=/dev/ram0 lpj=1554432"; + static Genode::Vm vm("linux", "initrd.gz", cmdline, + MAIN_MEM_START, MAIN_MEM_SIZE); + static Genode::Vmm vmm(TSC_VEA9X4_BASE, TPC_VEA9X4_BASE, + SYS_VEA9X4_BASE, SP810_VEA9X4_BASE, + &vm); + + PINF("Start virtual machine"); + vmm.start(); + + Genode::sleep_forever(); + return 0; +} diff --git a/os/src/server/vmm/target.mk b/os/src/server/vmm/target.mk new file mode 100644 index 0000000000..ae63a0b1fa --- /dev/null +++ b/os/src/server/vmm/target.mk @@ -0,0 +1,5 @@ +TARGET = vmm +REQUIRES = trustzone platform_vea9x4 +LIBS = env cxx elf signal +SRC_CC = main.cc +INC_DIR += $(PRG_DIR)/include \ No newline at end of file