diff --git a/repos/os/run/vmm_arm.run b/repos/os/run/vmm_arm.run index 480470fa7..6e3e5850b 100644 --- a/repos/os/run/vmm_arm.run +++ b/repos/os/run/vmm_arm.run @@ -6,7 +6,7 @@ assert_spec hw -if { ![have_spec imx7d_sabre] && ![have_spec arndale] } { +if { ![have_spec imx7d_sabre] && ![have_spec arndale] && ![have_spec imx8q_evk] } { puts "Run script is not supported on this platform" exit 0 } @@ -21,7 +21,7 @@ build $build_components create_boot_directory install_config { - + @@ -44,7 +44,7 @@ install_config { - + @@ -56,6 +56,18 @@ install_config { } +if { [have_spec arm] } { + + if {![file exists bin/linux]} { + puts "Download linux kernel ..." + exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-15.02/arm_vt/linux + } + + if {![file exists bin/dtb]} { + puts "Download device tree blob ..." + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb + } + # # This test uses a Linux kernel built from unmodified vanilla kernel sources # but using a slightly simplified kernel configuration, as well as device tree @@ -76,15 +88,62 @@ install_config { # ! make ARCH=arm CROSS_COMPILE= -j8 Image # ! make ARCH=arm CROSS_COMPILE= vexpress-v2p-ca15-tc1.dtb # - -if {![file exists bin/linux]} { - puts "Download linux kernel ..." - exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-15.02/arm_vt/linux } -if {![file exists bin/dtb]} { - puts "Download device tree blob ..." - exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb +if { [have_spec arm_64] } { + + if {![file exists bin/linux]} { + puts "Download linux kernel ..." + exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-19.11/linux-arm64-image-5.2 + } + + if {![file exists bin/dtb]} { + puts "Download device tree blob ..." + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-19.11/dtb-arm64-virt + } + + if {![file exists bin/initrd]} { + puts "Download initramfs ..." + exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-19.11/initrd-arm64 + } + +# +# To obtain the linux kernel, do the following steps: +# +# wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.53.tar.xz +# +# tar -xJf linux-4.19.53.tar.xz +# cd linux-4.19.53 +# +# make O=../build-linux-aarch64 ARCH=arm64 CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-aarch64- defconfig +# make O=../build-linux-aarch64 ARCH=arm64 CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-aarch64- -j32 +# +# copy ../build-linux-aarch64/arch/arm64/boot/Image to your build directory in 'bin/linux' +# +# +# To get the dtb (device-tree-binary), you have to compile the file: +# repos/os/src/server/vmm/spec/arm_v8/virt.dts with the dtc compiler: +# dtc repos/os/src/server/vmm/spec/arm_v8/virt.dts > bin/dtb +# +# +# To construct the initrd do the following: +# * get and install gcc from linaro +# (https://releases.linaro.org/components/toolchain/binaries/latest-7/) +# * build busybox +# wget https://busybox.net/downloads/busybox-1.29.3.tar.bz2 +# tar xjf busybox-1.29.3.tar.bz2 +# mkdir build-busybox-aarch64 +# cd busybox-1.29.3 +# make O=../build-busybox-aarch64 defconfig +# make O=../build-busybox-aarch64 menuconfig +# +# [*] Setting -> Build static binary (no shared libs) +# +# cd ../build-busybox-aarch64 +# make CROSS_COMPILE=/usr/local/gcc-linaro/bin/aarch64-linux-gnu- install -j6 +# * create ramdisk +# cd _install +# find . | cpio -H newc -o | gzip > ../initrd } set boot_modules { @@ -96,6 +155,7 @@ set boot_modules { linux dtb } +append_if [have_spec arm_64] boot_modules initrd build_boot_image $boot_modules # diff --git a/repos/os/src/server/vmm/board.h b/repos/os/src/server/vmm/spec/arm_v7/board.h similarity index 100% rename from repos/os/src/server/vmm/board.h rename to repos/os/src/server/vmm/spec/arm_v7/board.h diff --git a/repos/os/src/server/vmm/main.cc b/repos/os/src/server/vmm/spec/arm_v7/main.cc similarity index 100% rename from repos/os/src/server/vmm/main.cc rename to repos/os/src/server/vmm/spec/arm_v7/main.cc diff --git a/repos/os/src/server/vmm/target.mk b/repos/os/src/server/vmm/spec/arm_v7/target.mk similarity index 100% rename from repos/os/src/server/vmm/target.mk rename to repos/os/src/server/vmm/spec/arm_v7/target.mk diff --git a/repos/os/src/server/vmm/test/kernel/main.s b/repos/os/src/server/vmm/spec/arm_v7/test/main.s similarity index 100% rename from repos/os/src/server/vmm/test/kernel/main.s rename to repos/os/src/server/vmm/spec/arm_v7/test/main.s diff --git a/repos/os/src/server/vmm/test/kernel/target.mk b/repos/os/src/server/vmm/spec/arm_v7/test/target.mk similarity index 100% rename from repos/os/src/server/vmm/test/kernel/target.mk rename to repos/os/src/server/vmm/spec/arm_v7/test/target.mk diff --git a/repos/os/src/server/vmm/spec/arm_v8/address_space.cc b/repos/os/src/server/vmm/spec/arm_v8/address_space.cc new file mode 100644 index 000000000..2c866b03d --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/address_space.cc @@ -0,0 +1,32 @@ +/* + * \brief VMM address space utility + * \author Stefan Kalkowski + * \date 2019-09-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include + +using Vmm::Address_range; + +Address_range::Address_range(Genode::uint64_t start, + Genode::uint64_t size) +: start(start), size(size) { } + + +Address_range & Address_range::find(Address_range & bus_addr) +{ + if (match(bus_addr)) + return *this; + + Address_range * ar = + Avl_node::child(bus_addr.start > start); + if (!ar) throw Not_found(bus_addr); + return ar->find(bus_addr); +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/address_space.h b/repos/os/src/server/vmm/spec/arm_v8/address_space.h new file mode 100644 index 000000000..4be408d89 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/address_space.h @@ -0,0 +1,82 @@ +/* + * \brief VMM address space utility + * \author Stefan Kalkowski + * \date 2019-09-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__ADDRESS_SPACE_H_ +#define _SRC__SERVER__VMM__ADDRESS_SPACE_H_ + +#include +#include + +namespace Vmm { + struct Address_range; + class Address_space; +} + + +struct Vmm::Address_range : Genode::Avl_node +{ + Genode::uint64_t const start; + Genode::uint64_t const size; + + Address_range(Genode::uint64_t start, + Genode::uint64_t size); + + Genode::uint64_t end() const { return start + size; } + + bool match(Address_range & other) const { + return other.start >= start && other.end() <= end(); } + + Address_range & find(Address_range & bus_addr); + + struct Not_found : Exception + { + Not_found(Address_range & access) + : Exception("Could not find ", access) {} + }; + + void print(Genode::Output & out) const + { + Genode::print(out, "address=", Genode::Hex(start), + " width=", Genode::Hex(size)); + } + + /************************ + ** Avl_node interface ** + ************************/ + + bool higher(Address_range * range) { + return range->start > start; } +}; + + +class Vmm::Address_space +{ + private: + + Genode::Avl_tree _tree; + + public: + + template + T & get(Address_range & bus_addr) + { + if (!_tree.first()) throw Address_range::Not_found(bus_addr); + + return *static_cast(&_tree.first()->find(bus_addr)); + } + + void add(Address_range & ar) { _tree.insert(&ar); } +}; + + +#endif /* _SRC__SERVER__VMM__ADDRESS_SPACE_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/cpu.cc b/repos/os/src/server/vmm/spec/arm_v8/cpu.cc new file mode 100644 index 000000000..c2ba88c33 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/cpu.cc @@ -0,0 +1,403 @@ +/* + * \brief VMM cpu object + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ +#include +#include +#include + +using Vmm::Cpu; +using Vmm::Gic; + +Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; } + + +Cpu::System_register::Iss::access_t +Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1, + unsigned crm, unsigned op2) +{ + access_t v = 0; + Crn::set(v, crn); + Crm::set(v, crm); + Opcode0::set(v, op0); + Opcode1::set(v, op1); + Opcode2::set(v, op2); + return v; +}; + + +Cpu::System_register::Iss::access_t +Cpu::System_register::Iss::mask_encoding(access_t v) +{ + return Crm::masked(v) | + Crn::masked(v) | + Opcode1::masked(v) | + Opcode2::masked(v) | + Opcode0::masked(v); +} + + +Cpu::System_register::System_register(unsigned op0, + unsigned crn, + unsigned op1, + unsigned crm, + unsigned op2, + const char * name, + bool writeable, + Genode::addr_t v, + Genode::Avl_tree & tree) +: _encoding(Iss::value(op0, crn, op1, crm, op2)), + _name(name), + _writeable(writeable), + _value(v) +{ + tree.insert(this); +} + + +Genode::addr_t Cpu::Ccsidr::read() const +{ + struct Clidr : Genode::Register<32> + { + enum Cache_entry { + NO_CACHE, + INSTRUCTION_CACHE_ONLY, + DATA_CACHE_ONLY, + SEPARATE_CACHE, + UNIFIED_CACHE + }; + + static unsigned level(unsigned l, access_t reg) { + return (reg >> l*3) & 0b111; } + }; + + struct Csselr : Genode::Register<32> + { + struct Instr : Bitfield<0, 1> {}; + struct Level : Bitfield<1, 4> {}; + }; + + enum { INVALID = 0xffffffff }; + + unsigned level = Csselr::Level::get(csselr.read()); + bool instr = Csselr::Instr::get(csselr.read()); + + if (level > 6) { + Genode::warning("Invalid Csselr value!"); + return INVALID; + } + + unsigned ce = Clidr::level(level, state.clidr_el1); + + if (ce == Clidr::NO_CACHE || + (ce == Clidr::DATA_CACHE_ONLY && instr)) { + Genode::warning("Invalid Csselr value!"); + return INVALID; + } + + if (ce == Clidr::INSTRUCTION_CACHE_ONLY || + (ce == Clidr::SEPARATE_CACHE && instr)) { + Genode::log("Return Ccsidr instr value ", state.ccsidr_inst_el1[level]); + return state.ccsidr_inst_el1[level]; + } + + Genode::log("Return Ccsidr value ", state.ccsidr_data_el1[level]); + return state.ccsidr_data_el1[level]; +} + + +Genode::addr_t Cpu::Ctr_el0::read() const +{ + Genode::addr_t ret; + asm volatile("mrs %0, ctr_el0" : "=r" (ret)); + return ret; +} + + +void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v) +{ + + unsigned target_list = v & 0xffff; + unsigned irq = (v >> 24) & 0xf; + + for (unsigned i = 0; i <= Vm::last_cpu(); i++) { + if (target_list & (1<find_by_encoding(Iss::mask_encoding(v)); + + if (!reg) { + Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":"); + Genode::error(Iss::Direction::get(v) ? "read" : "write", + ": " + "op0=", Iss::Opcode0::get(v), " " + "op1=", Iss::Opcode1::get(v), " " + "r", Iss::Register::get(v), " " + "crn=", Iss::Crn::get(v), " " + "crm=", Iss::Crm::get(v), " ", + "op2=", Iss::Opcode2::get(v)); + if (Iss::Direction::get(v)) _state.r[Iss::Register::get(v)] = 0; + _state.ip += sizeof(Genode::uint32_t); + return false; + } + + if (Iss::Direction::get(v)) { /* read access */ + _state.r[Iss::Register::get(v)] = reg->read(); + } else { /* write access */ + if (!reg->writeable()) { + Genode::error("writing to system register ", + reg->name(), " not allowed!"); + return false; + } + reg->write(_state.r[Iss::Register::get(v)]); + } + _state.ip += sizeof(Genode::uint32_t); + return true; +} + + +void Cpu::_handle_wfi() +{ + _state.ip += sizeof(Genode::uint32_t); + + if (_state.esr_el2 & 1) return; /* WFE */ + + _active = false; + _timer.schedule_timeout(); +} + + +void Cpu::_handle_brk() +{ + Genode::uint64_t offset = 0x0; + if (!(_state.pstate & 0b100)) { + offset = 0x400; + } else if (_state.pstate & 0b1) { + offset = 0x200; + } + _state.esr_el1 = _state.esr_el2; + _state.spsr_el1 = _state.pstate; + _state.elr_el1 = _state.ip; + _state.ip = _state.vbar_el1 + offset; + _state.pstate = 0b1111000101; +} + + +void Cpu::_handle_sync() +{ + /* check device number*/ + switch (Esr::Ec::get(_state.esr_el2)) { + case Esr::Ec::HVC: + _handle_hyper_call(); + break; + case Esr::Ec::MRS_MSR: + _handle_sys_reg(); + break; + case Esr::Ec::DA: + _handle_data_abort(); + break; + case Esr::Ec::WFI: + _handle_wfi(); + return; + case Esr::Ec::BRK: + _handle_brk(); + return; + default: + throw Exception("Unknown trap: %x", + Esr::Ec::get(_state.esr_el2)); + }; +} + + +void Cpu::_handle_irq() +{ + enum { /* FIXME */ VT_TIMER_IRQ = 27 }; + switch (_state.irqs.last_irq) { + case VT_TIMER_IRQ: + _timer.handle_irq(); + break; + default: + _gic.handle_irq(); + }; +} + + +void Cpu::_handle_hyper_call() +{ + switch(_state.r[0]) { + case Psci::PSCI_VERSION: + _state.r[0] = Psci::VERSION; + return; + case Psci::MIGRATE_INFO_TYPE: + _state.r[0] = Psci::NOT_SUPPORTED; + return; + case Psci::PSCI_FEATURES: + _state.r[0] = Psci::NOT_SUPPORTED; + return; + case Psci::CPU_ON: + _vm.cpu((unsigned)_state.r[1], [&] (Cpu & cpu) { + cpu.state().ip = _state.r[2]; + cpu.state().r[0] = _state.r[3]; + cpu.run(); + }); + _state.r[0] = Psci::SUCCESS; + return; + default: + Genode::warning("unknown hypercall! ", cpu_id()); + dump(); + }; +} + + +void Cpu::_handle_data_abort() +{ + _vm.bus().handle_memory_access(*this); + _state.ip += sizeof(Genode::uint32_t); +} + + +void Cpu::_update_state() +{ + if (!_gic.pending_irq()) return; + + _active = true; + _timer.cancel_timeout(); +} + +unsigned Cpu::cpu_id() const { return _vcpu_id.id; } +void Cpu::run() { _vm_session.run(_vcpu_id); } +void Cpu::pause() { _vm_session.pause(_vcpu_id); } +bool Cpu::active() const { return _active; } +Cpu::State & Cpu::state() const { return _state; } +Gic::Gicd_banked & Cpu::gic() { return _gic; } + + +void Cpu::handle_exception() +{ + /* check exception reason */ + switch (_state.exception_type) { + case NO_EXCEPTION: break; + case AARCH64_IRQ: _handle_irq(); break; + case AARCH64_SYNC: _handle_sync(); break; + default: + throw Exception("Curious exception ", + _state.exception_type, " occured"); + } + _state.exception_type = NO_EXCEPTION; +} + + +void Cpu::dump() +{ + using namespace Genode; + + auto lambda = [] (addr_t exc) { + switch (exc) { + case AARCH64_SYNC: return "aarch64 sync"; + case AARCH64_IRQ: return "aarch64 irq"; + case AARCH64_FIQ: return "aarch64 fiq"; + case AARCH64_SERROR: return "aarch64 serr"; + case AARCH32_SYNC: return "aarch32 sync"; + case AARCH32_IRQ: return "aarch32 irq"; + case AARCH32_FIQ: return "aarch32 fiq"; + case AARCH32_SERROR: return "aarch32 serr"; + default: return "unknown"; + }; + }; + + log("VM state (", _active ? "active" : "inactive", ") :"); + for (unsigned i = 0; i < 31; i++) { + log(" r", i, " = ", + Hex(_state.r[i], Hex::PREFIX, Hex::PAD)); + } + log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD)); + log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD)); + log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD)); + log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD)); + log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD)); + log(" exception = ", _state.exception_type, " (", + lambda(_state.exception_type), ")"); + log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD)); + _timer.dump(); +} + + +void Cpu::recall() +{ + Genode::Signal_transmitter(_vm_handler).submit(); +}; + + +Cpu::Cpu(Vm & vm, + Genode::Vm_connection & vm_session, + Mmio_bus & bus, + Gic & gic, + Genode::Env & env, + Genode::Heap & heap, + Genode::Entrypoint & ep) +: _vm(vm), + _vm_session(vm_session), + _heap(heap), + _vm_handler(*this, ep, *this, &Cpu::_handle_nothing), + _vcpu_id(_vm_session.with_upgrade([&]() { + return _vm_session.create_vcpu(heap, env, _vm_handler); + })), + _state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))), + // op0, crn, op1, crm, op2, writeable, reset value + _sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree), + _sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree), + _sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree), + _sr_id_aa64dfr1_el1 (3, 0, 0, 5, 1, "ID_AA64DFR1_EL1", false, 0x0, _reg_tree), + _sr_id_aa64isar0_el1(3, 0, 0, 6, 0, "ID_AA64ISAR0_EL1", false, _state.id_aa64isar0_el1, _reg_tree), + _sr_id_aa64isar1_el1(3, 0, 0, 6, 1, "ID_AA64ISAR1_EL1", false, _state.id_aa64isar1_el1, _reg_tree), + _sr_id_aa64mmfr0_el1(3, 0, 0, 7, 0, "ID_AA64MMFR0_EL1", false, _state.id_aa64mmfr0_el1, _reg_tree), + _sr_id_aa64mmfr1_el1(3, 0, 0, 7, 1, "ID_AA64MMFR1_EL1", false, _state.id_aa64mmfr1_el1, _reg_tree), + _sr_id_aa64mmfr2_el1(3, 0, 0, 7, 2, "ID_AA64MMFR2_EL1", false, _state.id_aa64mmfr2_el1, _reg_tree), + _sr_id_aa64pfr0_el1 (_state.id_aa64pfr0_el1, _reg_tree), + _sr_id_aa64pfr1_el1 (3, 0, 0, 4, 1, "ID_AA64PFR1_EL1", false, 0x0, _reg_tree), + _sr_id_aa64zfr0_el1 (3, 0, 0, 4, 4, "ID_AA64ZFR0_EL1", false, 0x0, _reg_tree), + _sr_aidr_el1 (3, 0, 1, 0, 7, "AIDR_EL1", false, 0x0, _reg_tree), + _sr_revidr_el1 (3, 0, 0, 0, 6, "REVIDR_EL1", false, 0x0, _reg_tree), + + _sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, _state.clidr_el1, _reg_tree), + _sr_csselr_el1 (3, 0, 2, 0, 0, "CSSELR_EL1", true, 0x0, _reg_tree), + _sr_ctr_el0 (_reg_tree), + _sr_ccsidr_el1 (_sr_csselr_el1, _state, _reg_tree), + +//_sr_pmccfiltr_el0 (3, 14, 3, 15, 7, "PMCCFILTR_EL0", true, 0x0, _reg_tree), + _sr_pmuserenr_el0 (3, 9, 3, 14, 0, "PMUSEREN_EL0", true, 0x0, _reg_tree), + _sr_dbgbcr0 (2, 0, 0, 0, 5, "DBGBCR_EL1", true, 0x0, _reg_tree), + _sr_dbgbvr0 (2, 0, 0, 0, 4, "DBGBVR_EL1", true, 0x0, _reg_tree), + _sr_dbgwcr0 (2, 0, 0, 0, 7, "DBGWCR_EL1", true, 0x0, _reg_tree), + _sr_dbgwvr0 (2, 0, 0, 0, 6, "DBGWVR_EL1", true, 0x0, _reg_tree), + _sr_mdscr (2, 0, 0, 2, 2, "MDSCR_EL1", true, 0x0, _reg_tree), + _sr_osdlr (2, 1, 0, 3, 4, "OSDLR_EL1", true, 0x0, _reg_tree), + _sr_oslar (2, 1, 0, 0, 4, "OSLAR_EL1", true, 0x0, _reg_tree), + _sr_sgi1r_el1 (_reg_tree, vm), + _gic(*this, gic, bus), + _timer(env, ep, _gic.irq(27), *this) +{ + _state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */ + _state.vmpidr_el2 = cpu_id(); +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/cpu.h b/repos/os/src/server/vmm/spec/arm_v8/cpu.h new file mode 100644 index 000000000..664059ddc --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/cpu.h @@ -0,0 +1,331 @@ +/* + * \brief VMM cpu object + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__CPU_H_ +#define _SRC__SERVER__VMM__CPU_H_ + +#include +#include + +#include +#include +#include +#include +#include + +namespace Vmm { + class Vm; + class Cpu; + Genode::Lock & lock(); +} + +class Vmm::Cpu +{ + public: + + using State = Genode::Vm_state; + + struct Esr : Genode::Register<32> + { + struct Ec : Bitfield<26, 6> + { + enum { + WFI = 0x1, + HVC = 0x16, + MRS_MSR = 0x18, + DA = 0x24, + BRK = 0x3c + }; + }; + }; + + Cpu(Vm & vm, + Genode::Vm_connection & vm_session, + Mmio_bus & bus, + Gic & gic, + Genode::Env & env, + Genode::Heap & heap, + Genode::Entrypoint & ep); + + unsigned cpu_id() const; + void run(); + void pause(); + bool active() const; + State & state() const; + Gic::Gicd_banked & gic(); + void dump(); + void handle_exception(); + void recall(); + + template + void handle_signal(FUNC handler) + { + Genode::Lock::Guard guard(lock()); + + if (active()) { + pause(); + handle_exception(); + } + + handler(); + _update_state(); + if (active()) run(); + } + + template + struct Signal_handler : Genode::Vm_handler> + { + using Base = Genode::Vm_handler>; + + Cpu & cpu; + T & obj; + void (T::*member)(); + + void handle() + { + try { + cpu.handle_signal([this] () { (obj.*member)(); }); + } catch(Exception &e) { + Genode::error(e); + cpu.dump(); + } + } + + Signal_handler(Cpu & cpu, + Genode::Entrypoint & ep, + T & o, + void (T::*f)()) + : Base(ep, *this, &Signal_handler::handle), + cpu(cpu), obj(o), member(f) {} + }; + + private: + + enum Exception_type { + AARCH64_SYNC = 0x400, + AARCH64_IRQ = 0x480, + AARCH64_FIQ = 0x500, + AARCH64_SERROR = 0x580, + AARCH32_SYNC = 0x600, + AARCH32_IRQ = 0x680, + AARCH32_FIQ = 0x700, + AARCH32_SERROR = 0x780, + NO_EXCEPTION = 0xffff + }; + + class System_register : public Genode::Avl_node + { + private: + + const Esr::access_t _encoding; + const char *_name; + const bool _writeable; + Genode::uint64_t _value; + + public: + + struct Iss : Esr + { + struct Direction : Bitfield<0, 1> {}; + struct Crm : Bitfield<1, 4> {}; + struct Register : Bitfield<5, 5> {}; + struct Crn : Bitfield<10, 4> {}; + struct Opcode1 : Bitfield<14, 3> {}; + struct Opcode2 : Bitfield<17, 3> {}; + struct Opcode0 : Bitfield<20, 2> {}; + + static access_t value(unsigned op0, + unsigned crn, + unsigned op1, + unsigned crm, + unsigned op2); + + static access_t mask_encoding(access_t v); + }; + + System_register(unsigned op0, + unsigned crn, + unsigned op1, + unsigned crm, + unsigned op2, + const char * name, + bool writeable, + Genode::addr_t v, + Genode::Avl_tree & tree); + + const char * name() const { return _name; } + const bool writeable() const { return _writeable; } + + System_register * find_by_encoding(Iss::access_t e) + { + if (e == _encoding) return this; + + System_register * r = + Avl_node::child(e > _encoding); + return r ? r->find_by_encoding(e) : nullptr; + } + + virtual void write(Genode::addr_t v) { + _value = (Genode::addr_t)v; } + + virtual Genode::addr_t read() const { + return (Genode::addr_t)(_value); } + + + /************************ + ** Avl node interface ** + ************************/ + + bool higher(System_register *r) { + return (r->_encoding > _encoding); } + }; + + class Id_aa64pfr0 : public System_register, + public Genode::Register<64> + { + private: + + struct El0 : Bitfield<0, 4> { enum { AARCH64_ONLY = 1 }; }; + struct El1 : Bitfield<4, 4> { enum { AARCH64_ONLY = 1 }; }; + struct El2 : Bitfield<8, 4> { enum { NOT_IMPLEMENTED }; }; + struct El3 : Bitfield<12, 4> { enum { NOT_IMPLEMENTED }; }; + struct Ras : Bitfield<28, 4> { enum { NOT_IMPLEMENTED }; }; + struct Sve : Bitfield<32, 4> { enum { NOT_IMPLEMENTED }; }; + + access_t _reset_value(access_t orig) + { + El0::set(orig, El0::AARCH64_ONLY); + El1::set(orig, El1::AARCH64_ONLY); + El2::set(orig, El2::NOT_IMPLEMENTED); + El3::set(orig, El3::NOT_IMPLEMENTED); + Ras::set(orig, Ras::NOT_IMPLEMENTED); + Sve::set(orig, Sve::NOT_IMPLEMENTED); + return orig; + } + + public: + + Id_aa64pfr0(Genode::uint64_t id_aa64pfr0, + Genode::Avl_tree & tree) + : System_register(3, 0, 0, 4, 0, "ID_AA64PFR0_EL1", false, + _reset_value(id_aa64pfr0), tree) {} + }; + + struct Ccsidr : System_register + { + System_register & csselr; + State & state; + + Ccsidr(System_register &csselr, + State & state, + Genode::Avl_tree & tree) + : System_register(3, 0, 1, 0, 0, "CCSIDR_EL1", false, 0x0, tree), + csselr(csselr), state(state) {} + + virtual Genode::addr_t read() const override; + }; + + struct Ctr_el0 : System_register + { + Ctr_el0(Genode::Avl_tree & tree) + : System_register(3, 0, 3, 0, 1, "CTR_EL0", false, 0x0, tree) {} + + virtual Genode::addr_t read() const override; + }; + + struct Icc_sgi1r_el1 : System_register + { + Vm & vm; + + Icc_sgi1r_el1(Genode::Avl_tree & tree, Vm & vm) + : System_register(3, 12, 0, 11, 5, "SGI1R_EL1", true, 0x0, tree), + vm(vm) {} + + virtual void write(Genode::addr_t v) override; + }; + + bool _active { true }; + Vm & _vm; + Genode::Vm_connection & _vm_session; + Genode::Heap & _heap; + Signal_handler _vm_handler; + Genode::Vm_session::Vcpu_id _vcpu_id; + State & _state; + Genode::Avl_tree _reg_tree; + + /****************************** + ** Identification registers ** + ******************************/ + + System_register _sr_id_aa64afr0_el1; + System_register _sr_id_aa64afr1_el1; + System_register _sr_id_aa64dfr0_el1; + System_register _sr_id_aa64dfr1_el1; + System_register _sr_id_aa64isar0_el1; + System_register _sr_id_aa64isar1_el1; + System_register _sr_id_aa64mmfr0_el1; + System_register _sr_id_aa64mmfr1_el1; + System_register _sr_id_aa64mmfr2_el1; + Id_aa64pfr0 _sr_id_aa64pfr0_el1; + System_register _sr_id_aa64pfr1_el1; + System_register _sr_id_aa64zfr0_el1; + System_register _sr_aidr_el1; + System_register _sr_revidr_el1; + + /********************* + ** Cache registers ** + *********************/ + + System_register _sr_clidr_el1; + System_register _sr_csselr_el1; + Ctr_el0 _sr_ctr_el0; + Ccsidr _sr_ccsidr_el1; + + /*********************************** + ** Performance monitor registers ** + ***********************************/ + + System_register _sr_pmuserenr_el0; + + /***************************** + ** Debug monitor registers ** + *****************************/ + + System_register _sr_dbgbcr0; + System_register _sr_dbgbvr0; + System_register _sr_dbgwcr0; + System_register _sr_dbgwvr0; + System_register _sr_mdscr; + System_register _sr_osdlr; + System_register _sr_oslar; + + /*********************** + ** Local peripherals ** + ***********************/ + + Icc_sgi1r_el1 _sr_sgi1r_el1; + Gic::Gicd_banked _gic; + Generic_timer _timer; + + void _handle_nothing() {} + bool _handle_sys_reg(); + void _handle_brk(); + void _handle_wfi(); + void _handle_sync(); + void _handle_irq(); + void _handle_data_abort(); + void _handle_hyper_call(); + void _update_state(); +}; + +#endif /* _SRC__SERVER__VMM__CPU_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/exception.h b/repos/os/src/server/vmm/spec/arm_v8/exception.h new file mode 100644 index 000000000..d155d5a38 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/exception.h @@ -0,0 +1,22 @@ +/* + * \brief VMM exception handling + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__EXCEPTION_H_ +#define _SRC__SERVER__VMM__EXCEPTION_H_ + +#include + +namespace Vmm { using Exception = Genode::String<128>; } + +#endif /* _SRC__SERVER__VMM__EXCEPTION_H_ */ + diff --git a/repos/os/src/server/vmm/spec/arm_v8/generic_timer.cc b/repos/os/src/server/vmm/spec/arm_v8/generic_timer.cc new file mode 100644 index 000000000..003434053 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/generic_timer.cc @@ -0,0 +1,116 @@ +/* + * \brief VMM ARM Generic timer device model + * \author Stefan Kalkowski + * \date 2019-08-20 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +using Vmm::Generic_timer; + +Genode::uint64_t Generic_timer::_ticks_per_ms() +{ + static Genode::uint64_t ticks_per_ms = 0; + if (!ticks_per_ms) { + Genode::uint32_t freq = 0; + asm volatile("mrs %0, cntfrq_el0" : "=r" (freq)); + ticks_per_ms = freq / 1000; + } + return ticks_per_ms; +} + + +bool Generic_timer::_enabled() { + return Ctrl::Enabled::get(_cpu.state().timer.control); } + + +bool Generic_timer::_masked() { + return Ctrl::Imask::get(_cpu.state().timer.control); } + + +bool Generic_timer::_pending() { + return Ctrl::Istatus::get(_cpu.state().timer.control); } + + +void Generic_timer::_handle_timeout(Genode::Duration) +{ + _cpu.handle_signal([this] (void) { + if (_enabled() && !_masked()) handle_irq(); + }); +} + + +Genode::uint64_t Generic_timer::_usecs_left() +{ + Genode::uint64_t count; + asm volatile("mrs %0, cntpct_el0" : "=r" (count)); + count -= _cpu.state().timer.offset; + if (count > _cpu.state().timer.compare) return 0; + return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count, + _ticks_per_ms()); +} + + +Generic_timer::Generic_timer(Genode::Env & env, + Genode::Entrypoint & ep, + Gic::Irq & irq, + Cpu & cpu) +: _timer(env, ep), + _timeout(_timer, *this, &Generic_timer::_handle_timeout), + _irq(irq), + _cpu(cpu) +{ + _cpu.state().timer.irq = true; + _irq.handler(*this); +} + + +void Generic_timer::schedule_timeout() +{ + if (_pending()) { + handle_irq(); + return; + } + + if (_enabled()) { + if (_usecs_left()) { + _timeout.schedule(Genode::Microseconds(_usecs_left())); + } else _handle_timeout(Genode::Duration(Genode::Microseconds(0))); + } +} + + +void Generic_timer::cancel_timeout() +{ + if (_timeout.scheduled()) _timeout.discard(); +} + + +void Generic_timer::handle_irq() +{ + _irq.assert(); + _cpu.state().timer.irq = false; +} + + +void Generic_timer::eoi() +{ + _cpu.state().timer.irq = true; +}; + + +void Generic_timer::dump() +{ + using namespace Genode; + + log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD)); + log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD)); +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/generic_timer.h b/repos/os/src/server/vmm/spec/arm_v8/generic_timer.h new file mode 100644 index 000000000..09ccacc36 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/generic_timer.h @@ -0,0 +1,74 @@ +/* + * \brief VMM ARM Generic timer device model + * \author Stefan Kalkowski + * \date 2019-08-20 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__GENERIC_TIMER_H_ +#define _SRC__SERVER__VMM__GENERIC_TIMER_H_ + +#include + +#include +#include +#include +#include + +namespace Vmm { + class Cpu; + class Generic_timer; +} + +class Vmm::Generic_timer : Gic::Irq::Irq_handler +{ + private: + + Timer::Connection _timer; + Timer::One_shot_timeout _timeout; + Gic::Irq & _irq; + Cpu & _cpu; + + struct Ctrl : Genode::Register<32> + { + struct Enabled : Bitfield<0,1> {}; + struct Imask : Bitfield<1,1> {}; + struct Istatus : Bitfield<2,1> {}; + }; + + Genode::uint64_t _ticks_per_ms(); + + bool _enabled(); + bool _masked(); + bool _pending(); + + void _handle_timeout(Genode::Duration); + Genode::uint64_t _usecs_left(); + + public: + + Generic_timer(Genode::Env & env, + Genode::Entrypoint & ep, + Gic::Irq & irq, + Cpu & cpu); + + void schedule_timeout(); + void cancel_timeout(); + void handle_irq(); + void dump(); + + + /***************** + ** Irq_handler ** + *****************/ + + void eoi() override; +}; + +#endif /* _SRC__SERVER__VMM__GENERIC_TIMER_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/gic.h b/repos/os/src/server/vmm/spec/arm_v8/gic.h new file mode 100644 index 000000000..96b015824 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/gic.h @@ -0,0 +1,440 @@ +/* + * \brief VMM ARM Generic Interrupt Controller device model + * \author Stefan Kalkowski + * \date 2019-08-05 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__GIC_H_ +#define _SRC__SERVER__VMM__GIC_H_ + +#include + +#include +#include +#include +#include +#include + +namespace Vmm { class Gic; } + +class Vmm::Gic : public Vmm::Mmio_device +{ + public: + + enum Irqs { + MAX_SGI = 16, + MAX_PPI = 16, + MAX_SPI = 992, + MAX_IRQ = 1020, + SPURIOUS = 1023, + }; + + class Irq : public Genode::List::Element + { + public: + + struct List : Genode::List + { + void insert(Irq & irq); + Irq * highest_enabled(); + }; + + struct Irq_handler { + virtual void eoi() {}; + virtual void enabled() {}; + virtual void disabled() {}; + }; + + enum Type { SGI, PPI, SPI }; + enum State { INACTIVE, ACTIVE, PENDING, ACTIVE_PENDING }; + enum Config { LEVEL, EDGE }; + + Irq(unsigned num, Type t, List &pending_list); + + bool enabled() const; + bool active() const; + bool pending() const; + bool level() const; + unsigned number() const; + Genode::uint8_t priority() const; + Genode::uint8_t target() const; + + void enable(); + void disable(); + void activate(); + void deactivate(); + void assert(); + void deassert(); + void level(bool l); + void priority(Genode::uint8_t p); + void target(Genode::uint8_t t); + void handler(Irq_handler & handler); + + private: + + Irq(Irq const &); + Irq &operator = (Irq const &); + + bool _enabled { false }; + Type _type { SPI }; + State _state { INACTIVE }; + Config _config { LEVEL }; + unsigned _num { 0 }; + Genode::uint8_t _prio { 0 }; + Genode::uint8_t _target { 1 }; + List & _pending_list; + Irq_handler * _handler { nullptr }; + }; + + struct Irq_reg: Mmio_register + { + unsigned const irq_count; + + virtual Register read(Irq & irq) { return 0; } + virtual void write(Irq & irq, Register reg) { } + + Register read(Address_range & access, Cpu&) override; + void write(Address_range & access, Cpu&, Register value) override; + + Irq_reg(Mmio_register::Name name, + Mmio_register::Type type, + Genode::uint64_t start, + unsigned bits_per_irq, + unsigned irq_count) + : Mmio_register(name, type, start, bits_per_irq*irq_count/8), + irq_count(irq_count) {} + }; + + class Gicd_banked + { + public: + + Irq & irq(unsigned num); + void handle_irq(); + bool pending_irq(); + + Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus); + + private: + + Cpu & _cpu; + Gic & _gic; + Genode::Constructible _sgi[MAX_SGI]; + Genode::Constructible _ppi[MAX_PPI]; + Irq::List _pending_list; + + struct Redistributor : Mmio_device + { + unsigned cpu_id; + bool last; + Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO, + 0x0, 4, 0b10010 }; + Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO, + 0x8, 8, (Genode::uint64_t)cpu_id<<32 | cpu_id<<8 | (last ? 1<<4 : 0) }; + Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO, + 0x14, 4, 0 }; + Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO, + 0xffe8, 4, (3<<4) }; + Mmio_register gicr_igroupr0 { "GICR_IGROUPR0", Mmio_register::RO, + 0x10080, 4, 0 }; + + struct Gicr_isenabler0 : Irq_reg + { + Register read(Irq & irq) { return irq.enabled(); } + void write(Irq & irq, Register v) { if (v) irq.enable(); } + + Gicr_isenabler0() + : Irq_reg("GICR_ISENABLER0", Mmio_register::RW, 0x10100, 1, 32) {} + } gicr_isenabler0; + + struct Gicr_icenabler0 : Irq_reg + { + Register read(Irq & irq) { return irq.enabled(); } + void write(Irq & irq, Register v) { if (v) irq.disable(); } + + Gicr_icenabler0() + : Irq_reg("GICR_ICENABLER0", Mmio_register::RW, 0x10180, 1, 32) {} + } gicr_icenabler0; + + struct Gicr_ispendr0 : Irq_reg + { + Register read(Irq & irq) { return irq.pending(); } + void write(Irq & irq, Register v) { if (v) irq.assert(); } + + Gicr_ispendr0() + : Irq_reg("GICR_ISPENDR0", Mmio_register::RW, 0x10200, 1, 32) {} + } gicr_ispendr0; + + struct Gicr_icpendr0 : Irq_reg + { + Register read(Irq & irq) { return irq.pending(); } + void write(Irq & irq, Register v) { if (v) irq.deassert(); } + + Gicr_icpendr0() + : Irq_reg("GICR_ICPENDR0", Mmio_register::RW, 0x10280, 1, 32) {} + } gicr_icpendr0; + + struct Gicr_isactiver0 : Irq_reg + { + Register read(Irq & irq) { return irq.active(); } + void write(Irq & irq, Register v) { if (v) irq.activate(); } + + Gicr_isactiver0() + : Irq_reg("GICR_ISACTIVER0", Mmio_register::RW, 0x10300, 1, 32) {} + } gicr_isactiver0; + + struct Gicr_icactiver0 : Irq_reg + { + Register read(Irq & irq) { return irq.active(); } + void write(Irq & irq, Register v) { if (v) irq.deactivate(); } + + Gicr_icactiver0() + : Irq_reg("GICR_ICACTIVER0", Mmio_register::RW, 0x10380, 1, 32) {} + } gicr_icactiver0; + + struct Gicr_ipriorityr : Irq_reg + { + Register read(Irq & irq) { return irq.priority(); } + void write(Irq & irq, Register v) { irq.priority(v); } + + Gicr_ipriorityr() + : Irq_reg("GICR_IPRIORITYR", Mmio_register::RW, 0x10400, 8, 32) {} + } gicr_ipriorityr; + + struct Gicr_icfgr : Irq_reg + { + Register read(Irq & irq) { return irq.level() ? 0 : 1; } + void write(Irq & irq, Register v) { irq.level(!v); } + + Gicr_icfgr() + : Irq_reg("GICR_ICFGR", Mmio_register::RW, 0x10c00, 8, 32) {} + } gicr_icfgr; + + Redistributor(const Genode::uint64_t addr, + const Genode::uint64_t size, + unsigned cpu_id, + bool last) + : Mmio_device("GICR", addr, size), cpu_id(cpu_id), last(last) + { + add(gicr_ctlr); + add(gicr_typer); + add(gicr_waker); + add(gicr_pidr2); + add(gicr_igroupr0); + add(gicr_isenabler0); + add(gicr_icenabler0); + add(gicr_ispendr0); + add(gicr_icpendr0); + add(gicr_isactiver0); + add(gicr_icactiver0); + add(gicr_ipriorityr); + add(gicr_icfgr); + } + }; + + Genode::Constructible _rdist; + }; + + Gic(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Mmio_bus & bus, + Genode::Env & env); + + private: + + friend struct Gicd_banked; + + Genode::Constructible _spi[MAX_SPI]; + Irq::List _pending_list; + unsigned _cpu_cnt { 2 }; /* FIXME: smp support */ + unsigned _version { 3 }; /* FIXME: version support */ + + struct Gicd_ctlr : Genode::Register<32>, Mmio_register + { + struct Enable : Bitfield<0, 1> {}; + struct Disable : Bitfield<6, 1> {}; + + void write(Address_range & access, Cpu & cpu, + Mmio_register::Register value) override + { + access_t v = value; + Disable::set(v, 0); + Mmio_register::write(access, cpu, v); + } + + Gicd_ctlr() + : Mmio_register("GICD_CTLR", Mmio_register::RW, 0, 4, 0) {} + } _ctrl; + + struct Gicd_typer : Genode::Register<32>, Mmio_register + { + struct It_lines_number : Bitfield<0, 5> {}; + struct Cpu_number : Bitfield<5, 3> {}; + struct Id_bits : Bitfield<19, 5> {}; + + Gicd_typer(unsigned cpus) + : Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4, + It_lines_number::bits(31) | Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {} + } _typer { _cpu_cnt }; + + struct Gicd_iidr : Genode::Register<32>, Mmio_register + { + struct Implementer : Bitfield<0, 12> {}; + struct Revision : Bitfield<12, 4> {}; + struct Variant : Bitfield<16, 4> {}; + struct Product_id : Bitfield<24, 8> {}; + + Gicd_iidr() + : Mmio_register("GICD_IIDR", Mmio_register::RO, 0x8, 4, 0x123) {} + } _iidr; + + struct Gicd_igroupr : Irq_reg + { + Gicd_igroupr() + : Irq_reg("GICD_IGROUPR", Mmio_register::RW, 0x80, 1, 1024) {} + } _igroupr; + + struct Gicd_isenabler : Irq_reg + { + Register read(Irq & irq) { return irq.enabled(); } + void write(Irq & irq, Register v) { if (v) irq.enable(); } + + Gicd_isenabler() + : Irq_reg("GICD_ISENABLER", Mmio_register::RW, 0x100, 1, 1024) {} + } _isenabler; + + struct Gicd_icenabler : Irq_reg + { + Register read(Irq & irq) { return irq.enabled(); } + void write(Irq & irq, Register v) { if (v) irq.disable(); } + + Gicd_icenabler() + : Irq_reg("GICD_ICENABLER", Mmio_register::RW, 0x180, 1, 1024) {} + } _csenabler; + + struct Gicd_ispendr : Irq_reg + { + Register read(Irq & irq) { return irq.pending(); } + void write(Irq & irq, Register v) { if (v) irq.assert(); } + + Gicd_ispendr() + : Irq_reg("GICD_ISPENDR", Mmio_register::RW, 0x200, 1, 1024) {} + } _ispendr; + + struct Gicd_icpendr : Irq_reg + { + Register read(Irq & irq) { return irq.pending(); } + void write(Irq & irq, Register v) { if (v) irq.deassert(); } + + Gicd_icpendr() + : Irq_reg("GICD_ICPENDR", Mmio_register::RW, 0x280, 1, 1024) {} + } _icpendr; + + struct Gicd_isactiver : Irq_reg + { + Register read(Irq & irq) { return irq.active(); } + void write(Irq & irq, Register v) { if (v) irq.activate(); } + + Gicd_isactiver() + : Irq_reg("GICD_ISACTIVER", Mmio_register::RW, 0x300, 1, 1024) {} + } _isactiver; + + struct Gicd_icactiver : Irq_reg + { + Register read(Irq & irq) { return irq.active(); } + void write(Irq & irq, Register v) { if (v) irq.deactivate(); } + + Gicd_icactiver() + : Irq_reg("GICD_ICACTIVER", Mmio_register::RW, 0x380, 1, 1024) {} + } _icactiver; + + struct Gicd_ipriorityr : Irq_reg + { + Register read(Irq & irq) { return irq.priority(); } + void write(Irq & irq, Register v) { irq.priority(v); } + + Gicd_ipriorityr() + : Irq_reg("GICD_IPRIORITYR", Mmio_register::RW, 0x400, 8, 1024) {} + } _ipriorityr; + + struct Gicd_itargetr : Irq_reg + { + Register read(Irq & irq) { return irq.target(); } + void write(Irq & irq, Register v) { irq.target(v); } + + Gicd_itargetr() + : Irq_reg("GICD_ITARGETSR", Mmio_register::RW, 0x800, 8, 1024) {} + } _itargetr; + + + struct Gicd_icfgr : Irq_reg + { + Register read(Irq & irq) { return irq.level() ? 0 : 1; } + void write(Irq & irq, Register v) { irq.level(!v); } + + Gicd_icfgr() + : Irq_reg("GICD_ICFGR", Mmio_register::RW, 0xc00, 8, 1024) {} + } _icfgr; + + struct Gicd_sgir : Genode::Register<32>, Mmio_register + { + struct Enable : Bitfield<0, 1> {}; + struct Disable : Bitfield<6, 1> {}; + + void write(Address_range & access, Cpu & cpu, + Mmio_register::Register value) override + { + Genode::error("SGIR WRITE ", value); + } + + Gicd_sgir() + : Mmio_register("GICD_SGIR", Mmio_register::WO, 0xf00, 4, 0) {} + } _sgir; + + + struct Gicd_irouter : Irq_reg + { + Register read(Irq &) { return 0x0; } // FIXME smp + void write(Irq &, Register) { } + + Gicd_irouter() + : Irq_reg("GICD_IROUTER", Mmio_register::RW, 0x6100, 64, 1024) {} + } _irouter; + + /** + * FIXME: missing registers: + * GICD_IGROUP = 0x80, + * GICD_NSACR = 0xe00, + * GICD_SGIR = 0xf00, + * GICD_CPENDSGIR0 = 0xf10, + * GICD_SPENDSGIR0 = 0xf20, + * GICD identification registers 0xfd0... + */ + + /** + * Dummy container for holding array of noncopyable objects + * Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70395 + */ + struct Dummy { + Mmio_register regs[8]; + } _reg_container { .regs = { + { "GICD_PIDR4", Mmio_register::RO, 0xffd0, 4, 0x0 }, + { "GICD_PIDR5", Mmio_register::RO, 0xffd4, 4, 0x0 }, + { "GICD_PIDR6", Mmio_register::RO, 0xffd8, 4, 0x0 }, + { "GICD_PIDR7", Mmio_register::RO, 0xffdc, 4, 0x0 }, + { "GICD_PIDR0", Mmio_register::RO, 0xffe0, 4, 0x492 }, + { "GICD_PIDR1", Mmio_register::RO, 0xffe4, 4, 0xb0 }, + { "GICD_PIDR2", Mmio_register::RO, 0xffe8, 4, (_version<<4) | 0xb }, + { "GICD_PIDR3", Mmio_register::RO, 0xffec, 4, 0x44 } + }}; +}; + +#endif /* _SRC__SERVER__VMM__GIC_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/gicv2.cc b/repos/os/src/server/vmm/spec/arm_v8/gicv2.cc new file mode 100644 index 000000000..05bc1cc83 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/gicv2.cc @@ -0,0 +1,251 @@ +/* + * \brief VMM ARM Generic Interrupt Controller v2 device model + * \author Stefan Kalkowski + * \date 2019-08-05 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include + +using Vmm::Gic; +using Register = Vmm::Mmio_register::Register; + +bool Gic::Irq::enabled() const { return _enabled; } + +bool Gic::Irq::active() const { + return _state == ACTIVE || _state == ACTIVE_PENDING; } + +bool Gic::Irq::pending() const { + return _state == PENDING || _state == ACTIVE_PENDING; } + +bool Gic::Irq::level() const { return _config == LEVEL; } + +unsigned Gic::Irq::number() const { return _num; } + +Genode::uint8_t Gic::Irq::priority() const { return _prio; } + +Genode::uint8_t Gic::Irq::target() const { return _target; } + + +void Gic::Irq::enable() +{ + _enabled = true; + if (_handler) _handler->enabled(); +} + + +void Gic::Irq::disable() +{ + _enabled = false; + if (_handler) _handler->disabled(); +} + + +void Gic::Irq::activate() +{ + switch (_state) { + case INACTIVE: return; + case PENDING: return; + case ACTIVE_PENDING: _state = PENDING; return; + case ACTIVE: _state = INACTIVE; return; + }; +} + + +void Gic::Irq::deactivate() +{ + switch (_state) { + case INACTIVE: return; + case PENDING: return; + case ACTIVE_PENDING: _state = PENDING; return; + case ACTIVE: _state = INACTIVE; return; + }; +} + + +void Gic::Irq::assert() +{ + if (pending()) return; + + _state = PENDING; + _pending_list.insert(*this); +} + + +void Gic::Irq::deassert() +{ + if (_state == INACTIVE) return; + + _state = INACTIVE; + _pending_list.remove(this); + if (_handler) _handler->eoi(); +} + + +void Gic::Irq::target(Genode::uint8_t t) +{ + if (_target == t) return; + + _target = t; +} + + +void Gic::Irq::level(bool l) +{ + if (level() == l) return; + + _config = l ? LEVEL : EDGE; +} + + +void Gic::Irq::priority(Genode::uint8_t p) +{ + if (_prio == p) return; + + _prio = p; +} + + +void Gic::Irq::handler(Gic::Irq::Irq_handler & handler) { _handler = &handler; } + + +Gic::Irq::Irq(unsigned num, Type t, Irq::List & l) +: _type(t), _num(num), _pending_list(l) {} + + +void Gic::Irq::List::insert(Irq & irq) +{ + Irq * i = first(); + while (i && i->priority() < irq.priority() && i->next()) i = i->next(); + Genode::List::insert(&irq, i); +} + + +Gic::Irq * Gic::Irq::List::highest_enabled() +{ + Irq * i = first(); + while(i) { + if (i->enabled()) return i; + i = i->next(); + } + return nullptr; +} + + +Gic::Irq & Gic::Gicd_banked::irq(unsigned i) +{ + if (i < MAX_SGI) + return *_sgi[i]; + + if (i < MAX_SGI+MAX_PPI) + return *_ppi[i-MAX_SGI]; + + return *_gic._spi[i-MAX_SGI-MAX_PPI]; +} + + +void Gic::Gicd_banked::handle_irq() +{ + unsigned i = _cpu.state().irqs.virtual_irq; + if (i > MAX_IRQ) return; + + irq(i).deassert(); + + _cpu.state().irqs.virtual_irq = 1023; +} + + +bool Gic::Gicd_banked::pending_irq() +{ + if (_cpu.state().irqs.virtual_irq != 1023) return true; + + Irq * i = _gic._pending_list.highest_enabled(); + Irq * j = _pending_list.highest_enabled(); + Irq * n = j; + if (i && ((j && j->priority() > i->priority()) || !j)) n = i; + if (!n) return false; + _cpu.state().irqs.virtual_irq = n->number(); + n->activate(); + return true; +} + + +Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus) +: _cpu(cpu), _gic(gic) +{ + for (unsigned i = 0; i < MAX_SGI; i++) + _sgi[i].construct(i, Irq::SGI, _pending_list); + + for (unsigned i = 0; i < MAX_PPI; i++) + _ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list); + + _cpu.state().irqs.last_irq = 1023; + _cpu.state().irqs.virtual_irq = 1023; + + _rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id()); + bus.add(*_rdist); +} + + +Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu) +{ + Register ret = 0; + + Register bits_per_irq = size * 8 / irq_count; + for (unsigned i = (access.start * 8) / bits_per_irq; + i < ((access.start+access.size) * 8) / bits_per_irq; i++) + ret |= read(cpu.gic().irq(i)) << (i % 32/bits_per_irq); + return ret; +} + + +void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value) +{ + Register bits_per_irq = size * 8 / irq_count; + Register irq_value_mask = (1<> (i % 32/bits_per_irq)) + & irq_value_mask); +} + + +Gic::Gic(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Mmio_bus & bus, + Genode::Env & env) +: Mmio_device(name, addr, size) +{ + add(_ctrl); + add(_typer); + add(_iidr); + add(_igroupr); + add(_isenabler); + add(_csenabler); + add(_ispendr); + add(_icpendr); + add(_isactiver); + add(_icactiver); + add(_ipriorityr); + add(_itargetr); + add(_icfgr); + add(_irouter); + add(_sgir); + + for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++) + add(_reg_container.regs[i]); + + for (unsigned i = 0; i < MAX_SPI; i++) + _spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list); + + bus.add(*this); +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/hw_device.h b/repos/os/src/server/vmm/spec/arm_v8/hw_device.h new file mode 100644 index 000000000..97bd14ca0 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/hw_device.h @@ -0,0 +1,125 @@ +/* + * \brief VMM dedicated hardware device passed-through to VM + * \author Stefan Kalkowski + * \date 2019-09-25 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__HW_DEVICE_H_ +#define _SRC__SERVER__VMM__HW_DEVICE_H_ + +#include +#include +#include + +#include +#include + +namespace Vmm { + template class Hw_device; +} + + +template +class Vmm::Hw_device +{ + private: + + class Irq : Gic::Irq::Irq_handler + { + private: + + using Session = Genode::Constructible; + + Gic::Irq & _irq; + Genode::Env & _env; + Session _session; + Cpu::Signal_handler _handler; + + void _assert() + { + _irq.assert(); + } + + public: + + void enabled() override + { + if (!_session.constructed()) { + _session.construct(_env, _irq.number(), + _irq.level() ? + Genode::Irq_session::TRIGGER_LEVEL : + Genode::Irq_session::TRIGGER_EDGE); + _session->sigh(_handler); + _session->ack_irq(); + } + } + + void disabled() override + { + if (_session.constructed()) + _session.destruct(); + } + + void eoi() override + { + if (_session.constructed()) _session->ack_irq(); + } + + Irq(Gic::Irq & irq, Cpu & cpu, Genode::Env & env) + : _irq(irq), + _env(env), + _handler(cpu, env.ep(), *this, &Irq::_assert) + { + _irq.handler(*this); + } + }; + + Genode::Env & _env; + Genode::Vm_connection & _vm; + Cpu & _cpu; + + Genode::Constructible _ds[MMIO_COUNT]; + Genode::Constructible _irqs[IRQ_COUNT]; + + void mmio() { } + void irqs() { } + + public: + + Hw_device(Genode::Env &env, + Genode::Vm_connection &vm, + Cpu & cpu) + : _env(env), _vm(vm), _cpu(cpu) { }; + + template + void mmio(Genode::addr_t start, Genode::size_t size, ARGS &&... args) + { + mmio(args...); + for (unsigned i = 0; i < MMIO_COUNT; i++) { + if (_ds[i].constructed()) continue; + _ds[i].construct(_env, start, size); + _vm.attach(_ds[i]->cap(), start); + return; + } + } + + template + void irqs(unsigned irq, ARGS &&... args) + { + irqs(args...); + for (unsigned i = 0; i < IRQ_COUNT; i++) { + if (_irqs[i].constructed()) continue; + _irqs[i].construct(_cpu.gic().irq(irq), _cpu, _env); + return; + } + } +}; + +#endif /* _SRC__SERVER__VMM__HW_DEVICE_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/main.cc b/repos/os/src/server/vmm/spec/arm_v8/main.cc new file mode 100644 index 000000000..cdd6b19d2 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/main.cc @@ -0,0 +1,17 @@ +/* + * \brief VMM example for ARMv8 virtualization + * \author Stefan Kalkowski + * \date 2014-07-08 + */ + +/* + * Copyright (C) 2014-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +void Component::construct(Genode::Env & env) { static Vmm::Vm vm(env); } diff --git a/repos/os/src/server/vmm/spec/arm_v8/mmio.cc b/repos/os/src/server/vmm/spec/arm_v8/mmio.cc new file mode 100644 index 000000000..c7a1eb7fb --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/mmio.cc @@ -0,0 +1,126 @@ +/* + * \brief VMM mmio abstractions + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +using namespace Vmm; + +Mmio_register::Register Mmio_register::read(Address_range & access, Cpu & cpu) +{ + if (_type == WO) + throw Exception("Invalid read access to register ", + _name, " ", access); + + using namespace Genode; + + switch (access.size) { + case 1: return *(uint8_t*) ((addr_t)&_value + access.start); + case 2: return *(uint16_t*) ((addr_t)&_value + access.start); + case 4: return *(uint32_t*) ((addr_t)&_value + access.start); + case 8: return _value; + default: return 0; + } +} + + +void Mmio_register::write(Address_range & access, Cpu & cpu, Register value) +{ + if (_type == RO) + throw Exception("Invalid write access to register ", + _name, " ", access); + + using namespace Genode; + + switch (access.size) { + case 1: *((uint8_t*) ((addr_t)&_value + access.start)) = value; + case 2: *((uint16_t*) ((addr_t)&_value + access.start)) = value; + case 4: *((uint32_t*) ((addr_t)&_value + access.start)) = value; + case 8: _value = value; + } +} + + +Mmio_register::Register Mmio_register::value() { return _value; } + + +void Mmio_register::set(Register value) { _value = value; } + + +Mmio_device::Register Mmio_device::read(Address_range & access, Cpu & cpu) +{ + Mmio_register & reg = _registers.get(access); + Address_range ar(access.start - reg.start, access.size); + return reg.read(ar, cpu); +} + + +void Mmio_device::write(Address_range & access, Cpu & cpu, Register value) +{ + Mmio_register & reg = _registers.get(access); + Address_range ar(access.start - reg.start, access.size); + reg.write(ar, cpu, value); +} + + +void Mmio_device::add(Mmio_register & reg) { _registers.add(reg); } + + +void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu) +{ + using namespace Genode; + + Cpu::State & state = cpu.state(); + + struct Iss : Cpu::Esr + { + struct Write : Bitfield<6, 1> {}; + struct Register : Bitfield<16, 5> {}; + struct Sign_extend : Bitfield<21, 1> {}; + struct Access_size : Bitfield<22, 2> { + enum { BYTE, HALFWORD, WORD, DOUBLEWORD }; }; + struct Valid : Bitfield<24, 1> {}; + + static bool valid(access_t v) { + return Valid::get(v) && !Sign_extend::get(v); } + + static bool write(access_t v) { return Write::get(v); } + static unsigned r(access_t v) { return Register::get(v); } + }; + + if (!Iss::valid(state.esr_el2)) + throw Exception("Mmio_bus: unknown ESR=", Genode::Hex(state.esr_el2)); + + bool wr = Iss::Write::get(state.esr_el2); + unsigned idx = Iss::Register::get(state.esr_el2); + uint64_t ipa = ((uint64_t)state.hpfar_el2 << 8) + + (state.far_el2 & ((1 << 12) - 1)); + uint64_t width = 1 << Iss::Access_size::get(state.esr_el2); + + try { + Address_range bus_range(ipa, width); + Mmio_device & dev = get(bus_range); + Address_range dev_range(ipa - dev.start,width); + if (wr) { + dev.write(dev_range, cpu, (idx == 31) ? 0 : state.r[idx]); + } else { + if (idx > 30) + throw Exception("Wrong register index when reading ", bus_range); + state.r[idx] = dev.read(dev_range, cpu); + } + } catch(Exception & e) { + Genode::warning(e); + Genode::warning("Will ignore invalid bus access (IPA=", + Genode::Hex(ipa), ")"); + } +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/mmio.h b/repos/os/src/server/vmm/spec/arm_v8/mmio.h new file mode 100644 index 000000000..7b743b180 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/mmio.h @@ -0,0 +1,90 @@ +/* + * \brief VMM mmio abstractions + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__MMIO_H_ +#define _SRC__SERVER__VMM__MMIO_H_ + +#include + +namespace Vmm { + class Cpu; + class Mmio_register; + class Mmio_device; + class Mmio_bus; +} + + +class Vmm::Mmio_register : public Vmm::Address_range +{ + public: + + enum Type { RO, WO, RW }; + + using Name = Genode::String<64>; + using Register = Genode::uint64_t; + + virtual Register read(Address_range & access, Cpu&); + virtual void write(Address_range & access, Cpu&, Register value); + void set(Register value); + Register value(); + + Mmio_register(Name name, + Type type, + Genode::uint64_t start, + Genode::uint64_t size, + Register reset_value = 0) + : Address_range(start, size), + _name(name), + _type(type), + _value(reset_value) { } + + protected: + + Name const _name; + Type const _type; + Register _value; + + bool _unaligned(Address_range & access); +}; + + +class Vmm::Mmio_device : public Vmm::Address_range +{ + public: + + using Name = Genode::String<64>; + using Register = Genode::uint64_t; + + virtual Register read(Address_range & access, Cpu&); + virtual void write(Address_range & access, Cpu&, Register value); + + void add(Mmio_register & reg); + + Mmio_device(Name name, + Genode::uint64_t start, + Genode::uint64_t size) + : Address_range(start, size), _name(name) { } + + private: + + Name const _name; + Address_space _registers; +}; + + +struct Vmm::Mmio_bus : Vmm::Address_space +{ + void handle_memory_access(Cpu & cpu); +}; + +#endif /* _SRC__SERVER__VMM__MMIO_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/pl011.cc b/repos/os/src/server/vmm/spec/arm_v8/pl011.cc new file mode 100644 index 000000000..e9903fd51 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/pl011.cc @@ -0,0 +1,102 @@ +/* + * \brief VMM PL011 serial device model + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +using Vmm::Pl011; +using Register = Vmm::Mmio_register::Register; + +Register Pl011::Uartdr::read(Address_range&, Cpu&) +{ + ris.set(ris.value() & ~RX_MASK); + + if (!rx.empty()) return rx.get(); + + return 0; +} + + +void Pl011::Uartdr::write(Address_range&, Cpu&, Register reg) +{ + terminal.write(®, 1); +} + + +Register Pl011::Uartfr::read(Address_range&, Cpu&) +{ + return rx.empty() ? Rx_empty::bits(1) : Rx_full::bits(1); +} + + +void Pl011::Uartimsc::write(Address_range&, Cpu&, Register mask) +{ + if ((mask & RX_MASK) && !(value() & RX_MASK) && (ris.value() & RX_MASK)) + irq.assert(); + + set(mask); +} + + +Register Pl011::Uartmis::read(Address_range&, Cpu&) +{ + return ris.value() & imsc.value(); +} + + +void Pl011::Uarticr::write(Address_range & ar, Cpu &, Register value) +{ + ris.set(ris.value() & ~value); +} + + +void Pl011::_read() +{ + if (!_terminal.avail()) return; + + while (_terminal.avail()) { + unsigned char c = 0; + _terminal.read(&c, 1); + _rx_buf.add(c); + } + + _uart_ris.set(_uart_ris.value() | RX_MASK); + if (_uart_imsc.value() & RX_MASK) _irq.assert(); +} + + +Pl011::Pl011(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + unsigned irq, + Cpu & cpu, + Mmio_bus & bus, + Genode::Env & env) +: Mmio_device(name, addr, size), + _terminal(env), + _handler(cpu, env.ep(), *this, &Pl011::_read), + _irq(cpu.gic().irq(irq)) +{ + for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++) + add(_reg_container.regs[i]); + add(_uart_ris); + add(_uart_dr); + add(_uart_fr); + add(_uart_imsc); + add(_uart_mis); + add(_uart_icr); + + _terminal.read_avail_sigh(_handler); + + bus.add(*this); +} diff --git a/repos/os/src/server/vmm/spec/arm_v8/pl011.h b/repos/os/src/server/vmm/spec/arm_v8/pl011.h new file mode 100644 index 000000000..abd1f9b50 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/pl011.h @@ -0,0 +1,145 @@ +/* + * \brief VMM PL011 serial device model + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__PL011_H_ +#define _SRC__SERVER__VMM__PL011_H_ + +#include +#include + +#include + +namespace Vmm { + class Cpu; + class Pl011; +} + +class Vmm::Pl011 : public Vmm::Mmio_device +{ + private: + + using Ring_buffer = + Genode::Ring_buffer; + + enum Mask : Genode::uint16_t { + RX_MASK = 1 << 4, + TX_MASK = 1 << 5 + }; + + Terminal::Connection _terminal; + Cpu::Signal_handler _handler; + Gic::Irq & _irq; + Ring_buffer _rx_buf; + Mmio_register _uart_ris { "UARTRIS", Mmio_register::RO, + 0x3c, 2 }; + + /** + * Dummy container for holding array of noncopyable objects + * Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70395 + */ + struct Dummy { + Mmio_register regs[13]; + } _reg_container { .regs = { + { "UARTIBRD", Mmio_register::RW, 0x24, 2, 0 }, + { "UARTFBRD", Mmio_register::RW, 0x28, 2, 0 }, + { "UARTLCR_H", Mmio_register::RW, 0x2c, 2, 0 }, + { "UARTCR", Mmio_register::RW, 0x30, 2, 0x300 }, + { "UARTIFLS", Mmio_register::RW, 0x34, 2, 0x12 }, + { "UARTPERIPHID0", Mmio_register::RO, 0xfe0, 4, 0x11 }, + { "UARTPERIPHID1", Mmio_register::RO, 0xfe4, 4, 0x10 }, + { "UARTPERIPHID2", Mmio_register::RO, 0xfe8, 4, 0x14 }, + { "UARTPERIPHID3", Mmio_register::RO, 0xfec, 4, 0x0 }, + { "UARTPCELLID0", Mmio_register::RO, 0xff0, 4, 0xd }, + { "UARTPCELLID1", Mmio_register::RO, 0xff4, 4, 0xf0 }, + { "UARTPCELLID2", Mmio_register::RO, 0xff8, 4, 0x5 }, + { "UARTPCELLID3", Mmio_register::RO, 0xffc, 4, 0xb1 } + }}; + + struct Uartdr : Mmio_register + { + Terminal::Connection & terminal; + Ring_buffer & rx; + Mmio_register & ris; + + Register read(Address_range&, Cpu&) override; + void write(Address_range&, Cpu&, Register) override; + + Uartdr(Terminal::Connection & terminal, + Ring_buffer & rx, + Mmio_register & ris) + : Mmio_register("UARTDR", Mmio_register::RW, 0x0, 2), + terminal(terminal), rx(rx), ris(ris) {} + } _uart_dr { _terminal, _rx_buf, _uart_ris }; + + struct Uartfr : Mmio_register, Genode::Register<32> + { + struct Rx_empty : Bitfield<4, 1> {}; + struct Rx_full : Bitfield<6, 1> {}; + + Ring_buffer & rx; + + Mmio_register::Register read(Address_range&, Cpu&) override; + + Uartfr(Ring_buffer & rx) + : Mmio_register("UARTFR", Mmio_register::RO, 0x18, 4), rx(rx) {} + } _uart_fr { _rx_buf }; + + struct Uartimsc : Mmio_register + { + Gic::Irq & irq; + Mmio_register & ris; + + void write(Address_range&, Cpu&, Register) override; + + Uartimsc(Gic::Irq & irq, Mmio_register & ris) + : Mmio_register("UARTIMSC", Mmio_register::RW, 0x38, 2, 0xf), + irq(irq), ris(ris) {} + } _uart_imsc { _irq, _uart_ris }; + + struct Uartmis : Mmio_register + { + Mmio_register & ris; + Uartimsc & imsc; + + Register read(Address_range&, Cpu&) override; + + Uartmis(Mmio_register & ris, Uartimsc & imsc) + : Mmio_register("UARTMIS", Mmio_register::RO, 0x40, 2), + ris(ris), imsc(imsc) {} + } _uart_mis { _uart_ris, _uart_imsc }; + + struct Uarticr : Mmio_register + { + Mmio_register & ris; + + void write(Address_range&, Cpu&, Register) override; + + Uarticr(Mmio_register & ris) + : Mmio_register("UARTICR", Mmio_register::WO, 0x44, 2), ris(ris) {} + } _uart_icr { _uart_ris }; + + void _read(); + + public: + + Pl011(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + unsigned irq, + Cpu & cpu, + Mmio_bus & bus, + Genode::Env & env); +}; + +#endif /* _SRC__SERVER__VMM__PL011_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/psci.h b/repos/os/src/server/vmm/spec/arm_v8/psci.h new file mode 100644 index 000000000..ead06b8e3 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/psci.h @@ -0,0 +1,35 @@ +/* + * \brief Power State Coordination Interface for ARMv8 virtualization + * \author Stefan Kalkowski + * \date 2019-10-21 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__PSCI_H_ +#define _SRC__SERVER__VMM__PSCI_H_ + +namespace Vmm { + + namespace Psci { + enum Function_id { + PSCI_VERSION = 0x84000000, + MIGRATE_INFO_TYPE = 0x84000006, + PSCI_FEATURES = 0x8400000a, + CPU_ON = 0xc4000003, + }; + + enum { + VERSION = 1 << 16, /* 1.0 */ + SUCCESS = 0, + NOT_SUPPORTED = -1, + }; + }; +}; + +#endif /* _SRC__SERVER__VMM__PSCI_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/ram.h b/repos/os/src/server/vmm/spec/arm_v8/ram.h new file mode 100644 index 000000000..9789e6221 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/ram.h @@ -0,0 +1,39 @@ +/* + * \brief VMM ram object + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__RAM_H_ +#define _SRC__SERVER__VMM__RAM_H_ + +#include + +class Ram { + + private: + + Genode::addr_t const _base; + Genode::size_t const _size; + Genode::addr_t const _local; + + public: + + Ram(Genode::addr_t const addr, + Genode::size_t const sz, + Genode::addr_t const local) + : _base(addr), _size(sz), _local(local) { } + + Genode::addr_t base() const { return _base; } + Genode::size_t size() const { return _size; } + Genode::addr_t local() const { return _local; } +}; + +#endif /* _SRC__SERVER__VMM__RAM_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/target.mk b/repos/os/src/server/vmm/spec/arm_v8/target.mk new file mode 100644 index 000000000..797b883a9 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/target.mk @@ -0,0 +1,14 @@ +TARGET = vmm +REQUIRES = hw arm_v8 +LIBS = base +SRC_CC += address_space.cc +SRC_CC += cpu.cc +SRC_CC += generic_timer.cc +SRC_CC += gicv2.cc +SRC_CC += main.cc +SRC_CC += mmio.cc +SRC_CC += pl011.cc +SRC_CC += vm.cc +INC_DIR += $(PRG_DIR) + +CC_CXX_WARN_STRICT := diff --git a/repos/os/src/server/vmm/spec/arm_v8/virt.dts b/repos/os/src/server/vmm/spec/arm_v8/virt.dts new file mode 100755 index 000000000..92a0110c8 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/virt.dts @@ -0,0 +1,65 @@ +/dts-v1/; + +/ { + compatible = "linux,dummy-virt"; + #address-cells = <0x02>; + #size-cells = <0x02>; + interrupt-parent = <0x8001>; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + + cpu@0 { + compatible = "arm,cortex-a53"; + reg = <0x00>; + device_type = "cpu"; + }; + }; + + timer { + interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>; + compatible = "arm,armv8-timer\0arm,armv7-timer"; + always-on; + }; + + apb-pclk { + compatible = "fixed-clock"; + phandle = <0x8000>; + clock-output-names = "clk24mhz"; + clock-frequency = <0x16e3600>; + #clock-cells = <0x00>; + }; + + pl011@9000000 { + interrupts = <0x00 0x01 0x04>; + compatible = "arm,pl011\0arm,primecell"; + clock-names = "uartclk\0apb_pclk"; + reg = <0x00 0x9000000 0x00 0x1000>; + clocks = <0x8000 0x8000>; + }; + + memory@40000000 { + reg = <0x00 0x40000000 0x00 0x8000000>; + device_type = "memory"; + }; + + chosen { + bootargs = "rdinit=/bin/sh"; + linux,initrd-start = <0x42000000>; + linux,initrd-end = <0x42113b86>; + stdout-path = "/pl011@9000000"; + }; + + intc@8000000 { + compatible = "arm,gic-v3"; + phandle = <0x8001>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + ranges; + #address-cells = <0x02>; + #redistributor-regions = <0x01>; + interrupt-controller; + #interrupt-cells = <0x03>; + #size-cells = <0x02>; + }; +}; diff --git a/repos/os/src/server/vmm/spec/arm_v8/vm.cc b/repos/os/src/server/vmm/spec/arm_v8/vm.cc new file mode 100644 index 000000000..abe2e8e35 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/vm.cc @@ -0,0 +1,75 @@ +/* + * \brief VMM example for ARMv8 virtualization + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include + +using Vmm::Vm; + +void Vm::_load_kernel() +{ + Genode::memcpy((void*)(_ram.local() + KERNEL_OFFSET), + _kernel_rom.local_addr(), + _kernel_rom.size()); +} + +void Vm::_load_dtb() +{ + Genode::memcpy((void*)(_ram.local() + DTB_OFFSET), + _dtb_rom.local_addr(), + _dtb_rom.size()); +} + + +void Vm::_load_initrd() +{ + Genode::memcpy((void*)(_ram.local() + INITRD_OFFSET), + _initrd_rom.local_addr(), + _initrd_rom.size()); +} + + +Vmm::Cpu & Vm::boot_cpu() +{ + if (!_cpus[0].constructed()) + _cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep()); + return *_cpus[0]; +} + + +Vm::Vm(Genode::Env & env) +: _env(env), + _gic("Gicv3", 0x8000000, 0x10000, _bus, env), + _uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env) +{ + _vm.attach(_vm_ram.cap(), RAM_ADDRESS); + + /* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */ + + _load_kernel(); + _load_dtb(); + _load_initrd(); + + for (unsigned i = 1; i < MAX_CPUS; i++) { + Genode::Affinity::Space space = _env.cpu().affinity_space(); + Genode::Affinity::Location location(space.location_of_index(i)); + _eps[i].construct(_env, STACK_SIZE, "vcpu ep", location); + _cpus[i].construct(*this, _vm, _bus, _gic, _env, _heap, *_eps[i]); + } + + Genode::log("Start virtual machine ..."); + + Cpu & cpu = boot_cpu(); + cpu.state().ip = _ram.base() + KERNEL_OFFSET; + cpu.state().r[0] = _ram.base() + DTB_OFFSET; + cpu.run(); +}; diff --git a/repos/os/src/server/vmm/spec/arm_v8/vm.h b/repos/os/src/server/vmm/spec/arm_v8/vm.h new file mode 100644 index 000000000..b1b78cbaf --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/vm.h @@ -0,0 +1,82 @@ +/* + * \brief VMM example for ARMv8 virtualization + * \author Stefan Kalkowski + * \date 2019-07-18 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__SERVER__VMM__VM_H_ +#define _SRC__SERVER__VMM__VM_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Vmm { class Vm; } + +class Vmm::Vm +{ + private: + + using Ep = Genode::Entrypoint; + + enum { + RAM_ADDRESS = 0x40000000, + RAM_SIZE = 128 * 1024 *1024, + KERNEL_OFFSET = 0x80000, + INITRD_OFFSET = 32 * 1024 * 1024, + DTB_OFFSET = 64 * 1024 * 1024, + MAX_CPUS = 1, + STACK_SIZE = sizeof(unsigned long) * 2048, + }; + + Genode::Env & _env; + Genode::Vm_connection _vm { _env }; + Genode::Attached_rom_dataspace _kernel_rom { _env, "linux" }; + Genode::Attached_rom_dataspace _dtb_rom { _env, "dtb" }; + Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" }; + Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(), + RAM_SIZE, Genode::UNCACHED }; + Ram _ram { RAM_ADDRESS, RAM_SIZE, + (Genode::addr_t)_vm_ram.local_addr()}; + Genode::Heap _heap { _env.ram(), _env.rm() }; + Mmio_bus _bus; + Gic _gic; + Genode::Constructible _eps[MAX_CPUS]; + Genode::Constructible _cpus[MAX_CPUS]; + Pl011 _uart; + + void _load_kernel(); + void _load_dtb(); + void _load_initrd(); + + public: + + Vm(Genode::Env & env); + + Mmio_bus & bus() { return _bus; } + Cpu & boot_cpu(); + + template + void cpu(unsigned cpu, F func) + { + if (cpu >= MAX_CPUS) Genode::error("Cpu number out of bounds "); + else func(*_cpus[cpu]); + } + + static unsigned last_cpu() { return MAX_CPUS - 1; } +}; + +#endif /* _SRC__SERVER__VMM__VM_H_ */