From 90d9470dfd6cbbfa95902f464b868699a842162c Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Fri, 19 Feb 2021 11:02:58 +0100 Subject: [PATCH] vmm: add virtio block device model * Add new virtio device model * Extend test run-script with vfat block test image * Add vmm depot src recipe * Use packages in test run-script Fix #4025 --- repos/os/recipes/src/vmm/content.mk | 2 + repos/os/recipes/src/vmm/hash | 1 + repos/os/recipes/src/vmm/used_apis | 7 + repos/os/run/vmm_arm.run | 62 ++-- repos/os/src/server/vmm/spec/arm_v7/board.h | 4 + repos/os/src/server/vmm/spec/arm_v7/virt.dts | 7 + repos/os/src/server/vmm/spec/arm_v8/board.h | 4 + repos/os/src/server/vmm/spec/arm_v8/virt.dts | 7 + repos/os/src/server/vmm/virtio_block.h | 284 +++++++++++++++++++ repos/os/src/server/vmm/vm.cc | 4 +- repos/os/src/server/vmm/vm.h | 2 + 11 files changed, 361 insertions(+), 23 deletions(-) create mode 100644 repos/os/recipes/src/vmm/content.mk create mode 100644 repos/os/recipes/src/vmm/hash create mode 100644 repos/os/recipes/src/vmm/used_apis create mode 100644 repos/os/src/server/vmm/virtio_block.h diff --git a/repos/os/recipes/src/vmm/content.mk b/repos/os/recipes/src/vmm/content.mk new file mode 100644 index 0000000000..4988150987 --- /dev/null +++ b/repos/os/recipes/src/vmm/content.mk @@ -0,0 +1,2 @@ +SRC_DIR = src/server/vmm +include $(GENODE_DIR)/repos/base/recipes/src/content.inc diff --git a/repos/os/recipes/src/vmm/hash b/repos/os/recipes/src/vmm/hash new file mode 100644 index 0000000000..3e529f8cc6 --- /dev/null +++ b/repos/os/recipes/src/vmm/hash @@ -0,0 +1 @@ +2021-02-19 d3b2bfd047b47215092d5fad951ab172b3fcb468 diff --git a/repos/os/recipes/src/vmm/used_apis b/repos/os/recipes/src/vmm/used_apis new file mode 100644 index 0000000000..2e642939a5 --- /dev/null +++ b/repos/os/recipes/src/vmm/used_apis @@ -0,0 +1,7 @@ +base-hw +base +block_session +nic_session +os +terminal_session +timer_session diff --git a/repos/os/run/vmm_arm.run b/repos/os/run/vmm_arm.run index 83a810c5a5..d5b5e54bbe 100644 --- a/repos/os/run/vmm_arm.run +++ b/repos/os/run/vmm_arm.run @@ -12,16 +12,18 @@ if { ![have_board imx7d_sabre] && ![have_board imx8q_evk] && exit 0 } -set build_components { - core init timer - server/terminal_crosslink - test/terminal_expect_send - server/log_terminal - server/nic_router - server/vmm -} -build $build_components create_boot_directory +import_from_depot [depot_user]/src/[base_src] \ + [depot_user]/src/init \ + [depot_user]/src/libc \ + [depot_user]/src/log_terminal \ + [depot_user]/src/nic_router \ + [depot_user]/src/terminal_crosslink \ + [depot_user]/src/vfs \ + [depot_user]/src/vfs_block \ + [depot_user]/src/vmm + +build { test/terminal_expect_send } install_config { @@ -39,10 +41,12 @@ install_config { + + @@ -57,14 +61,29 @@ install_config { + + + + + + + + + + + + + + + @@ -74,10 +93,11 @@ install_config { + - + @@ -96,7 +116,7 @@ if { [have_spec arm] } { if {![file exists bin/dtb]} { puts "Download device tree blob ..." - exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.05/dtb-arm32-virt + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-21.02/dtb-arm32-virt } if {![file exists bin/initrd]} { @@ -153,7 +173,7 @@ if { [have_spec arm_64] } { if {![file exists bin/dtb]} { puts "Download device tree blob ..." - exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.11/dtb-arm64-virt-smp + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-21.02/dtb-arm64-virt-smp } if {![file exists bin/initrd]} { @@ -200,23 +220,21 @@ if { [have_spec arm_64] } { # find . | cpio -H newc -o | gzip > ../initrd } -set boot_modules { - core ld.lib.so init - timer - terminal_crosslink +catch { exec [installed_command dd] if=/dev/zero of=bin/block.img bs=1M count=0 seek=64 } +exec [installed_command mkfs.vfat] bin/block.img + +build_boot_image { test-terminal_expect_send - nic_router - log_terminal - vmm linux dtb initrd + block.img } -build_boot_image $boot_modules # # Execute test case # append qemu_args " -nographic " -run_genode_until "\[init -> vm\] .*sbin.*" 220 -exec rm bin/linux bin/dtb bin/initrd + +run_genode_until "\[init -> vm\] .*resolv.conf.*" 220 +exec rm bin/linux bin/dtb bin/initrd bin/block.img diff --git a/repos/os/src/server/vmm/spec/arm_v7/board.h b/repos/os/src/server/vmm/spec/arm_v7/board.h index da0a557848..32d533d72d 100644 --- a/repos/os/src/server/vmm/spec/arm_v7/board.h +++ b/repos/os/src/server/vmm/spec/arm_v7/board.h @@ -41,6 +41,10 @@ namespace Vmm { VIRTIO_NET_MMIO_SIZE = 0x200, VIRTIO_NET_IRQ = 49, + VIRTIO_BLK_MMIO_START = 0xa000400, + VIRTIO_BLK_MMIO_SIZE = 0x200, + VIRTIO_BLK_IRQ = 50, + RAM_START = 0x40000000, RAM_SIZE = 128 * 1024 *1024, diff --git a/repos/os/src/server/vmm/spec/arm_v7/virt.dts b/repos/os/src/server/vmm/spec/arm_v7/virt.dts index 81e3f34f7d..4007ed7cb9 100755 --- a/repos/os/src/server/vmm/spec/arm_v7/virt.dts +++ b/repos/os/src/server/vmm/spec/arm_v7/virt.dts @@ -86,4 +86,11 @@ dma-coherent; reg = <0x00 0xa000200 0x00 0x200>; }; + + virtio_mmio@a000400 { + interrupts = <0x00 0x12 0x01>; + compatible = "virtio,mmio"; + dma-coherent; + reg = <0x00 0xa000400 0x00 0x200>; + }; }; diff --git a/repos/os/src/server/vmm/spec/arm_v8/board.h b/repos/os/src/server/vmm/spec/arm_v8/board.h index ec9a1993d0..9f17addb85 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/board.h +++ b/repos/os/src/server/vmm/spec/arm_v8/board.h @@ -41,6 +41,10 @@ namespace Vmm { VIRTIO_NET_MMIO_SIZE = 0x200, VIRTIO_NET_IRQ = 49, + VIRTIO_BLK_MMIO_START = 0xa000400, + VIRTIO_BLK_MMIO_SIZE = 0x200, + VIRTIO_BLK_IRQ = 50, + RAM_START = 0x40000000, RAM_SIZE = 128 * 1024 *1024, diff --git a/repos/os/src/server/vmm/spec/arm_v8/virt.dts b/repos/os/src/server/vmm/spec/arm_v8/virt.dts index c455598d01..7c29c669d0 100755 --- a/repos/os/src/server/vmm/spec/arm_v8/virt.dts +++ b/repos/os/src/server/vmm/spec/arm_v8/virt.dts @@ -114,4 +114,11 @@ dma-coherent; reg = <0x00 0xa000200 0x00 0x200>; }; + + virtio_mmio@a000400 { + interrupts = <0x00 0x12 0x01>; + compatible = "virtio,mmio"; + dma-coherent; + reg = <0x00 0xa000400 0x00 0x200>; + }; }; diff --git a/repos/os/src/server/vmm/virtio_block.h b/repos/os/src/server/vmm/virtio_block.h new file mode 100644 index 0000000000..e0ec30a69a --- /dev/null +++ b/repos/os/src/server/vmm/virtio_block.h @@ -0,0 +1,284 @@ +/* + * \brief Virtio Block implementation + * \author Stefan Kalkowski + * \date 2020-12-22 + */ + +/* + * Copyright (C) 2020 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 _VIRTIO_BLOCK_H_ +#define _VIRTIO_BLOCK_H_ + +#include +#include + +#include + +namespace Vmm +{ + class Virtio_block_queue; + class Virtio_block_request; + class Virtio_block_device; + + using namespace Genode; +} + + +class Vmm::Virtio_block_queue : public Virtio_split_queue +{ + private: + + Ring_index _used_idx; + + friend class Virtio_block_request; + friend class Virtio_block_device; + + public: + + using Virtio_split_queue::Virtio_split_queue; + + template + bool notify(FUNC func) + { + memory_barrier(); + for (Ring_index avail_idx = _avail.current(); + _cur_idx != avail_idx; _cur_idx.inc()) { + func(_avail.get(_cur_idx), _descriptors, _ram); + } + return false; + } + + void ack(Descriptor_index id, size_t written) + { + _used.add(_used_idx, id, written); + _used_idx.inc(); + _used.write(_used_idx.idx()); + memory_barrier(); + } +}; + + +class Vmm::Virtio_block_request +{ + public: + + struct Invalid_request : Genode::Exception {}; + + private: + + using Index = Virtio_block_queue::Descriptor_index; + using Descriptor = Virtio_block_queue::Descriptor; + using Descriptor_array = Virtio_block_queue::Descriptor_array; + + struct Request + { + enum Type { + READ = 0, + WRITE = 1, + FLUSH = 4, + DISCARD = 11, + WRITE_ZEROES = 13, + }; + + uint32_t type; + uint32_t reserved; + uint64_t sector; + }; + + enum Status { OK, IO_ERROR, UNSUPPORTED }; + + Index _next(Descriptor & desc) { + if (!Descriptor::Flags::Next::get(desc.flags())) { + throw Invalid_request(); } + return desc.next(); + } + + Descriptor_array & _array; + Ram & _ram; + + template + T * _desc_addr(Descriptor const & desc) const { + return (T*) _ram.local_address(desc.address(), + desc.length()); } + + Index _request_idx; + Descriptor _request { _array.get(_request_idx) }; + Request & _vbr { *_desc_addr(_request) }; + Index _data_idx { _next(_request) }; + Descriptor _data { _array.get(_data_idx) }; + Index _status_idx { _next(_data) }; + Descriptor _status { _array.get(_status_idx) }; + size_t _written { 0 }; + + bool _write() const { return _vbr.type == Request::WRITE; } + + public: + + Virtio_block_request(Index id, + Descriptor_array & array, + Ram & ram) + : _array(array), _ram(ram), _request_idx(id) + { + if (_request.length() != sizeof(Request) || + _status.length() != sizeof(uint8_t)) { + throw Invalid_request(); } + } + + Block::Operation const operation(Block::Session::Info & info) const + { + if (_vbr.type != Request::READ && _vbr.type != Request::WRITE) { + throw Invalid_request(); } + + size_t sz = _data.length(); + + Block::Operation const op { + .type = _write() ? Block::Operation::Type::WRITE + : Block::Operation::Type::READ, + .block_number = (_vbr.sector * 512) / info.block_size, + .count = (sz(_data); } + size_t size() const { return _data.length(); } + void written_to_descriptor(size_t sz) { _written = sz; } + + void done(Virtio_block_queue & queue) + { + *_desc_addr(_status) = OK; + queue.ack(_request_idx, _written); + } +}; + + +class Vmm::Virtio_block_device +: public Virtio_device +{ + private: + + using Index = Virtio_block_queue::Descriptor_index; + using Descriptor_array = Virtio_block_queue::Descriptor_array; + + enum Queue_idx { REQUEST }; + + enum { BLOCK_BUFFER_SIZE = 1024*1024 }; + + struct Job : Virtio_block_request, + Block::Connection::Job + { + Job(Block::Connection & con, + Block::Session::Info & info, + Virtio_block_device::Index id, + Virtio_block_device::Descriptor_array & array, + Ram & ram) + : Virtio_block_request(id, array, ram), + Block::Connection::Job(con, Virtio_block_request::operation(info)) {} + }; + + Heap & _heap; + Allocator_avl _block_alloc { &_heap }; + Block::Connection _block; + Block::Session::Info _block_info { _block.info() }; + Cpu::Signal_handler _handler; + + void _block_signal() + { + Genode::Mutex::Guard guard(_mutex); + _block.update_jobs(*this); + } + + void _notify(unsigned idx) override + { + auto lambda = [&] (Index id, + Descriptor_array & array, + Ram & ram) + { + try { + new (_heap) Job(_block, _block_info, id, array, ram); + _block.update_jobs(*this); + } catch(Virtio_block_request::Invalid_request &) { + error("Invalid block request ignored!"); + } + return 0; + }; + + _queue[REQUEST]->notify(lambda); + } + + enum Device_id { BLOCK = 2 }; + + struct Configuration_area : Mmio_register + { + uint64_t capacity; + + Register read(Address_range& range, Cpu&) override + { + if (range.start == 0 && range.size == 4) + return capacity & 0xffffffff; + + if (range.start == 4 && range.size == 4) + return capacity >> 32; + + throw Exception("Invalid read access of configuration area ", + range); + } + + Configuration_area(Virtio_block_device & device, uint64_t capacity) + : Mmio_register("Configuration_area", Mmio_register::RO, 0x100, 8), + capacity(capacity) { device.add(*this); } + } _config_area{ *this, _block_info.block_count * + (_block_info.block_size / 512) }; + + public: + + Virtio_block_device(const char * const name, + const uint64_t addr, + const uint64_t size, + unsigned irq, + Cpu & cpu, + Mmio_bus & bus, + Ram & ram, + Env & env, + Heap & heap) + : Virtio_device(name, addr, size, + irq, cpu, bus, ram, BLOCK), + _heap(heap), + _block(env, &_block_alloc, BLOCK_BUFFER_SIZE), + _handler(cpu, env.ep(), *this, &Virtio_block_device::_block_signal) { + _block.sigh(_handler); } + + + /***************************************************** + ** Block::Connection::Update_jobs_policy interface ** + *****************************************************/ + + void produce_write_content(Job & job, off_t offset, + char *dst, size_t length) + { + size_t sz = Genode::min(length,job.size()); + memcpy(dst, job.address(), sz); + job.written_to_descriptor(sz); + } + + void consume_read_result(Job & job, off_t offset, + char const *src, size_t length) + { + size_t sz = Genode::min(length,job.size()); + memcpy(job.address(), src, sz); + } + + void completed(Job &job, bool success) + { + job.done(*_queue[REQUEST]); + _assert_irq(); + destroy(_heap, &job); + } +}; + +#endif /* _VIRTIO_BLOCK_H_ */ diff --git a/repos/os/src/server/vmm/vm.cc b/repos/os/src/server/vmm/vm.cc index bfd9b31d17..207b7b32f0 100644 --- a/repos/os/src/server/vmm/vm.cc +++ b/repos/os/src/server/vmm/vm.cc @@ -55,7 +55,9 @@ Vm::Vm(Genode::Env & env) _virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE, VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env), _virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE, - VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env) + VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env), + _virtio_block("Block", VIRTIO_BLK_MMIO_START, VIRTIO_BLK_MMIO_SIZE, + VIRTIO_BLK_IRQ, boot_cpu(), _bus, _ram, env, _heap) { _vm.attach(_vm_ram.cap(), RAM_START); diff --git a/repos/os/src/server/vmm/vm.h b/repos/os/src/server/vmm/vm.h index 8b2819d811..6e11b97a56 100644 --- a/repos/os/src/server/vmm/vm.h +++ b/repos/os/src/server/vmm/vm.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ class Vmm::Vm Pl011 _uart; Virtio_console _virtio_console; Virtio_net _virtio_net; + Virtio_block_device _virtio_block; void _load_kernel(); void _load_dtb();