[U-Boot] [PATCH v2 0/6] Board for QEMU's '-machine virt' on ARM

These patches add a board for running U-Boot under QEMU's '-machine virt' emulation, thus making it possible to boot Linux distros that use the extlinux.conf booting method under '-machine virt'.
To some extent, this is currently possible by emulating and running U-Boot on some of the Versatile boards, but (IIRC) they have some limitations like limiting to 1GB of RAM or lacking support for PCI.
Changes in v2: - Introduce helper functions for implementing memory-mapped PCI config space accesses + refactor two existing drivers to use them. - Addresses Bin Meng's review comments (in patches 4 & 5) - Hook up test.py to run on Travis CI (patch 6). This requires https://github.com/swarren/uboot-test-hooks/pull/14 to go in first.
Travis run with the uboot-test-hooks hacked to point to my local repository: https://travis-ci.org/dezgeg/u-boot/builds/276392558
Tuomas Tynkkynen (6): pci: Add helper for implementing memory-mapped config space accesses pci: xilinx: Use pci_generic_mmap_{read,write}_config() pci: layerscape: Use pci_generic_mmap_{read,write}_config PCI: Add driver for a 'pci-host-ecam-generic' host controller ARM: Add a new arch + board for QEMU's 'virt' machine travis.yml: Add job for running test.py in qemu_arm
.travis.yml | 5 ++ arch/arm/Kconfig | 10 +++ arch/arm/mach-qemu/Kconfig | 12 +++ board/emulation/qemu-arm/MAINTAINERS | 6 ++ board/emulation/qemu-arm/Makefile | 5 ++ board/emulation/qemu-arm/qemu-arm.c | 33 ++++++++ configs/qemu_arm_defconfig | 28 +++++++ drivers/pci/Kconfig | 8 ++ drivers/pci/Makefile | 1 + drivers/pci/pci-uclass.c | 58 ++++++++++++++ drivers/pci/pcie_ecam_generic.c | 143 +++++++++++++++++++++++++++++++++++ drivers/pci/pcie_layerscape.c | 68 +++++------------ drivers/pci/pcie_xilinx.c | 53 ++----------- include/configs/qemu-arm.h | 58 ++++++++++++++ include/pci.h | 51 +++++++++++++ 15 files changed, 442 insertions(+), 97 deletions(-) create mode 100644 arch/arm/mach-qemu/Kconfig create mode 100644 board/emulation/qemu-arm/MAINTAINERS create mode 100644 board/emulation/qemu-arm/Makefile create mode 100644 board/emulation/qemu-arm/qemu-arm.c create mode 100644 configs/qemu_arm_defconfig create mode 100644 drivers/pci/pcie_ecam_generic.c create mode 100644 include/configs/qemu-arm.h

This sort of pattern for implementing memory-mapped PCI config space accesses appears in U-Boot twice already, and a third user is coming up. So add helper functions to avoid code duplication, similar to how Linux has pci_generic_config_write and pci_generic_config_read.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- drivers/pci/pci-uclass.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ include/pci.h | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 86df141d60..5a24eb6428 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -518,6 +518,64 @@ int pci_auto_config_devices(struct udevice *bus) return sub_bus; }
+int pci_generic_mmap_write_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong value, + enum pci_size_t size) +{ + void *address; + + if (addr_f(bus, bdf, offset, &address) < 0) + return 0; + + switch (size) { + case PCI_SIZE_8: + writeb(value, address); + return 0; + case PCI_SIZE_16: + writew(value, address); + return 0; + case PCI_SIZE_32: + writel(value, address); + return 0; + default: + return -EINVAL; + } +} + +int pci_generic_mmap_read_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong *valuep, + enum pci_size_t size) +{ + void *address; + + if (addr_f(bus, bdf, offset, &address) < 0) { + *valuep = pci_get_ff(size); + return 0; + } + + switch (size) { + case PCI_SIZE_8: + *valuep = readb(address); + return 0; + case PCI_SIZE_16: + *valuep = readw(address); + return 0; + case PCI_SIZE_32: + *valuep = readl(address); + return 0; + default: + return -EINVAL; + } +} + int dm_pci_hose_probe_bus(struct udevice *bus) { int sub_bus; diff --git a/include/pci.h b/include/pci.h index c8ef997d0d..7adc04301c 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1086,6 +1086,57 @@ int pci_read_config32(pci_dev_t pcidev, int offset, u32 *valuep); int pci_read_config16(pci_dev_t pcidev, int offset, u16 *valuep); int pci_read_config8(pci_dev_t pcidev, int offset, u8 *valuep);
+/** + * pci_generic_mmap_write_config() - Generic helper for writing to + * memory-mapped PCI configuration space. + * @bus: Pointer to the PCI bus + * @addr_f: Callback for calculating the config space address + * @bdf: Identifies the PCI device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. The callback function @addr_f is + * responsible for calculating the CPU address of the respective configuration + * space offset. + * + * Return: 0 on success, else -EINVAL + */ +int pci_generic_mmap_write_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong value, + enum pci_size_t size); + +/** + * pci_generic_mmap_read_config() - Generic helper for reading from + * memory-mapped PCI configuration space. + * @bus: Pointer to the PCI bus + * @addr_f: Callback for calculating the config space address + * @bdf: Identifies the PCI device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. The callback function @addr_f is responsible for + * calculating the CPU address of the respective configuration space offset. + * + * Return: 0 on success, else -EINVAL + */ +int pci_generic_mmap_read_config( + struct udevice *bus, + int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp), + pci_dev_t bdf, + uint offset, + ulong *valuep, + enum pci_size_t size); + #ifdef CONFIG_DM_PCI_COMPAT /* Compatibility with old naming */ static inline int pci_write_config_dword(pci_dev_t pcidev, int offset,

Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
This sort of pattern for implementing memory-mapped PCI config space accesses appears in U-Boot twice already, and a third user is coming up. So add helper functions to avoid code duplication, similar to how Linux has pci_generic_config_write and pci_generic_config_read.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
drivers/pci/pci-uclass.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ include/pci.h | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+)
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 86df141d60..5a24eb6428 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -518,6 +518,64 @@ int pci_auto_config_devices(struct udevice *bus) return sub_bus; }
+int pci_generic_mmap_write_config(
struct udevice *bus,
int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp),
pci_dev_t bdf,
uint offset,
ulong value,
enum pci_size_t size)
+{
void *address;
if (addr_f(bus, bdf, offset, &address) < 0)
return 0;
switch (size) {
case PCI_SIZE_8:
writeb(value, address);
return 0;
case PCI_SIZE_16:
writew(value, address);
return 0;
case PCI_SIZE_32:
writel(value, address);
return 0;
default:
return -EINVAL;
}
+}
+int pci_generic_mmap_read_config(
struct udevice *bus,
int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp),
pci_dev_t bdf,
uint offset,
ulong *valuep,
enum pci_size_t size)
+{
void *address;
if (addr_f(bus, bdf, offset, &address) < 0) {
*valuep = pci_get_ff(size);
return 0;
}
switch (size) {
case PCI_SIZE_8:
*valuep = readb(address);
return 0;
case PCI_SIZE_16:
*valuep = readw(address);
return 0;
case PCI_SIZE_32:
*valuep = readl(address);
return 0;
default:
return -EINVAL;
}
+}
int dm_pci_hose_probe_bus(struct udevice *bus) { int sub_bus; diff --git a/include/pci.h b/include/pci.h index c8ef997d0d..7adc04301c 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1086,6 +1086,57 @@ int pci_read_config32(pci_dev_t pcidev, int offset, u32 *valuep); int pci_read_config16(pci_dev_t pcidev, int offset, u16 *valuep); int pci_read_config8(pci_dev_t pcidev, int offset, u8 *valuep);
+/**
- pci_generic_mmap_write_config() - Generic helper for writing to
- memory-mapped PCI configuration space.
nits: suggest adding one blank line here.
- @bus: Pointer to the PCI bus
- @addr_f: Callback for calculating the config space address
- @bdf: Identifies the PCI device to access
- @offset: The offset into the device's configuration space
- @value: The value to write
- @size: Indicates the size of access to perform
- Write the value @value of size @size from offset @offset within the
- configuration space of the device identified by the bus, device & function
- numbers in @bdf on the PCI bus @bus. The callback function @addr_f is
- responsible for calculating the CPU address of the respective configuration
- space offset.
- Return: 0 on success, else -EINVAL
- */
+int pci_generic_mmap_write_config(
struct udevice *bus,
int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp),
pci_dev_t bdf,
uint offset,
ulong value,
enum pci_size_t size);
+/**
- pci_generic_mmap_read_config() - Generic helper for reading from
- memory-mapped PCI configuration space.
nits: suggest adding one blank line here.
- @bus: Pointer to the PCI bus
- @addr_f: Callback for calculating the config space address
- @bdf: Identifies the PCI device to access
- @offset: The offset into the device's configuration space
- @valuep: A pointer at which to store the read value
- @size: Indicates the size of access to perform
- Read a value of size @size from offset @offset within the configuration
- space of the device identified by the bus, device & function numbers in @bdf
- on the PCI bus @bus. The callback function @addr_f is responsible for
- calculating the CPU address of the respective configuration space offset.
- Return: 0 on success, else -EINVAL
- */
+int pci_generic_mmap_read_config(
struct udevice *bus,
int (*addr_f)(struct udevice *bus, pci_dev_t bdf, uint offset, void **addrp),
pci_dev_t bdf,
uint offset,
ulong *valuep,
enum pci_size_t size);
#ifdef CONFIG_DM_PCI_COMPAT /* Compatibility with old naming */ static inline int pci_write_config_dword(pci_dev_t pcidev, int offset, --
Other than above nits:
Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin

