e0561e60f1
The ARM virt machines put firmware in flash memory. To configure it,
you use -drive if=pflash,unit=0,... and optionally -drive
if=pflash,unit=1,...
Why two -drive? This permits setting up one part of the flash memory
read-only, and the other part read/write. It also makes upgrading
firmware on the host easier. Below the hood, we get two separate
flash devices, because we were too lazy to improve our flash device
models to support sector protection.
The problem at hand is to do the same with -blockdev somehow, as one
more step towards deprecating -drive.
We recently solved this problem for x86 PC machines, in commit
ebc29e1bea
. See the commit message for design rationale.
This commit solves it for ARM virt basically the same way: new machine
properties pflash0, pflash1 forward to the onboard flash devices'
properties. Requires creating the onboard devices in the
.instance_init() method virt_instance_init(). The existing code to
pick up drives defined with -drive if=pflash is replaced by code to
desugar into the machine properties.
There are a few behavioral differences, though:
* The flash devices are always present (x86: only present if
configured)
* Flash base addresses and sizes are fixed (x86: sizes depend on
images, mapped back to back below a fixed address)
* -bios configures contents of first pflash (x86: -bios configures ROM
contents)
* -bios is rejected when first pflash is also configured with -machine
pflash0=... (x86: bios is silently ignored then)
* -machine pflash1=... does not require -machine pflash0=... (x86: it
does).
The actual code is a bit simpler than for x86 mostly due to the first
two differences.
Before the patch, all the action is in create_flash(), called from the
machine's .init() method machvirt_init():
main()
machine_run_board_init()
machvirt_init()
create_flash()
create_one_flash() for flash[0]
create
configure
includes obeying -drive if=pflash,unit=0
realize
map
fall back to -bios
create_one_flash() for flash[1]
create
configure
includes obeying -drive if=pflash,unit=1
realize
map
update FDT
To make the machine properties work, we need to move device creation
to its .instance_init() method virt_instance_init().
Another complication is machvirt_init()'s computation of
@firmware_loaded: it predicts what create_flash() will do. Instead of
predicting what create_flash()'s replacement virt_firmware_init() will
do, I decided to have virt_firmware_init() return what it did.
Requires calling it a bit earlier.
Resulting call tree:
main()
current_machine = object_new()
...
virt_instance_init()
virt_flash_create()
virt_flash_create1() for flash[0]
create
configure: set defaults
become child of machine [NEW]
add machine prop pflash0 as alias for drive [NEW]
virt_flash_create1() for flash[1]
create
configure: set defaults
become child of machine [NEW]
add machine prop pflash1 as alias for drive [NEW]
for all machine props from the command line: machine_set_property()
...
property_set_alias() for machine props pflash0, pflash1
...
set_drive() for cfi.pflash01 prop drive
this is how -machine pflash0=... etc set
machine_run_board_init(current_machine);
virt_firmware_init()
pflash_cfi01_legacy_drive()
legacy -drive if=pflash,unit=0 and =1 [NEW]
virt_flash_map()
virt_flash_map1() for flash[0]
configure: num-blocks
realize
map
virt_flash_map1() for flash[1]
configure: num-blocks
realize
map
fall back to -bios
virt_flash_fdt()
update FDT
You have László to thank for making me explain this in detail.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Message-id: 20190416091348.26075-4-armbru@redhat.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
163 lines
4.3 KiB
C
163 lines
4.3 KiB
C
/*
|
|
*
|
|
* Copyright (c) 2015 Linaro Limited
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2 or later, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Emulate a virtual board which works by passing Linux all the information
|
|
* it needs about what devices are present via the device tree.
|
|
* There are some restrictions about what we can do here:
|
|
* + we can only present devices whose Linux drivers will work based
|
|
* purely on the device tree with no platform data at all
|
|
* + we want to present a very stripped-down minimalist platform,
|
|
* both because this reduces the security attack surface from the guest
|
|
* and also because it reduces our exposure to being broken when
|
|
* the kernel updates its device tree bindings and requires further
|
|
* information in a device binding that we aren't providing.
|
|
* This is essentially the same approach kvmtool uses.
|
|
*/
|
|
|
|
#ifndef QEMU_ARM_VIRT_H
|
|
#define QEMU_ARM_VIRT_H
|
|
|
|
#include "qemu-common.h"
|
|
#include "exec/hwaddr.h"
|
|
#include "qemu/notify.h"
|
|
#include "hw/boards.h"
|
|
#include "hw/arm/arm.h"
|
|
#include "hw/block/flash.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "hw/intc/arm_gicv3_common.h"
|
|
|
|
#define NUM_GICV2M_SPIS 64
|
|
#define NUM_VIRTIO_TRANSPORTS 32
|
|
#define NUM_SMMU_IRQS 4
|
|
|
|
#define ARCH_GIC_MAINT_IRQ 9
|
|
|
|
#define ARCH_TIMER_VIRT_IRQ 11
|
|
#define ARCH_TIMER_S_EL1_IRQ 13
|
|
#define ARCH_TIMER_NS_EL1_IRQ 14
|
|
#define ARCH_TIMER_NS_EL2_IRQ 10
|
|
|
|
#define VIRTUAL_PMU_IRQ 7
|
|
|
|
#define PPI(irq) ((irq) + 16)
|
|
|
|
enum {
|
|
VIRT_FLASH,
|
|
VIRT_MEM,
|
|
VIRT_CPUPERIPHS,
|
|
VIRT_GIC_DIST,
|
|
VIRT_GIC_CPU,
|
|
VIRT_GIC_V2M,
|
|
VIRT_GIC_HYP,
|
|
VIRT_GIC_VCPU,
|
|
VIRT_GIC_ITS,
|
|
VIRT_GIC_REDIST,
|
|
VIRT_SMMU,
|
|
VIRT_UART,
|
|
VIRT_MMIO,
|
|
VIRT_RTC,
|
|
VIRT_FW_CFG,
|
|
VIRT_PCIE,
|
|
VIRT_PCIE_MMIO,
|
|
VIRT_PCIE_PIO,
|
|
VIRT_PCIE_ECAM,
|
|
VIRT_PLATFORM_BUS,
|
|
VIRT_GPIO,
|
|
VIRT_SECURE_UART,
|
|
VIRT_SECURE_MEM,
|
|
VIRT_LOWMEMMAP_LAST,
|
|
};
|
|
|
|
/* indices of IO regions located after the RAM */
|
|
enum {
|
|
VIRT_HIGH_GIC_REDIST2 = VIRT_LOWMEMMAP_LAST,
|
|
VIRT_HIGH_PCIE_ECAM,
|
|
VIRT_HIGH_PCIE_MMIO,
|
|
};
|
|
|
|
typedef enum VirtIOMMUType {
|
|
VIRT_IOMMU_NONE,
|
|
VIRT_IOMMU_SMMUV3,
|
|
VIRT_IOMMU_VIRTIO,
|
|
} VirtIOMMUType;
|
|
|
|
typedef struct MemMapEntry {
|
|
hwaddr base;
|
|
hwaddr size;
|
|
} MemMapEntry;
|
|
|
|
typedef struct {
|
|
MachineClass parent;
|
|
bool disallow_affinity_adjustment;
|
|
bool no_its;
|
|
bool no_pmu;
|
|
bool claim_edge_triggered_timers;
|
|
bool smbios_old_sys_ver;
|
|
bool no_highmem_ecam;
|
|
} VirtMachineClass;
|
|
|
|
typedef struct {
|
|
MachineState parent;
|
|
Notifier machine_done;
|
|
DeviceState *platform_bus_dev;
|
|
FWCfgState *fw_cfg;
|
|
PFlashCFI01 *flash[2];
|
|
bool secure;
|
|
bool highmem;
|
|
bool highmem_ecam;
|
|
bool its;
|
|
bool virt;
|
|
int32_t gic_version;
|
|
VirtIOMMUType iommu;
|
|
struct arm_boot_info bootinfo;
|
|
MemMapEntry *memmap;
|
|
const int *irqmap;
|
|
int smp_cpus;
|
|
void *fdt;
|
|
int fdt_size;
|
|
uint32_t clock_phandle;
|
|
uint32_t gic_phandle;
|
|
uint32_t msi_phandle;
|
|
uint32_t iommu_phandle;
|
|
int psci_conduit;
|
|
hwaddr highest_gpa;
|
|
} VirtMachineState;
|
|
|
|
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
|
|
|
|
#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
|
|
#define VIRT_MACHINE(obj) \
|
|
OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE)
|
|
#define VIRT_MACHINE_GET_CLASS(obj) \
|
|
OBJECT_GET_CLASS(VirtMachineClass, obj, TYPE_VIRT_MACHINE)
|
|
#define VIRT_MACHINE_CLASS(klass) \
|
|
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
|
|
|
|
void virt_acpi_setup(VirtMachineState *vms);
|
|
|
|
/* Return the number of used redistributor regions */
|
|
static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
|
|
{
|
|
uint32_t redist0_capacity =
|
|
vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
|
|
|
|
assert(vms->gic_version == 3);
|
|
|
|
return vms->smp_cpus > redist0_capacity ? 2 : 1;
|
|
}
|
|
|
|
#endif /* QEMU_ARM_VIRT_H */
|