os: enable smp support for ARM vmm

* Fix GIC model to support priority and cpu target settings correspondingly
* Fix semantic of SGIR register for GICv2
* Minor GIC model IRQ state fix
* Introduce synchronization for VirtIO and GIC models
* Enable multiple CPUs in test run-script for ARMv8

Fix #3926
This commit is contained in:
Stefan Kalkowski 2020-09-11 14:45:47 +02:00 committed by Christian Helmuth
parent 6be09a27ca
commit 429cd8d37a
14 changed files with 185 additions and 44 deletions

View File

@ -150,7 +150,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.02/dtb-arm64-virt
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.11/dtb-arm64-virt-smp
}
if {![file exists bin/initrd]} {
@ -216,4 +216,4 @@ build_boot_image $boot_modules
#
append qemu_args " -nographic "
run_genode_until "\[init -> vm\] .*sbin.*" 220
exec rm bin/linux bin/dtb
exec rm bin/linux bin/dtb bin/initrd

View File

@ -90,6 +90,7 @@ void Cpu_base::_handle_sync()
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
case Esr::Ec::HVC_32: [[fallthrough]];
case Esr::Ec::HVC:
_handle_hyper_call();
break;
@ -137,6 +138,7 @@ void Cpu_base::_handle_hyper_call()
case Psci::PSCI_FEATURES:
_state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::CPU_ON_32: [[fallthrough]];
case Psci::CPU_ON:
_vm.cpu((unsigned)_state.reg(1), [&] (Cpu & cpu) {
cpu.state().ip = _state.reg(2);

View File

@ -45,6 +45,7 @@ class Vmm::Cpu_base
enum {
WFI = 0x1,
MRC_MCR = 0x3,
HVC_32 = 0x12,
HVC = 0x16,
MRS_MSR = 0x18,
DA = 0x24,
@ -219,6 +220,10 @@ class Vmm::Cpu_base
void _handle_data_abort();
void _handle_hyper_call();
void _update_state();
public:
Vm & vm() { return _vm; }
};
#endif /* _SRC__SERVER__VMM__CPU_BASE_H_ */

View File

@ -58,8 +58,9 @@ void Generic_timer::schedule_timeout()
}
if (_enabled()) {
if (_usecs_left()) {
_timeout.schedule(Genode::Microseconds(_usecs_left()));
Genode::uint64_t usecs = _usecs_left();
if (usecs) {
_timeout.schedule(Genode::Microseconds(usecs));
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
}
}
@ -67,7 +68,7 @@ void Generic_timer::schedule_timeout()
void Generic_timer::cancel_timeout()
{
if (_timeout.scheduled()) _timeout.discard();
if (_timeout.scheduled()) { _timeout.discard(); }
}

View File

@ -18,6 +18,13 @@
using Vmm::Gic;
using Register = Vmm::Mmio_register::Register;
static Genode::Mutex & big_gic_lock()
{
static Genode::Mutex mutex;
return mutex;
}
bool Gic::Irq::enabled() const { return _enabled; }
bool Gic::Irq::active() const {
@ -52,10 +59,10 @@ void Gic::Irq::disable()
void Gic::Irq::activate()
{
switch (_state) {
case INACTIVE: return;
case PENDING: return;
case ACTIVE_PENDING: _state = PENDING; return;
case ACTIVE: _state = INACTIVE; return;
case INACTIVE: _state = ACTIVE; return;
case PENDING: _state = ACTIVE_PENDING; return;
case ACTIVE_PENDING: return;
case ACTIVE: return;
};
}
@ -75,6 +82,8 @@ void Gic::Irq::assert()
{
if (pending()) return;
Genode::Mutex::Guard guard(big_gic_lock());
_state = PENDING;
_pending_list.insert(*this);
}
@ -84,6 +93,8 @@ void Gic::Irq::deassert()
{
if (_state == INACTIVE) return;
Genode::Mutex::Guard guard(big_gic_lock());
_state = INACTIVE;
_pending_list.remove(this);
if (_handler) _handler->eoi();
@ -124,17 +135,17 @@ Gic::Irq::Irq(unsigned num, Type t, Irq::List & l)
void Gic::Irq::List::insert(Irq & irq)
{
Irq * i = first();
while (i && i->priority() < irq.priority() && i->next()) i = i->next();
while (i && i->priority() <= irq.priority() && i->next()) i = i->next();
Genode::List<Irq>::insert(&irq, i);
}
Gic::Irq * Gic::Irq::List::highest_enabled()
Gic::Irq * Gic::Irq::List::highest_enabled(unsigned cpu_id)
{
Irq * i = first();
while(i) {
if (i->enabled()) return i;
i = i->next();
for (Irq * i = first(); i; i = i->next()) {
if (!i->enabled() || i->active()) { continue; }
if (cpu_id < ~0U && (i->target() != cpu_id)) { continue; }
return i;
}
return nullptr;
}
@ -165,6 +176,8 @@ void Gic::Gicd_banked::handle_irq()
bool Gic::Gicd_banked::pending_irq()
{
Genode::Mutex::Guard guard(big_gic_lock());
if (_cpu.state().irqs.virtual_irq != SPURIOUS) return true;
Irq * i = _gic._pending_list.highest_enabled();
@ -202,6 +215,8 @@ Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu)
{
Genode::Mutex::Guard guard(big_gic_lock());
Register ret = 0;
Register bits_per_irq = size * 8 / irq_count;
@ -214,6 +229,8 @@ Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu)
void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
{
Genode::Mutex::Guard guard(big_gic_lock());
Register bits_per_irq = size * 8 / irq_count;
Register irq_value_mask = (1<<bits_per_irq) - 1;
for (unsigned i = (access.start * 8) / bits_per_irq;
@ -226,6 +243,50 @@ void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
unsigned Gic::version() { return _version; }
void Gic::Gicd_sgir::write(Address_range &, Cpu & cpu,
Mmio_register::Register value)
{
Target_filter::Target_type type =
(Target_filter::Target_type) Target_filter::get(value);
unsigned target_list = Target_list::get(value);
unsigned irq = Int_id::get(value);
for (unsigned i = 0; i <= Vm::last_cpu(); i++) {
switch (type) {
case Target_filter::MYSELF:
if (i != cpu.cpu_id()) { continue; }
break;
case Target_filter::ALL:
if (i == cpu.cpu_id()) { continue; }
break;
case Target_filter::LIST:
if (!(target_list & (1<<i))) { continue; }
break;
default:
continue;
};
cpu.vm().cpu(i, [&] (Cpu & c) {
c.gic().irq(irq).assert();
if (cpu.cpu_id() != c.cpu_id()) { cpu.recall(); }
});
}
}
Register Gic::Gicd_itargetr::read(Address_range & access, Cpu & cpu)
{
if (access.start < 0x20) { return (1 << cpu.cpu_id()) << 8; }
return Irq_reg::read(access, cpu);
}
void Gic::Gicd_itargetr::write(Address_range & access, Cpu & cpu, Register value)
{
if (access.start >= 0x20) { Irq_reg::write(access, cpu, value); }
}
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,

View File

@ -47,7 +47,7 @@ class Vmm::Gic : public Vmm::Mmio_device
struct List : Genode::List<Irq>
{
void insert(Irq & irq);
Irq * highest_enabled();
Irq * highest_enabled(unsigned cpu_id = ~0U);
};
struct Irq_handler {
@ -92,7 +92,7 @@ class Vmm::Gic : public Vmm::Mmio_device
Config _config { LEVEL };
unsigned _num { 0 };
Genode::uint8_t _prio { 0 };
Genode::uint8_t _target { 1 };
Genode::uint8_t _target { 0 };
List & _pending_list;
Irq_handler * _handler { nullptr };
};
@ -382,6 +382,9 @@ class Vmm::Gic : public Vmm::Mmio_device
Register read(Irq & irq) { return irq.target(); }
void write(Irq & irq, Register v) { irq.target(v); }
Register read(Address_range & access, Cpu&) override;
void write(Address_range & access, Cpu&, Register value) override;
Gicd_itargetr()
: Irq_reg("GICD_ITARGETSR", Mmio_register::RW, 0x800, 8, 1024) {}
} _itargetr;
@ -398,14 +401,14 @@ class Vmm::Gic : public Vmm::Mmio_device
struct Gicd_sgir : Genode::Register<32>, Mmio_register
{
struct Enable : Bitfield<0, 1> {};
struct Disable : Bitfield<6, 1> {};
struct Int_id : Bitfield<0, 4> {};
struct Target_list : Bitfield<16, 8> {};
struct Target_filter : Bitfield<24, 2> {
enum Target_type { LIST, ALL, MYSELF, INVALID };
};
void write(Address_range & access, Cpu & cpu,
Mmio_register::Register value) override
{
Genode::error("SGIR WRITE ", value);
}
Mmio_register::Register value) override;
Gicd_sgir()
: Mmio_register("GICD_SGIR", Mmio_register::WO, 0xf00, 4, 0) {}
@ -414,8 +417,10 @@ class Vmm::Gic : public Vmm::Mmio_device
struct Gicd_irouter : Irq_reg
{
Register read(Irq &) { return 0x0; } // FIXME smp
void write(Irq &, Register) { }
Register read(Irq &) { return 0x0; } /* FIXME affinity routing support */
void write(Irq & i, Register v) {
if (v) Genode::error("Affinity routing not supported ", i.number()); }
Gicd_irouter()
: Irq_reg("GICD_IROUTER", Mmio_register::RW, 0x6100, 64, 1024) {}

View File

@ -21,6 +21,7 @@ namespace Vmm {
PSCI_VERSION = 0x84000000,
MIGRATE_INFO_TYPE = 0x84000006,
PSCI_FEATURES = 0x8400000a,
CPU_ON_32 = 0x84000003,
CPU_ON = 0xc4000003,
};

View File

@ -186,4 +186,5 @@ Cpu::Cpu(Vm & vm,
{
_state.cpsr = 0x93; /* el1 mode and IRQs disabled */
_state.sctrl = 0xc50078;
_state.vmpidr = (1UL << 31) | cpu_id();
}

View File

@ -14,9 +14,18 @@
compatible = "arm,cortex-a15";
reg = <0x00>;
device_type = "cpu";
enable-method = "psci";
};
};
psci {
compatible = "arm,psci-1.0";
method = "hvc";
cpu_suspend = <0xc4000001>;
cpu_off = <0xc4000002>;
cpu_on = <0xc4000003>;
};
timer {
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
compatible = "arm,armv7-timer";

View File

@ -46,7 +46,7 @@ namespace Vmm {
VTIMER_IRQ = 27,
MAX_CPUS = 1,
MAX_CPUS = 4,
};
}

View File

@ -4,39 +4,75 @@
compatible = "linux,dummy-virt";
#address-cells = <0x02>;
#size-cells = <0x02>;
interrupt-parent = <0x8001>;
interrupt-parent = <&gic>;
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x00>;
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x00>;
device_type = "cpu";
enable-method = "psci";
};
cpu@1 {
compatible = "arm,cortex-a53";
reg = <0x01>;
device_type = "cpu";
enable-method = "psci";
};
cpu@2 {
compatible = "arm,cortex-a53";
reg = <0x02>;
device_type = "cpu";
enable-method = "psci";
};
cpu@3 {
compatible = "arm,cortex-a53";
reg = <0x03>;
device_type = "cpu";
enable-method = "psci";
};
};
psci {
compatible = "arm,psci-1.0";
method = "hvc";
cpu_suspend = <0xc4000001>;
cpu_off = <0xc4000002>;
cpu_on = <0xc4000003>;
};
timer {
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
compatible = "arm,armv8-timer\0arm,armv7-timer";
compatible = "arm,armv8-timer", "arm,armv7-timer";
always-on;
};
apb-pclk {
compatible = "fixed-clock";
phandle = <0x8000>;
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
clocks {
#address-cells = <1>;
#size-cells = <0>;
clk_24mhz: clk@0 {
compatible = "fixed-clock";
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
reg = <0>;
};
};
pl011@9000000 {
interrupts = <0x00 0x01 0x04>;
compatible = "arm,pl011\0arm,primecell";
clock-names = "uartclk\0apb_pclk";
compatible = "arm,pl011", "arm,primecell";
clock-names = "uartclk", "apb_pclk";
reg = <0x00 0x9000000 0x00 0x1000>;
clocks = <0x8000 0x8000>;
clocks = <&clk_24mhz>, <&clk_24mhz>;
};
memory@40000000 {
@ -45,17 +81,17 @@
};
chosen {
/* bootargs = "rdinit=/bin/sh console=hvc0 earlycon=pl011,0x9000000"; */
/*bootargs = "rdinit=/bin/sh console=hvc0 earlycon=pl011,0x9000000";*/
bootargs = "init=/sbin/init ip=dhcp console=hvc0";
linux,initrd-start = <0x42000000>;
linux,initrd-end = <0x420aa539>;
stdout-path = "/pl011@9000000";
};
intc@8000000 {
gic: intc@8000000 {
compatible = "arm,gic-v3";
phandle = <0x8001>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>;
reg = <0x00 0x8000000 0x00 0x10000>,
<0x00 0x80a0000 0x00 0xf60000>;
ranges;
#address-cells = <0x02>;
#redistributor-regions = <0x01>;

View File

@ -32,6 +32,8 @@ class Vmm::Virtio_console : public Virtio_device
void _read()
{
Genode::Mutex::Guard guard(_mutex);
auto read = [&] (addr_t data, size_t size)
{
if (!_terminal.avail()) return 0ul;
@ -61,6 +63,7 @@ class Vmm::Virtio_console : public Virtio_device
}
Register _device_specific_features() { return 0; }
public:
Virtio_console(const char * const name,

View File

@ -225,6 +225,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
Genode::Constructible<Virtio_queue> _queue[NUM];
Gic::Irq &_irq;
Ram &_ram;
Genode::Mutex _mutex;
struct Dummy {
Mmio_register regs[7];
@ -344,6 +345,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
if (reg >= device.NUM) return;
device._queue_select(reg);
}
@ -359,6 +361,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().num = Genode::min(reg,
device._reg_container.regs[6].value());
}
@ -374,11 +377,13 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
Register read(Address_range&, Cpu&) override
{
Genode::Mutex::Guard guard(device.mutex());
return device._queue_data().ready;
}
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
bool construct = reg == 1 ? true : false;
device._queue_data().ready = reg;
device._queue_state(construct);
@ -395,6 +400,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
if (!device._queue[reg].constructed()) return;
device._notify(reg);
@ -411,6 +417,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().descr_low = reg;
}
@ -425,6 +432,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().descr_high = reg;
}
@ -439,6 +447,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().driver_low = reg;
}
@ -453,6 +462,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().driver_high = reg;
}
@ -467,6 +477,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().device_low = reg;
}
@ -481,6 +492,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().device_high = reg;
}
@ -507,6 +519,7 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._deassert_irq();
}
@ -525,6 +538,8 @@ class Vmm::Virtio_device : public Vmm::Mmio_device
Mmio_bus &bus,
Ram &ram,
unsigned queue_size = 8);
Genode::Mutex & mutex() { return _mutex; }
};
#endif /* _VIRTIO_DEVICE_H_ */

View File

@ -109,6 +109,8 @@ class Vmm::Virtio_net : public Virtio_device
void _handle()
{
Genode::Mutex::Guard guard(_mutex);
_rx();
_tx();
}