On Tue, Sep 19, 2017 at 11:18:03PM +0300, Tuomas Tynkkynen wrote:
This sort of pattern for implementing memory-mapped PCI config space accesses appears in U-Boot twice already, and a third user is coming up. So add helper functions to avoid code duplication, similar to how Linux has pci_generic_config_write and pci_generic_config_read.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot/master, thanks!

Use the new helper function to avoid boilerplate in the driver.
Note that this changes __raw_writel et al. to writel. AFAICT this is no problem because:
- The Linux driver for the same hardware uses the non-__raw variants as well (via pci_generic_config_write()). - This driver seems to be used only on MIPS so far, where the __raw and non-__raw accessors are the same.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- drivers/pci/pcie_xilinx.c | 53 +++++++---------------------------------------- 1 file changed, 7 insertions(+), 46 deletions(-)
diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c index 08e2e93445..d788552fed 100644 --- a/drivers/pci/pcie_xilinx.c +++ b/drivers/pci/pcie_xilinx.c @@ -43,7 +43,7 @@ static bool pcie_xilinx_link_up(struct xilinx_pcie *pcie)
/** * pcie_xilinx_config_address() - Calculate the address of a config access - * @pcie: Pointer to the PCI controller state + * @udev: Pointer to the PCI bus * @bdf: Identifies the PCIe device to access * @offset: The offset into the device's configuration space * @paddress: Pointer to the pointer to write the calculates address to @@ -57,9 +57,10 @@ static bool pcie_xilinx_link_up(struct xilinx_pcie *pcie) * * Return: 0 on success, else -ENODEV */ -static int pcie_xilinx_config_address(struct xilinx_pcie *pcie, pci_dev_t bdf, +static int pcie_xilinx_config_address(struct udevice *udev, pci_dev_t bdf, uint offset, void **paddress) { + struct xilinx_pcie *pcie = dev_get_priv(udev); unsigned int bus = PCI_BUS(bdf); unsigned int dev = PCI_DEV(bdf); unsigned int func = PCI_FUNC(bdf); @@ -103,29 +104,8 @@ static int pcie_xilinx_read_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong *valuep, enum pci_size_t size) { - struct xilinx_pcie *pcie = dev_get_priv(bus); - void *address; - int err; - - err = pcie_xilinx_config_address(pcie, bdf, offset, &address); - if (err < 0) { - *valuep = pci_get_ff(size); - return 0; - } - - switch (size) { - case PCI_SIZE_8: - *valuep = __raw_readb(address); - return 0; - case PCI_SIZE_16: - *valuep = __raw_readw(address); - return 0; - case PCI_SIZE_32: - *valuep = __raw_readl(address); - return 0; - default: - return -EINVAL; - } + return pci_generic_mmap_read_config(bus, pcie_xilinx_config_address, + bdf, offset, valuep, size); }
/** @@ -146,27 +126,8 @@ static int pcie_xilinx_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, enum pci_size_t size) { - struct xilinx_pcie *pcie = dev_get_priv(bus); - void *address; - int err; - - err = pcie_xilinx_config_address(pcie, bdf, offset, &address); - if (err < 0) - return 0; - - switch (size) { - case PCI_SIZE_8: - __raw_writeb(value, address); - return 0; - case PCI_SIZE_16: - __raw_writew(value, address); - return 0; - case PCI_SIZE_32: - __raw_writel(value, address); - return 0; - default: - return -EINVAL; - } + return pci_generic_mmap_write_config(bus, pcie_xilinx_config_address, + bdf, offset, value, size); }
/**

On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
Use the new helper function to avoid boilerplate in the driver.
Note that this changes __raw_writel et al. to writel. AFAICT this is no problem because:
- The Linux driver for the same hardware uses the non-__raw variants as well (via pci_generic_config_write()).
- This driver seems to be used only on MIPS so far, where the __raw and non-__raw accessors are the same.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
drivers/pci/pcie_xilinx.c | 53 +++++++---------------------------------------- 1 file changed, 7 insertions(+), 46 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On Tue, Sep 19, 2017 at 11:18:04PM +0300, Tuomas Tynkkynen wrote:
Use the new helper function to avoid boilerplate in the driver.
Note that this changes __raw_writel et al. to writel. AFAICT this is no problem because:
- The Linux driver for the same hardware uses the non-__raw variants as well (via pci_generic_config_write()).
- This driver seems to be used only on MIPS so far, where the __raw and non-__raw accessors are the same.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot/master, thanks!

Use the new helpers to avoid boilerplate in the driver.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- drivers/pci/pcie_layerscape.c | 68 +++++++++++-------------------------------- 1 file changed, 17 insertions(+), 51 deletions(-)
diff --git a/drivers/pci/pcie_layerscape.c b/drivers/pci/pcie_layerscape.c index 610f85c4e8..0cb7f6d564 100644 --- a/drivers/pci/pcie_layerscape.c +++ b/drivers/pci/pcie_layerscape.c @@ -241,14 +241,19 @@ static int ls_pcie_addr_valid(struct ls_pcie *pcie, pci_dev_t bdf) return 0; }
-void *ls_pcie_conf_address(struct ls_pcie *pcie, pci_dev_t bdf, - int offset) +int ls_pcie_conf_address(struct udevice *bus, pci_dev_t bdf, + uint offset, void **paddress) { - struct udevice *bus = pcie->bus; + struct ls_pcie *pcie = dev_get_priv(bus); u32 busdev;
- if (PCI_BUS(bdf) == bus->seq) - return pcie->dbi + offset; + if (ls_pcie_addr_valid(pcie, bdf)) + return -EINVAL; + + if (PCI_BUS(bdf) == bus->seq) { + *paddress = pcie->dbi + offset; + return 0; + }
busdev = PCIE_ATU_BUS(PCI_BUS(bdf)) | PCIE_ATU_DEV(PCI_DEV(bdf)) | @@ -256,67 +261,28 @@ void *ls_pcie_conf_address(struct ls_pcie *pcie, pci_dev_t bdf,
if (PCI_BUS(bdf) == bus->seq + 1) { ls_pcie_cfg0_set_busdev(pcie, busdev); - return pcie->cfg0 + offset; + *paddress = pcie->cfg0 + offset; } else { ls_pcie_cfg1_set_busdev(pcie, busdev); - return pcie->cfg1 + offset; + *paddress = pcie->cfg1 + offset; } + return 0; }
static int ls_pcie_read_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong *valuep, enum pci_size_t size) { - struct ls_pcie *pcie = dev_get_priv(bus); - void *address; - - if (ls_pcie_addr_valid(pcie, bdf)) { - *valuep = pci_get_ff(size); - return 0; - } - - address = ls_pcie_conf_address(pcie, bdf, offset); - - switch (size) { - case PCI_SIZE_8: - *valuep = readb(address); - return 0; - case PCI_SIZE_16: - *valuep = readw(address); - return 0; - case PCI_SIZE_32: - *valuep = readl(address); - return 0; - default: - return -EINVAL; - } + return pci_generic_mmap_read_config(bus, ls_pcie_conf_address, + bdf, offset, valuep, size); }
static int ls_pcie_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, enum pci_size_t size) { - struct ls_pcie *pcie = dev_get_priv(bus); - void *address; - - if (ls_pcie_addr_valid(pcie, bdf)) - return 0; - - address = ls_pcie_conf_address(pcie, bdf, offset); - - switch (size) { - case PCI_SIZE_8: - writeb(value, address); - return 0; - case PCI_SIZE_16: - writew(value, address); - return 0; - case PCI_SIZE_32: - writel(value, address); - return 0; - default: - return -EINVAL; - } + return pci_generic_mmap_write_config(bus, ls_pcie_conf_address, + bdf, offset, value, size); }
/* Clear multi-function bit */

On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
Use the new helpers to avoid boilerplate in the driver.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
drivers/pci/pcie_layerscape.c | 68 +++++++++++-------------------------------- 1 file changed, 17 insertions(+), 51 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

On Tue, Sep 19, 2017 at 11:18:05PM +0300, Tuomas Tynkkynen wrote:
Use the new helpers to avoid boilerplate in the driver.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot/master, thanks!

QEMU emulates such a device with '-machine virt,highmem=off' on ARM. The 'highmem=off' part is required for things to work as the PCI code in U-Boot doesn't seem to support 64-bit BARs.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- v2: - no 'default n' - remove unnecessary non-DM struct field (inherited from the Xilinx driver) - fix doc comment problems (inherited from the Xilinx driver) - use the new generic memory mapped config space helpers --- drivers/pci/Kconfig | 8 +++ drivers/pci/Makefile | 1 + drivers/pci/pcie_ecam_generic.c | 143 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 drivers/pci/pcie_ecam_generic.c
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e2a1c0a409..648dff7543 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -33,6 +33,14 @@ config PCI_PNP help Enable PCI memory and I/O space resource allocation and assignment.
+config PCIE_ECAM_GENERIC + bool "Generic ECAM-based PCI host controller support" + default n + depends on DM_PCI + help + Say Y here if you want to enable support for generic ECAM-based + PCIe host controllers, such as the one emulated by QEMU. + config PCIE_DW_MVEBU bool "Enable Armada-8K PCIe driver (DesignWare core)" default n diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index ad44e83996..5eb12efbf5 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_PCI) += pci.o pci_auto_old.o endif obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o
+obj-$(CONFIG_PCIE_ECAM_GENERIC) += pcie_ecam_generic.o obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o diff --git a/drivers/pci/pcie_ecam_generic.c b/drivers/pci/pcie_ecam_generic.c new file mode 100644 index 0000000000..2758f90de1 --- /dev/null +++ b/drivers/pci/pcie_ecam_generic.c @@ -0,0 +1,143 @@ +/* + * Generic PCIE host provided by e.g. QEMU + * + * Heavily based on drivers/pci/pcie_xilinx.c + * + * Copyright (C) 2016 Imagination Technologies + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> + +#include <asm/io.h> + +/** + * struct generic_ecam_pcie - generic_ecam PCIe controller state + * @cfg_base: The base address of memory mapped configuration space + */ +struct generic_ecam_pcie { + void *cfg_base; +}; + +/** + * pci_generic_ecam_conf_address() - Calculate the address of a config access + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @paddress: Pointer to the pointer to write the calculates address to + * + * Calculates the address that should be accessed to perform a PCIe + * configuration space access for a given device identified by the PCIe + * controller device @pcie and the bus, device & function numbers in @bdf. If + * access to the device is not valid then the function will return an error + * code. Otherwise the address to access will be written to the pointer pointed + * to by @paddress. + */ +static int pci_generic_ecam_conf_address(struct udevice *bus, pci_dev_t bdf, + uint offset, void **paddress) +{ + struct generic_ecam_pcie *pcie = dev_get_priv(bus); + void *addr; + + addr = pcie->cfg_base; + addr += PCI_BUS(bdf) << 20; + addr += PCI_DEV(bdf) << 15; + addr += PCI_FUNC(bdf) << 12; + addr += offset; + *paddress = addr; + + return 0; +} + +/** + * pci_generic_ecam_read_config() - Read from configuration space + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. + */ +static int pci_generic_ecam_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + return pci_generic_mmap_read_config(bus, pci_generic_ecam_conf_address, + bdf, offset, valuep, size); +} + +/** + * pci_generic_ecam_write_config() - Write to configuration space + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. + */ +static int pci_generic_ecam_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + return pci_generic_mmap_write_config(bus, pci_generic_ecam_conf_address, + bdf, offset, value, size); +} + +/** + * pci_generic_ecam_ofdata_to_platdata() - Translate from DT to device state + * @dev: A pointer to the device being operated on + * + * Translate relevant data from the device tree pertaining to device @dev into + * state that the driver will later make use of. This state is stored in the + * device's private data structure. + * + * Return: 0 on success, else -EINVAL + */ +static int pci_generic_ecam_ofdata_to_platdata(struct udevice *dev) +{ + struct generic_ecam_pcie *pcie = dev_get_priv(dev); + struct fdt_resource reg_res; + DECLARE_GLOBAL_DATA_PTR; + int err; + + err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", + 0, ®_res); + if (err < 0) { + error(""reg" resource not found\n"); + return err; + } + + pcie->cfg_base = map_physmem(reg_res.start, + fdt_resource_size(®_res), + MAP_NOCACHE); + + return 0; +} + +static const struct dm_pci_ops pci_generic_ecam_ops = { + .read_config = pci_generic_ecam_read_config, + .write_config = pci_generic_ecam_write_config, +}; + +static const struct udevice_id pci_generic_ecam_ids[] = { + { .compatible = "pci-host-ecam-generic" }, + { } +}; + +U_BOOT_DRIVER(pci_generic_ecam) = { + .name = "pci_generic_ecam", + .id = UCLASS_PCI, + .of_match = pci_generic_ecam_ids, + .ops = &pci_generic_ecam_ops, + .ofdata_to_platdata = pci_generic_ecam_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct generic_ecam_pcie), +};

Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
QEMU emulates such a device with '-machine virt,highmem=off' on ARM. The 'highmem=off' part is required for things to work as the PCI code in U-Boot doesn't seem to support 64-bit BARs.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
v2:
- no 'default n'
- remove unnecessary non-DM struct field (inherited from the Xilinx driver)
- fix doc comment problems (inherited from the Xilinx driver)
- use the new generic memory mapped config space helpers
drivers/pci/Kconfig | 8 +++ drivers/pci/Makefile | 1 + drivers/pci/pcie_ecam_generic.c | 143 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 drivers/pci/pcie_ecam_generic.c
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e2a1c0a409..648dff7543 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -33,6 +33,14 @@ config PCI_PNP help Enable PCI memory and I/O space resource allocation and assignment.
+config PCIE_ECAM_GENERIC
bool "Generic ECAM-based PCI host controller support"
default n
Still 'default n' here?
depends on DM_PCI
help
Say Y here if you want to enable support for generic ECAM-based
PCIe host controllers, such as the one emulated by QEMU.
config PCIE_DW_MVEBU bool "Enable Armada-8K PCIe driver (DesignWare core)" default n diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index ad44e83996..5eb12efbf5 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_PCI) += pci.o pci_auto_old.o endif obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o
+obj-$(CONFIG_PCIE_ECAM_GENERIC) += pcie_ecam_generic.o obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o diff --git a/drivers/pci/pcie_ecam_generic.c b/drivers/pci/pcie_ecam_generic.c new file mode 100644 index 0000000000..2758f90de1 --- /dev/null +++ b/drivers/pci/pcie_ecam_generic.c @@ -0,0 +1,143 @@ +/*
- Generic PCIE host provided by e.g. QEMU
- Heavily based on drivers/pci/pcie_xilinx.c
- Copyright (C) 2016 Imagination Technologies
- SPDX-License-Identifier: GPL-2.0
- */
+#include <common.h> +#include <dm.h> +#include <pci.h>
+#include <asm/io.h>
+/**
- struct generic_ecam_pcie - generic_ecam PCIe controller state
- @cfg_base: The base address of memory mapped configuration space
- */
+struct generic_ecam_pcie {
void *cfg_base;
+};
+/**
- pci_generic_ecam_conf_address() - Calculate the address of a config access
nits: please add one blank line here
- @bus: Pointer to the PCI bus
- @bdf: Identifies the PCIe device to access
- @offset: The offset into the device's configuration space
- @paddress: Pointer to the pointer to write the calculates address to
- Calculates the address that should be accessed to perform a PCIe
- configuration space access for a given device identified by the PCIe
- controller device @pcie and the bus, device & function numbers in @bdf. If
- access to the device is not valid then the function will return an error
- code. Otherwise the address to access will be written to the pointer pointed
- to by @paddress.
- */
+static int pci_generic_ecam_conf_address(struct udevice *bus, pci_dev_t bdf,
uint offset, void **paddress)
+{
struct generic_ecam_pcie *pcie = dev_get_priv(bus);
void *addr;
addr = pcie->cfg_base;
addr += PCI_BUS(bdf) << 20;
addr += PCI_DEV(bdf) << 15;
addr += PCI_FUNC(bdf) << 12;
addr += offset;
*paddress = addr;
return 0;
+}
+/**
- pci_generic_ecam_read_config() - Read from configuration space
nits: please add one blank line here
- @bus: Pointer to the PCI bus
- @bdf: Identifies the PCIe device to access
- @offset: The offset into the device's configuration space
- @valuep: A pointer at which to store the read value
- @size: Indicates the size of access to perform
- Read a value of size @size from offset @offset within the configuration
- space of the device identified by the bus, device & function numbers in @bdf
- on the PCI bus @bus.
- */
+static int pci_generic_ecam_read_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
+{
return pci_generic_mmap_read_config(bus, pci_generic_ecam_conf_address,
bdf, offset, valuep, size);
+}
+/**
- pci_generic_ecam_write_config() - Write to configuration space
nits: please add one blank line here
- @bus: Pointer to the PCI bus
- @bdf: Identifies the PCIe device to access
- @offset: The offset into the device's configuration space
- @value: The value to write
- @size: Indicates the size of access to perform
- Write the value @value of size @size from offset @offset within the
- configuration space of the device identified by the bus, device & function
- numbers in @bdf on the PCI bus @bus.
- */
+static int pci_generic_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
uint offset, ulong value,
enum pci_size_t size)
+{
return pci_generic_mmap_write_config(bus, pci_generic_ecam_conf_address,
bdf, offset, value, size);
+}
+/**
- pci_generic_ecam_ofdata_to_platdata() - Translate from DT to device state
nits: please add one blank line here
- @dev: A pointer to the device being operated on
- Translate relevant data from the device tree pertaining to device @dev into
- state that the driver will later make use of. This state is stored in the
- device's private data structure.
- Return: 0 on success, else -EINVAL
- */
+static int pci_generic_ecam_ofdata_to_platdata(struct udevice *dev) +{
struct generic_ecam_pcie *pcie = dev_get_priv(dev);
struct fdt_resource reg_res;
DECLARE_GLOBAL_DATA_PTR;
int err;
err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
0, ®_res);
if (err < 0) {
error("\"reg\" resource not found\n");
return err;
}
pcie->cfg_base = map_physmem(reg_res.start,
fdt_resource_size(®_res),
MAP_NOCACHE);
return 0;
+}
+static const struct dm_pci_ops pci_generic_ecam_ops = {
.read_config = pci_generic_ecam_read_config,
.write_config = pci_generic_ecam_write_config,
+};
+static const struct udevice_id pci_generic_ecam_ids[] = {
{ .compatible = "pci-host-ecam-generic" },
{ }
+};
+U_BOOT_DRIVER(pci_generic_ecam) = {
.name = "pci_generic_ecam",
.id = UCLASS_PCI,
.of_match = pci_generic_ecam_ids,
.ops = &pci_generic_ecam_ops,
.ofdata_to_platdata = pci_generic_ecam_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct generic_ecam_pcie),
+};
Other than above nits: Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin

