u-boot/board/emulation/qemu-arm/qemu-arm.c

192 lines
3.6 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017 Tuomas Tynkkynen
*/
#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <efi.h>
#include <efi_loader.h>
#include <fdtdec.h>
#include <init.h>
#include <log.h>
#include <virtio_types.h>
#include <virtio.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
/* GUIDs for capsule updatable firmware images */
#define QEMU_ARM_UBOOT_IMAGE_GUID \
EFI_GUID(0xf885b085, 0x99f8, 0x45af, 0x84, 0x7d, \
0xd5, 0x14, 0x10, 0x7a, 0x4a, 0x2c)
#define QEMU_ARM64_UBOOT_IMAGE_GUID \
EFI_GUID(0x058b7d83, 0x50d5, 0x4c47, 0xa1, 0x95, \
0x60, 0xd8, 0x6a, 0xd3, 0x41, 0xc4)
#ifdef CONFIG_ARM64
#include <asm/armv8/mmu.h>
#if CONFIG_IS_ENABLED(EFI_HAVE_CAPSULE_SUPPORT)
struct efi_fw_image fw_images[] = {
#if defined(CONFIG_TARGET_QEMU_ARM_32BIT)
{
.image_type_id = QEMU_ARM_UBOOT_IMAGE_GUID,
.fw_name = u"Qemu-Arm-UBOOT",
.image_index = 1,
},
#elif defined(CONFIG_TARGET_QEMU_ARM_64BIT)
{
.image_type_id = QEMU_ARM64_UBOOT_IMAGE_GUID,
.fw_name = u"Qemu-Arm-UBOOT",
.image_index = 1,
},
#endif
};
struct efi_capsule_update_info update_info = {
.images = fw_images,
};
u8 num_image_type_guids = ARRAY_SIZE(fw_images);
#endif /* EFI_HAVE_CAPSULE_SUPPORT */
static struct mm_region qemu_arm64_mem_map[] = {
{
/* Flash */
.virt = 0x00000000UL,
.phys = 0x00000000UL,
.size = 0x08000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_INNER_SHARE
}, {
/* Lowmem peripherals */
.virt = 0x08000000UL,
.phys = 0x08000000UL,
.size = 0x38000000,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
/* RAM */
.virt = 0x40000000UL,
.phys = 0x40000000UL,
.size = 255UL * SZ_1G,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_INNER_SHARE
}, {
/* Highmem PCI-E ECAM memory area */
.virt = 0x4010000000ULL,
.phys = 0x4010000000ULL,
.size = 0x10000000,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
/* Highmem PCI-E MMIO memory area */
.virt = 0x8000000000ULL,
.phys = 0x8000000000ULL,
.size = 0x8000000000ULL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
/* List terminator */
0,
}
};
struct mm_region *mem_map = qemu_arm64_mem_map;
#endif
int board_init(void)
{
return 0;
}
int board_late_init(void)
{
/*
* Make sure virtio bus is enumerated so that peripherals
* on the virtio bus can be discovered by their drivers
*/
virtio_init();
return 0;
}
int dram_init(void)
{
if (fdtdec_setup_mem_size_base() != 0)
return -EINVAL;
return 0;
}
int dram_init_banksize(void)
{
fdtdec_setup_memory_banksize();
return 0;
}
void *board_fdt_blob_setup(int *err)
{
*err = 0;
/* QEMU loads a generated DTB for us at the start of RAM. */
return (void *)CFG_SYS_SDRAM_BASE;
}
void enable_caches(void)
{
icache_enable();
dcache_enable();
}
arm: qemu: override flash accessors to use virtualizable instructions Some instructions in the ARM ISA have multiple output registers, such as ldrd/ldp (load pair), where two registers are loaded from memory, but also ldr with indexing, where the memory base register is incremented as well when the value is loaded to the destination register. MMIO emulation under KVM is based on using the architecturally defined syndrome information that is provided when an exception is taken to the hypervisor. This syndrome information describes whether the instruction that triggered the exception is a load or a store, what the faulting address was, and which register was the destination register. This syndrome information can only describe one destination register, and when the trapping instruction is one with multiple outputs, KVM throws an error like kvm [615929]: Data abort outside memslots with no valid syndrome info on the host and kills the QEMU process with the following error: U-Boot 2020.07-rc3-00208-g88bd5b179360-dirty (Jun 06 2020 - 11:59:22 +0200) DRAM: 1 GiB Flash: error: kvm run failed Function not implemented R00=00000001 R01=00000040 R02=7ee0ce20 R03=00000000 R04=7ffd9eec R05=00000004 R06=7ffda3f8 R07=00000055 R08=7ffd9eec R09=7ef0ded0 R10=7ee0ce20 R11=00000000 R12=00000004 R13=7ee0cdf8 R14=00000000 R15=7ff72d08 PSR=200001d3 --C- A svc32 QEMU: Terminated This means that, in order to run U-Boot in QEMU under KVM, we need to avoid such instructions when accessing emulated devices. For the flash in particular, which is a hybrid between a ROM (backed by a read-only KVM memslot) when in array mode, and an emulated MMIO device (when in write mode), we need to take care to only use instructions that KVM can deal with when they trap. So override the flash read accessors that are used when running on QEMU under KVM. Note that the the 64-bit wide read and write accessors have been omitted: they are never used when running under QEMU given that it does not emulate CFI flash that supports it. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Andre Przywara <andre.przywara@arm.com>
2020-07-07 10:07:11 +00:00
#ifdef CONFIG_ARM64
#define __W "w"
#else
#define __W
#endif
u8 flash_read8(void *addr)
{
u8 ret;
asm("ldrb %" __W "0, %1" : "=r"(ret) : "m"(*(u8 *)addr));
return ret;
}
u16 flash_read16(void *addr)
{
u16 ret;
asm("ldrh %" __W "0, %1" : "=r"(ret) : "m"(*(u16 *)addr));
return ret;
}
u32 flash_read32(void *addr)
{
u32 ret;
asm("ldr %" __W "0, %1" : "=r"(ret) : "m"(*(u32 *)addr));
return ret;
}
void flash_write8(u8 value, void *addr)
{
asm("strb %" __W "1, %0" : "=m"(*(u8 *)addr) : "r"(value));
}
void flash_write16(u16 value, void *addr)
{
asm("strh %" __W "1, %0" : "=m"(*(u16 *)addr) : "r"(value));
}
void flash_write32(u32 value, void *addr)
{
asm("str %" __W "1, %0" : "=m"(*(u32 *)addr) : "r"(value));
}