mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 14:37:50 +00:00
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
This commit is contained in:
parent
2879aa003b
commit
90d9470dfd
2
repos/os/recipes/src/vmm/content.mk
Normal file
2
repos/os/recipes/src/vmm/content.mk
Normal file
@ -0,0 +1,2 @@
|
||||
SRC_DIR = src/server/vmm
|
||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/os/recipes/src/vmm/hash
Normal file
1
repos/os/recipes/src/vmm/hash
Normal file
@ -0,0 +1 @@
|
||||
2021-02-19 d3b2bfd047b47215092d5fad951ab172b3fcb468
|
7
repos/os/recipes/src/vmm/used_apis
Normal file
7
repos/os/recipes/src/vmm/used_apis
Normal file
@ -0,0 +1,7 @@
|
||||
base-hw
|
||||
base
|
||||
block_session
|
||||
nic_session
|
||||
os
|
||||
terminal_session
|
||||
timer_session
|
@ -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 {
|
||||
<config verbose="yes" prio_levels="2">
|
||||
@ -39,10 +41,12 @@ install_config {
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="nic_drv">
|
||||
<binary name="nic_router" />
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
@ -57,14 +61,29 @@ install_config {
|
||||
</domain>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="vfs_block">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Block"/> </provides>
|
||||
<config>
|
||||
<vfs> <rom name="block.img"/> </vfs>
|
||||
<default-policy file="/block.img" block_size="512"/>
|
||||
</config>
|
||||
<route>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="log_terminal">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Terminal"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="terminal_crosslink">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Terminal"/> </provides>
|
||||
</start>
|
||||
|
||||
<start name="vmm" caps="200" priority="-1">
|
||||
<resource name="RAM" quantum="256M"/>
|
||||
<route>
|
||||
@ -74,10 +93,11 @@ install_config {
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="vm">
|
||||
<binary name="test-terminal_expect_send"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config expect="/ #" send="ls" verbose="yes"/>
|
||||
<config expect="/ #" send="mount /dev/vda /root; cp -r /etc /root; ls /root/etc; umount /root" verbose="yes"/>
|
||||
<route>
|
||||
<service name="Terminal"> <child name="terminal_crosslink"/> </service>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
@ -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
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
284
repos/os/src/server/vmm/virtio_block.h
Normal file
284
repos/os/src/server/vmm/virtio_block.h
Normal file
@ -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 <base/log.h>
|
||||
#include <block_session/connection.h>
|
||||
|
||||
#include <virtio_device.h>
|
||||
|
||||
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 <typename FUNC>
|
||||
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_queue::Idx>(_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 <typename T>
|
||||
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>(_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<info.block_size) ? 1 : (sz/info.block_size)
|
||||
};
|
||||
return op;
|
||||
}
|
||||
|
||||
void * address() const { return _desc_addr<void>(_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<uint8_t>(_status) = OK;
|
||||
queue.ack(_request_idx, _written);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Vmm::Virtio_block_device
|
||||
: public Virtio_device<Virtio_block_queue, 1>
|
||||
{
|
||||
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
|
||||
{
|
||||
Job(Block::Connection<Job> & 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>::Job(con, Virtio_block_request::operation(info)) {}
|
||||
};
|
||||
|
||||
Heap & _heap;
|
||||
Allocator_avl _block_alloc { &_heap };
|
||||
Block::Connection<Job> _block;
|
||||
Block::Session::Info _block_info { _block.info() };
|
||||
Cpu::Signal_handler<Virtio_block_device> _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<Virtio_block_queue,1>(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_ */
|
@ -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);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <pl011.h>
|
||||
#include <virtio_console.h>
|
||||
#include <virtio_net.h>
|
||||
#include <virtio_block.h>
|
||||
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user