On Tue, Sep 19, 2017 at 11:18:06PM +0300, Tuomas Tynkkynen wrote:
QEMU emulates such a device with '-machine virt,highmem=off' on ARM. The 'highmem=off' part is required for things to work as the PCI code in U-Boot doesn't seem to support 64-bit BARs.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi Reviewed-by: Bin Meng bmeng.cn@gmail.com
Applied to u-boot/master, thanks!

This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0 - To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user - To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci - To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- v2: - enable CONFIG_NVME - alphasort correctly - remove unnecessary gd declaration - move board under board/emulation - add MAINTAINERS --- arch/arm/Kconfig | 10 +++++++ arch/arm/mach-qemu/Kconfig | 12 ++++++++ board/emulation/qemu-arm/MAINTAINERS | 6 ++++ board/emulation/qemu-arm/Makefile | 5 ++++ board/emulation/qemu-arm/qemu-arm.c | 33 ++++++++++++++++++++ configs/qemu_arm_defconfig | 28 +++++++++++++++++ include/configs/qemu-arm.h | 58 ++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+) create mode 100644 arch/arm/mach-qemu/Kconfig create mode 100644 board/emulation/qemu-arm/MAINTAINERS create mode 100644 board/emulation/qemu-arm/Makefile create mode 100644 board/emulation/qemu-arm/qemu-arm.c create mode 100644 configs/qemu_arm_defconfig create mode 100644 include/configs/qemu-arm.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 53eae8953e..1de5be7a72 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -630,6 +630,14 @@ config ARCH_MX5 select CPU_V7 select BOARD_EARLY_INIT_F
+config ARCH_QEMU + bool "QEMU Virtual Platform" + select CPU_V7 + select ARCH_SUPPORT_PSCI + select DM + select DM_SERIAL + select OF_CONTROL + config ARCH_RMOBILE bool "Renesas ARM SoCs" select DM @@ -1142,6 +1150,8 @@ source "arch/arm/mach-rmobile/Kconfig"
source "arch/arm/mach-meson/Kconfig"
+source "arch/arm/mach-qemu/Kconfig" + source "arch/arm/mach-rockchip/Kconfig"
source "arch/arm/mach-s5pc1xx/Kconfig" diff --git a/arch/arm/mach-qemu/Kconfig b/arch/arm/mach-qemu/Kconfig new file mode 100644 index 0000000000..3500b56cb0 --- /dev/null +++ b/arch/arm/mach-qemu/Kconfig @@ -0,0 +1,12 @@ +if ARCH_QEMU + +config SYS_VENDOR + default "emulation" + +config SYS_BOARD + default "qemu-arm" + +config SYS_CONFIG_NAME + default "qemu-arm" + +endif diff --git a/board/emulation/qemu-arm/MAINTAINERS b/board/emulation/qemu-arm/MAINTAINERS new file mode 100644 index 0000000000..a803061ff4 --- /dev/null +++ b/board/emulation/qemu-arm/MAINTAINERS @@ -0,0 +1,6 @@ +QEMU ARM 'VIRT' BOARD +M: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi +S: Maintained +F: board/emulation/qemu-arm/ +F: include/configs/qemu-arm.h +F: configs/qemu_arm_defconfig diff --git a/board/emulation/qemu-arm/Makefile b/board/emulation/qemu-arm/Makefile new file mode 100644 index 0000000000..716a6e9c28 --- /dev/null +++ b/board/emulation/qemu-arm/Makefile @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += qemu-arm.o diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c new file mode 100644 index 0000000000..e29ba4630f --- /dev/null +++ b/board/emulation/qemu-arm/qemu-arm.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Tuomas Tynkkynen + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <fdtdec.h> + +int board_init(void) +{ + return 0; +} + +int dram_init(void) +{ + if (fdtdec_setup_memory_size() != 0) + return -EINVAL; + + return 0; +} + +int dram_init_banksize(void) +{ + fdtdec_setup_memory_banksize(); + + return 0; +} + +void *board_fdt_blob_setup(void) +{ + /* QEMU loads a generated DTB for us at the start of RAM. */ + return (void *)CONFIG_SYS_SDRAM_BASE; +} diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig new file mode 100644 index 0000000000..2a8594d472 --- /dev/null +++ b/configs/qemu_arm_defconfig @@ -0,0 +1,28 @@ +CONFIG_ARM=y +CONFIG_ARM_SMCCC=y +CONFIG_ARCH_QEMU=y +CONFIG_AHCI=y +CONFIG_DISTRO_DEFAULTS=y +# CONFIG_DISPLAY_CPUINFO is not set +# CONFIG_DISPLAY_BOARDINFO is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_PCI=y +CONFIG_CMD_USB=y +CONFIG_OF_BOARD=y +CONFIG_AHCI_PCI=y +CONFIG_BLK=y +# CONFIG_MMC is not set +CONFIG_DM_ETH=y +CONFIG_E1000=y +CONFIG_NVME=y +CONFIG_PCI=y +CONFIG_DM_PCI=y +CONFIG_PCIE_ECAM_GENERIC=y +CONFIG_SCSI=y +CONFIG_DM_SCSI=y +CONFIG_SYSRESET=y +CONFIG_SYSRESET_PSCI=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_PCI=y diff --git a/include/configs/qemu-arm.h b/include/configs/qemu-arm.h new file mode 100644 index 0000000000..4376a24787 --- /dev/null +++ b/include/configs/qemu-arm.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Tuomas Tynkkynen + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include <linux/sizes.h> + +/* Physical memory map */ +#define CONFIG_SYS_TEXT_BASE 0x00000000 + +#define CONFIG_NR_DRAM_BANKS 1 +#define CONFIG_SYS_SDRAM_BASE 0x40000000 + +/* The DTB generated by QEMU is placed at start of RAM, stay away from there */ +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_2M) +#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_2M) +#define CONFIG_SYS_MALLOC_LEN SZ_16M + +/* QEMU's PL011 serial port is detected via FDT using the device model */ +#define CONFIG_PL01X_SERIAL + +/* QEMU implements a 62.5MHz architected timer */ +/* FIXME: can we rely on CNTFREQ instead of hardcoding this fact here? */ +#define CONFIG_SYS_ARCH_TIMER +#define CONFIG_SYS_HZ 1000 +#define CONFIG_SYS_HZ_CLOCK 62500000 + +/* For block devices, QEMU emulates an ICH9 AHCI controller over PCI */ +#define CONFIG_SYS_SCSI_MAX_SCSI_ID 6 +#define CONFIG_SCSI_AHCI +#define CONFIG_LIBATA + +/* Environment options */ +#define CONFIG_ENV_SIZE SZ_64K + +#include <config_distro_defaults.h> + +#define BOOT_TARGET_DEVICES(func) \ + func(SCSI, scsi, 0) + +#include <config_distro_bootcmd.h> + +#define CONFIG_PREBOOT "pci enum" +#define CONFIG_EXTRA_ENV_SETTINGS \ + "fdt_high=0xffffffff\0" \ + "initrd_high=0xffffffff\0" \ + "fdt_addr=0x40000000\0" \ + "scriptaddr=0x40200000\0" \ + "pxefile_addr_r=0x40300000\0" \ + "kernel_addr_r=0x40400000\0" \ + "ramdisk_addr_r=0x44000000\0" \ + BOOTENV + +#endif /* __CONFIG_H */

Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
With this parameter I got:
Warning: requested NIC (anonymous, model e1000) was not created (not supported by this machine?)
I am using QEMU v2.5.0. It's better to mention the minimum required QEMU version in the commit message as well.
- To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci
- To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
v2:
- enable CONFIG_NVME
- alphasort correctly
- remove unnecessary gd declaration
- move board under board/emulation
- add MAINTAINERS
arch/arm/Kconfig | 10 +++++++ arch/arm/mach-qemu/Kconfig | 12 ++++++++ board/emulation/qemu-arm/MAINTAINERS | 6 ++++ board/emulation/qemu-arm/Makefile | 5 ++++ board/emulation/qemu-arm/qemu-arm.c | 33 ++++++++++++++++++++ configs/qemu_arm_defconfig | 28 +++++++++++++++++ include/configs/qemu-arm.h | 58 ++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+) create mode 100644 arch/arm/mach-qemu/Kconfig create mode 100644 board/emulation/qemu-arm/MAINTAINERS create mode 100644 board/emulation/qemu-arm/Makefile create mode 100644 board/emulation/qemu-arm/qemu-arm.c create mode 100644 configs/qemu_arm_defconfig create mode 100644 include/configs/qemu-arm.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 53eae8953e..1de5be7a72 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -630,6 +630,14 @@ config ARCH_MX5 select CPU_V7 select BOARD_EARLY_INIT_F
+config ARCH_QEMU
bool "QEMU Virtual Platform"
select CPU_V7
select ARCH_SUPPORT_PSCI
select DM
select DM_SERIAL
select OF_CONTROL
config ARCH_RMOBILE bool "Renesas ARM SoCs" select DM @@ -1142,6 +1150,8 @@ source "arch/arm/mach-rmobile/Kconfig"
source "arch/arm/mach-meson/Kconfig"
+source "arch/arm/mach-qemu/Kconfig"
source "arch/arm/mach-rockchip/Kconfig"
source "arch/arm/mach-s5pc1xx/Kconfig" diff --git a/arch/arm/mach-qemu/Kconfig b/arch/arm/mach-qemu/Kconfig new file mode 100644 index 0000000000..3500b56cb0 --- /dev/null +++ b/arch/arm/mach-qemu/Kconfig @@ -0,0 +1,12 @@ +if ARCH_QEMU
+config SYS_VENDOR
default "emulation"
+config SYS_BOARD
default "qemu-arm"
+config SYS_CONFIG_NAME
default "qemu-arm"
+endif diff --git a/board/emulation/qemu-arm/MAINTAINERS b/board/emulation/qemu-arm/MAINTAINERS new file mode 100644 index 0000000000..a803061ff4 --- /dev/null +++ b/board/emulation/qemu-arm/MAINTAINERS @@ -0,0 +1,6 @@ +QEMU ARM 'VIRT' BOARD +M: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi +S: Maintained +F: board/emulation/qemu-arm/ +F: include/configs/qemu-arm.h +F: configs/qemu_arm_defconfig diff --git a/board/emulation/qemu-arm/Makefile b/board/emulation/qemu-arm/Makefile new file mode 100644 index 0000000000..716a6e9c28 --- /dev/null +++ b/board/emulation/qemu-arm/Makefile @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +#
+obj-y += qemu-arm.o diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c new file mode 100644 index 0000000000..e29ba4630f --- /dev/null +++ b/board/emulation/qemu-arm/qemu-arm.c @@ -0,0 +1,33 @@ +/*
- Copyright (c) 2017 Tuomas Tynkkynen
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <fdtdec.h>
+int board_init(void) +{
return 0;
+}
+int dram_init(void) +{
if (fdtdec_setup_memory_size() != 0)
return -EINVAL;
return 0;
+}
+int dram_init_banksize(void) +{
fdtdec_setup_memory_banksize();
return 0;
+}
+void *board_fdt_blob_setup(void) +{
/* QEMU loads a generated DTB for us at the start of RAM. */
nits: remove the ending period
return (void *)CONFIG_SYS_SDRAM_BASE;
+} diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig
Can we use 'qemu-arm_defconfig", to be in consistent with the board name?
new file mode 100644 index 0000000000..2a8594d472 --- /dev/null +++ b/configs/qemu_arm_defconfig @@ -0,0 +1,28 @@ +CONFIG_ARM=y +CONFIG_ARM_SMCCC=y +CONFIG_ARCH_QEMU=y +CONFIG_AHCI=y +CONFIG_DISTRO_DEFAULTS=y +# CONFIG_DISPLAY_CPUINFO is not set +# CONFIG_DISPLAY_BOARDINFO is not set +# CONFIG_CMD_IMLS is not set +CONFIG_CMD_PCI=y +CONFIG_CMD_USB=y +CONFIG_OF_BOARD=y +CONFIG_AHCI_PCI=y +CONFIG_BLK=y +# CONFIG_MMC is not set +CONFIG_DM_ETH=y +CONFIG_E1000=y +CONFIG_NVME=y +CONFIG_PCI=y +CONFIG_DM_PCI=y +CONFIG_PCIE_ECAM_GENERIC=y +CONFIG_SCSI=y +CONFIG_DM_SCSI=y +CONFIG_SYSRESET=y +CONFIG_SYSRESET_PSCI=y +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_PCI=y diff --git a/include/configs/qemu-arm.h b/include/configs/qemu-arm.h new file mode 100644 index 0000000000..4376a24787 --- /dev/null +++ b/include/configs/qemu-arm.h @@ -0,0 +1,58 @@ +/*
- Copyright (c) 2017 Tuomas Tynkkynen
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __CONFIG_H +#define __CONFIG_H
+#include <linux/sizes.h>
+/* Physical memory map */ +#define CONFIG_SYS_TEXT_BASE 0x00000000
+#define CONFIG_NR_DRAM_BANKS 1 +#define CONFIG_SYS_SDRAM_BASE 0x40000000
+/* The DTB generated by QEMU is placed at start of RAM, stay away from there */ +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_2M) +#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_2M) +#define CONFIG_SYS_MALLOC_LEN SZ_16M
+/* QEMU's PL011 serial port is detected via FDT using the device model */ +#define CONFIG_PL01X_SERIAL
+/* QEMU implements a 62.5MHz architected timer */ +/* FIXME: can we rely on CNTFREQ instead of hardcoding this fact here? */ +#define CONFIG_SYS_ARCH_TIMER +#define CONFIG_SYS_HZ 1000 +#define CONFIG_SYS_HZ_CLOCK 62500000
+/* For block devices, QEMU emulates an ICH9 AHCI controller over PCI */ +#define CONFIG_SYS_SCSI_MAX_SCSI_ID 6 +#define CONFIG_SCSI_AHCI +#define CONFIG_LIBATA
+/* Environment options */ +#define CONFIG_ENV_SIZE SZ_64K
+#include <config_distro_defaults.h>
+#define BOOT_TARGET_DEVICES(func) \
func(SCSI, scsi, 0)
+#include <config_distro_bootcmd.h>
+#define CONFIG_PREBOOT "pci enum" +#define CONFIG_EXTRA_ENV_SETTINGS \
"fdt_high=0xffffffff\0" \
"initrd_high=0xffffffff\0" \
"fdt_addr=0x40000000\0" \
"scriptaddr=0x40200000\0" \
"pxefile_addr_r=0x40300000\0" \
"kernel_addr_r=0x40400000\0" \
"ramdisk_addr_r=0x44000000\0" \
BOOTENV
+#endif /* __CONFIG_H */
Regards, Bin

Hi Bin,
On 09/20/2017 08:02 AM, Bin Meng wrote:
Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
With this parameter I got:
Warning: requested NIC (anonymous, model e1000) was not created (not supported by this machine?)
I am using QEMU v2.5.0. It's better to mention the minimum required QEMU version in the commit message as well.
I primarily tested with 2.9.0, but I found out that it will work in 2.5.0 as well by changing the syntax to -netdev user,id=net0 -device e1000,netdev=net0 I will update the commit message accordingly.
- To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci
- To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
v2:
- enable CONFIG_NVME
- alphasort correctly
- remove unnecessary gd declaration
- move board under board/emulation
- add MAINTAINERS
arch/arm/Kconfig | 10 +++++++ arch/arm/mach-qemu/Kconfig | 12 ++++++++ board/emulation/qemu-arm/MAINTAINERS | 6 ++++ board/emulation/qemu-arm/Makefile | 5 ++++ board/emulation/qemu-arm/qemu-arm.c | 33 ++++++++++++++++++++ configs/qemu_arm_defconfig | 28 +++++++++++++++++ include/configs/qemu-arm.h | 58 ++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+) create mode 100644 arch/arm/mach-qemu/Kconfig create mode 100644 board/emulation/qemu-arm/MAINTAINERS create mode 100644 board/emulation/qemu-arm/Makefile create mode 100644 board/emulation/qemu-arm/qemu-arm.c create mode 100644 configs/qemu_arm_defconfig create mode 100644 include/configs/qemu-arm.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 53eae8953e..1de5be7a72 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -630,6 +630,14 @@ config ARCH_MX5 select CPU_V7 select BOARD_EARLY_INIT_F
+config ARCH_QEMU
bool "QEMU Virtual Platform"
select CPU_V7
select ARCH_SUPPORT_PSCI
select DM
select DM_SERIAL
select OF_CONTROL
- config ARCH_RMOBILE bool "Renesas ARM SoCs" select DM
@@ -1142,6 +1150,8 @@ source "arch/arm/mach-rmobile/Kconfig"
source "arch/arm/mach-meson/Kconfig"
+source "arch/arm/mach-qemu/Kconfig"
source "arch/arm/mach-rockchip/Kconfig"
source "arch/arm/mach-s5pc1xx/Kconfig"
diff --git a/arch/arm/mach-qemu/Kconfig b/arch/arm/mach-qemu/Kconfig new file mode 100644 index 0000000000..3500b56cb0 --- /dev/null +++ b/arch/arm/mach-qemu/Kconfig @@ -0,0 +1,12 @@ +if ARCH_QEMU
+config SYS_VENDOR
default "emulation"
+config SYS_BOARD
default "qemu-arm"
+config SYS_CONFIG_NAME
default "qemu-arm"
+endif diff --git a/board/emulation/qemu-arm/MAINTAINERS b/board/emulation/qemu-arm/MAINTAINERS new file mode 100644 index 0000000000..a803061ff4 --- /dev/null +++ b/board/emulation/qemu-arm/MAINTAINERS @@ -0,0 +1,6 @@ +QEMU ARM 'VIRT' BOARD +M: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi +S: Maintained +F: board/emulation/qemu-arm/ +F: include/configs/qemu-arm.h +F: configs/qemu_arm_defconfig diff --git a/board/emulation/qemu-arm/Makefile b/board/emulation/qemu-arm/Makefile new file mode 100644 index 0000000000..716a6e9c28 --- /dev/null +++ b/board/emulation/qemu-arm/Makefile @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +#
+obj-y += qemu-arm.o diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c new file mode 100644 index 0000000000..e29ba4630f --- /dev/null +++ b/board/emulation/qemu-arm/qemu-arm.c @@ -0,0 +1,33 @@ +/*
- Copyright (c) 2017 Tuomas Tynkkynen
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <fdtdec.h>
+int board_init(void) +{
return 0;
+}
+int dram_init(void) +{
if (fdtdec_setup_memory_size() != 0)
return -EINVAL;
return 0;
+}
+int dram_init_banksize(void) +{
fdtdec_setup_memory_banksize();
return 0;
+}
+void *board_fdt_blob_setup(void) +{
/* QEMU loads a generated DTB for us at the start of RAM. */
nits: remove the ending period
return (void *)CONFIG_SYS_SDRAM_BASE;
+} diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig
Can we use 'qemu-arm_defconfig", to be in consistent with the board name?
Sure.

Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci
-device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
With this parameter I got:
Warning: requested NIC (anonymous, model e1000) was not created (not supported by this machine?)
I am using QEMU v2.5.0. It's better to mention the minimum required QEMU version in the commit message as well.
I primarily tested with 2.9.0, but I found out that it will work in 2.5.0 as well by changing the syntax to -netdev user,id=net0 -device e1000,netdev=net0 I will update the commit message accordingly.
I wonder how hard it would be to support virtio interfaces for network/storage, that would provide a fast/standard interface.
Peter

On Tue, Sep 19, 2017 at 11:18:07PM +0300, Tuomas Tynkkynen wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
- To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci
- To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
Applied to u-boot/master, thanks!

Hi Tom,
On Sat, Oct 7, 2017 at 9:08 PM, Tom Rini trini@konsulko.com wrote:
On Tue, Sep 19, 2017 at 11:18:07PM +0300, Tuomas Tynkkynen wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
- To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci
- To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
Applied to u-boot/master, thanks!
I expect Tuomas will send a new version with commit message revised. See below:
I primarily tested with 2.9.0, but I found out that it will work in 2.5.0 as well by changing the syntax to -netdev user,id=net0 -device e1000,netdev=net0 I will update the commit message accordingly.
Regards, Bin

On Sat, Oct 07, 2017 at 09:27:21PM +0800, Bin Meng wrote:
Hi Tom,
On Sat, Oct 7, 2017 at 9:08 PM, Tom Rini trini@konsulko.com wrote:
On Tue, Sep 19, 2017 at 11:18:07PM +0300, Tuomas Tynkkynen wrote:
This board builds an U-Boot binary that is bootable with QEMU's 'virt' machine on ARM. The minimal QEMU command line is:
qemu-system-arm -machine virt,highmem=off -bios u-boot.bin
(Note that the 'highmem=off' parameter to the 'virt' machine is required for PCI to work in U-Boot.) This command line enables the following: - u-boot.bin loaded and executing in the emulated flash at address 0x0 - A generated device tree blob placed at the start of RAM - A freely configurable amount of RAM, described by the DTB - A PL011 serial port, discoverable via the DTB - An ARMv7 architected timer - PSCI for rebooting the system - A generic ECAM-based PCI host controller, discoverable via the DTB
Additionally, QEMU allows plugging a bunch of useful peripherals to the PCI bus. The following ones are supported by both U-Boot and Linux:
- To add a Serial ATA disk via an Intel ICH9 AHCI controller, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device ich9-ahci,id=ahci -device ide-drive,drive=mydisk,bus=ahci.0
- To add an Intel E1000 network adapter, pass e.g.: -net nic,model=e1000 -net user
- To add an EHCI-compliant USB host controller, pass e.g.: -device usb-ehci,id=ehci
- To add a NVMe disk, pass e.g.: -drive if=none,file=disk.img,id=mydisk -device nvme,drive=mydisk,serial=foo
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
Applied to u-boot/master, thanks!
I expect Tuomas will send a new version with commit message revised. See below:
I primarily tested with 2.9.0, but I found out that it will work in 2.5.0 as well by changing the syntax to -netdev user,id=net0 -device e1000,netdev=net0 I will update the commit message accordingly.
Ah, oops. But, lets make sure that information ends up in a doc/ README somewhere for easy reference. Thanks!

Note that this commit requires https://github.com/swarren/uboot-test-hooks/pull/14 to go in first.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/.travis.yml b/.travis.yml index 1d29eb35c1..b8f69f0a0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -289,6 +289,11 @@ matrix: QEMU_TARGET="arm-softmmu" BUILDMAN="^integratorcp_cm926ejs$" - env: + - TEST_PY_BD="qemu_arm" + TEST_PY_TEST_SPEC="not sleep" + QEMU_TARGET="arm-softmmu" + BUILDMAN="^qemu_arm$" + - env: - TEST_PY_BD="qemu_mips" TEST_PY_TEST_SPEC="not sleep" QEMU_TARGET="mips-softmmu"

Hi Tuomas,
On Wed, Sep 20, 2017 at 4:18 AM, Tuomas Tynkkynen tuomas.tynkkynen@iki.fi wrote:
Note that this commit requires https://github.com/swarren/uboot-test-hooks/pull/14 to go in first.
I believe the above should not be put into the commit message, instead we can put it below ---
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
.travis.yml | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/.travis.yml b/.travis.yml index 1d29eb35c1..b8f69f0a0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -289,6 +289,11 @@ matrix: QEMU_TARGET="arm-softmmu" BUILDMAN="^integratorcp_cm926ejs$" - env:
- TEST_PY_BD="qemu_arm"
TEST_PY_TEST_SPEC="not sleep"
QEMU_TARGET="arm-softmmu"
BUILDMAN="^qemu_arm$"
- env:
- TEST_PY_BD="qemu_mips" TEST_PY_TEST_SPEC="not sleep" QEMU_TARGET="mips-softmmu"
--
Regards, Bin

On Tue, Sep 19, 2017 at 11:18:08PM +0300, Tuomas Tynkkynen wrote:
Note that this commit requires https://github.com/swarren/uboot-test-hooks/pull/14 to go in first.
Signed-off-by: Tuomas Tynkkynen tuomas.tynkkynen@iki.fi
Applied to u-boot/master, thanks!
participants (4)
-
Bin Meng
-
Peter Robinson
-
Tom Rini
-
Tuomas Tynkkynen