diff options
Diffstat (limited to 'meta-agl-jailhouse')
44 files changed, 4110 insertions, 3 deletions
diff --git a/meta-agl-jailhouse/README.md b/meta-agl-jailhouse/README.md index 395b49d4..fb0a3d42 100644 --- a/meta-agl-jailhouse/README.md +++ b/meta-agl-jailhouse/README.md @@ -1,4 +1,44 @@ -JAILHOUSE SUPPORT LAYER ------------------------ +# Jailhouse support layer + +Yocto layer that enables use of the Jailhouse partitioning hypervisor - <https://github.com/siemens/jailhouse>. + +## How to use + +The AGL feature `agl-jailhouse` has to be enabled. That needs to be done when including aglsetup.sh, for example: + + source meta-agl/scripts/aglsetup.sh -m raspberrypi4 agl-demo agl-netboot agl-appfw-smack agl-jailhouse + +That will enable this layer and include the `jailhouse` package in the image. + +Then, in the target system, the cell configurations (*.cell) are placed in `/usr/share/jailhouse/cells/` and the demo inmates (bare-metal applications to run in a non-root cell) are located in `/usr/share/jailhouse/inmates`. + +## Raspberry Pi 4 example + +Use this commands to enable Jailhouse and run the GIC demo inmate in a non-root cell. After issuing these commands, the GIC demo will be mesauring jitter of a timer and print the output on the serial console of the RPi. + + jailhouse enable /usr/share/jailhouse/cells/rpi4.cell + jailhouse cell create /usr/share/jailhouse/cells/rpi4-inmate-demo.cell + jailhouse cell load inmate-demo /usr/share/jailhouse/inmates/gic-demo.bin + jailhouse cell start inmate-demo + +## Dependencies + +This layer depends on: + +* URI: git://git.yoctoproject.org/meta-arm + * branch: dunfell + * revision: 0bd9c740267c0926e89bcfdb489790b7bf1fbd4b + * note: actually only required on the Raspberry Pi 4 target + +## Supported targets + +* Raspberry Pi 4 + * Currently only the 1 GB RAM variant. The other ones will work too, + but will be limited to 1 GB of memory. + +* QEMU x86-64 + * Work in progress. Requires KVM. Nested virtualization must be enabled on the host. Currently, the right configuration of QEMU and Jailhouse to work out-of-box is being worked on. + + + -to be written diff --git a/meta-agl-jailhouse/conf/layer.conf b/meta-agl-jailhouse/conf/layer.conf new file mode 100644 index 00000000..2031e788 --- /dev/null +++ b/meta-agl-jailhouse/conf/layer.conf @@ -0,0 +1,23 @@ +# We have a conf and classes directory, append to BBPATH +BBPATH .= ":${LAYERDIR}" + +# We have a recipes directory, add to BBFILES +BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend" + +BBFILE_COLLECTIONS += "agl-jailhouse" +BBFILE_PATTERN_agl-jailhouse := "^${LAYERDIR}/" +BBFILE_PRIORITY_agl-jailhouse = "61" + +# This should only be incremented on significant changes that will +# cause compatibility issues with other layers +LAYERVERSION_agl-jailhouse = "1" +LAYERSERIES_COMPAT_agl-jailhouse = "dunfell" + +# This is only needed for Raspberry Pi +# TODO: can this be expressed dynamically? +LAYERDEPENDS_agl-jailhouse = "meta-arm" + +BBFILES_DYNAMIC += " \ + raspberrypi:${LAYERDIR}/dynamic-layers/raspberrypi/*/*/*.bbappend \ +" + diff --git a/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/bootfiles/rpi-config_git.bbappend b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/bootfiles/rpi-config_git.bbappend new file mode 100644 index 00000000..178c982a --- /dev/null +++ b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/bootfiles/rpi-config_git.bbappend @@ -0,0 +1,27 @@ + +do_deploy_append_raspberrypi4() { + # if ARMSTUB is set, it should be set in config.txt by earlier recipes, so replace it + if [ -n "${ARMSTUB}" ]; then + sed -i 's/^armstub=.*/armstub=bl31.bin/' ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + + if ! grep '^enable_gic' config.txt; then + sed -i 's/^enable_gic=.*/enable_gic=1/' ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + else + echo "enable_gic=1" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + fi + + # otherwise, set it + else + echo "# ARM stub configuration" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + echo "armstub=bl31.bin" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + echo "enable_gic=1" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + fi + + sed -i -e "s#dtoverlay=mcp2515.*##g" ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + echo "# Enable CAN (Waveshare RS485 CAN HAT)" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + echo "dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25,spimaxfrequency=1000000" >> ${DEPLOYDIR}/bcm2835-bootfiles/config.txt + + +} + + diff --git a/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/trusted-firmware-a/trusted-firmware-a_%.bbappend b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/trusted-firmware-a/trusted-firmware-a_%.bbappend new file mode 100644 index 00000000..158eb6e8 --- /dev/null +++ b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-bsp/trusted-firmware-a/trusted-firmware-a_%.bbappend @@ -0,0 +1,21 @@ +COMPATIBLE_MACHINE = "raspberrypi4" +TFA_BUILD_TARGET = "bl31" +TFA_PLATFORM = "rpi4" + +# Skip installing the binary into /lib/firmware. We only need it on the boot +# partition that is generated from the files in DEPLOYDIR +do_install[noexec] = "1" + +FILES_${PN} = "" + +do_deploy() { + if ${@"true" if d.getVar('TFA_DEBUG') == '1' else "false"}; then + BUILD_PLAT=${B}/${BUILD_DIR}/debug/ + else + BUILD_PLAT=${B}/${BUILD_DIR}/release/ + fi + + install -d ${DEPLOYDIR}/bcm2835-bootfiles + cp ${BUILD_PLAT}/bl31.bin ${DEPLOYDIR}/bcm2835-bootfiles/bl31.bin +} + diff --git a/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-extended/jailhouse/jailhouse_%.bbappend b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-extended/jailhouse/jailhouse_%.bbappend new file mode 100644 index 00000000..1d8ea6b7 --- /dev/null +++ b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-extended/jailhouse/jailhouse_%.bbappend @@ -0,0 +1,2 @@ +DEPENDS_append_raspberrypi4 = " trusted-firmware-a" + diff --git a/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_%.bbappend b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_%.bbappend new file mode 100644 index 00000000..0616c06e --- /dev/null +++ b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_%.bbappend @@ -0,0 +1,2 @@ +# required for Jailhouse to work with the supplied cell confiugrations +CMDLINE_append = " mem=768M"
\ No newline at end of file diff --git a/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_5.4%.bbappend b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_5.4%.bbappend new file mode 100644 index 00000000..7e37d21b --- /dev/null +++ b/meta-agl-jailhouse/dynamic-layers/raspberrypi/recipes-kernel/linux/linux-raspberrypi_5.4%.bbappend @@ -0,0 +1,4 @@ +LINUX_VERSION = "5.4.51" +SRCREV = "2c8ec3bb4403a7c76c22ec6d3d5fc4b2a366024e" + +require recipes-kernel/linux/linux-jailhouse-5.4.inc diff --git a/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse-arch.inc b/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse-arch.inc new file mode 100644 index 00000000..498b25ed --- /dev/null +++ b/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse-arch.inc @@ -0,0 +1,22 @@ +# Set jailhouse architecture JH_ARCH variable +# +# return value must match one of architectures supported by jailhouse +# +valid_jh_archs = "x86 arm" + +def map_jh_arch(a, d): + import re + + valid_jh_archs = d.getVar('valid_jh_archs', True).split() + + if re.match('(i.86|athlon|x86.64)$', a): return 'x86' + elif re.match('armeb$', a): return 'arm' + elif re.match('aarch64$', a): return 'arm64' + elif re.match('aarch64_be$', a): return 'arm64' + elif a in valid_jh_archs: return a + else: + bb.error("cannot map '%s' to a jailhouse supported architecture" % a) + +export JH_ARCH = "${@map_jh_arch(d.getVar('TARGET_ARCH', True), d)}" + +COMPATIBLE_HOST = "(i.86|x86_64|arm|aarch64).*-linux" diff --git a/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse_git.bb b/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse_git.bb new file mode 100644 index 00000000..c17e5f48 --- /dev/null +++ b/meta-agl-jailhouse/recipes-extended/jailhouse/jailhouse_git.bb @@ -0,0 +1,77 @@ +SUMMARY = "Linux-based partitioning hypervisor" +DESCRIPTION = "Jailhouse is a partitioning Hypervisor based on Linux. It is able to run bare-metal applications or (adapted) \ +operating systems besides Linux. For this purpose, it configures CPU and device virtualization features of the hardware \ +platform in a way that none of these domains, called 'cells' here, can interfere with each other in an unacceptable way." +HOMEPAGE = "https://github.com/siemens/jailhouse" +SECTION = "jailhouse" +LICENSE = "GPL-2.0 & BSD-2-Clause" + +LIC_FILES_CHKSUM = " \ + file://COPYING;md5=9fa7f895f96bde2d47fd5b7d95b6ba4d \ +" + +SRCREV = "4ce7658dddfd5a1682a379d5ac46657e93fe1ff0" +PV = "0.12+git${SRCPV}" + +SRC_URI = "git://github.com/siemens/jailhouse" + +DEPENDS = "virtual/kernel dtc-native python3-mako-native make-native" + +require jailhouse-arch.inc +inherit module python3native bash-completion setuptools3 + +S = "${WORKDIR}/git" +B = "${S}" + +JH_DATADIR ?= "${datadir}/jailhouse" +JH_EXEC_DIR ?= "${libexecdir}/jailhouse" +CELL_DIR ?= "${JH_DATADIR}/cells" +INMATES_DIR ?= "${JH_DATADIR}/inmates" +DTS_DIR ?= "${JH_DATADIR}/cells/dts" + +JH_CELL_FILES ?= "*.cell" + +EXTRA_OEMAKE = "ARCH=${JH_ARCH} CROSS_COMPILE=${TARGET_PREFIX} CC="${CC}" KDIR=${STAGING_KERNEL_BUILDDIR}" + +do_configure() { + sed -i '1s|^#!/usr/bin/env python$|#!/usr/bin/env python3|' ${B}/tools/${BPN}-* +} + +do_compile() { + oe_runmake +} + +do_install() { + # Install pyjailhouse python modules needed by the tools + distutils3_do_install + + # We want to install the python tools, but we do not want to use pip... + # At least with v0.10, we can work around this with + # 'PIP=":" PYTHON_PIP_USEABLE=yes' + oe_runmake PIP=: PYTHON=python3 PYTHON_PIP_USEABLE=yes DESTDIR=${D} install + + install -d ${D}${CELL_DIR} + install -m 0644 ${B}/configs/${JH_ARCH}/${JH_CELL_FILES} ${D}${CELL_DIR}/ + + install -d ${D}${INMATES_DIR} + install -m 0644 ${B}/inmates/demos/${JH_ARCH}/*.bin ${D}${INMATES_DIR} + + if [ ${JH_ARCH} != "x86" ]; then + install -d ${D}${DTS_DIR} + install -m 0644 ${B}/configs/${JH_ARCH}/dts/*.dtb ${D}${DTS_DIR} + fi +} + +PACKAGE_BEFORE_PN = "kernel-module-jailhouse pyjailhouse ${PN}-tools ${PN}-demos" +FILES_${PN} = "${base_libdir}/firmware ${libexecdir} ${sbindir} ${JH_DATADIR}" +FILES_pyjailhouse = "${PYTHON_SITEPACKAGES_DIR}" +FILES_${PN}-tools = "${libexecdir}/${BPN}/${BPN}-* ${JH_DATADIR}/*.tmpl" +FILES_${PN}-demos = "${JH_DATADIR}/ ${sbindir}/ivshmem-demo" + +RDEPENDS_${PN}-tools = "pyjailhouse python3-mmap python3-math python3-datetime python3-curses python3-compression python3-mako" +RDEPENDS_pyjailhouse = "python3-core python3-ctypes python3-fcntl" +RDEPENDS_${PN}-demos = "jailhouse" + +RRECOMMENDS_${PN} = "${PN}-tools" + +KERNEL_MODULE_AUTOLOAD += "jailhouse" diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux-jailhouse-5.4.inc b/meta-agl-jailhouse/recipes-kernel/linux/linux-jailhouse-5.4.inc new file mode 100644 index 00000000..4b571ffd --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux-jailhouse-5.4.inc @@ -0,0 +1,39 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/linux:" + +SRC_URI_append = " file://jailhouse.cfg" + +SRC_URI_append = " \ +file://0001-x86-jailhouse-Improve-setup-data-version-comparison.patch \ +file://0002-x86-jailhouse-Only-enable-platform-UARTs-if-availabl.patch \ +file://0003-jailhouse-Add-simple-debug-console-via-the-hyperviso.patch \ +file://0004-arm-Export-__boot_cpu_mode-for-use-in-Jailhouse-driv.patch \ +file://0005-mm-Re-export-ioremap_page_range.patch \ +file://0006-arm-arm64-export-__hyp_stub_vectors.patch \ +file://0007-x86-Export-lapic_timer_period.patch \ +file://0008-arm64-dts-marvell-armada-37xx-Set-pci-domain.patch \ +file://0009-arm64-dts-marvell-armada-8030-mcbin-Set-pci-domain.patch \ +file://0010-uio-Enable-read-only-mappings.patch \ +file://0011-ivshmem-Add-header-file.patch \ +file://0012-uio-Add-driver-for-inter-VM-shared-memory-device.patch \ +file://0013-ivshmem-net-virtual-network-device-for-Jailhouse.patch \ +file://0014-ivshmem-net-Map-shmem-region-as-RAM.patch \ +file://0015-ivshmem-net-fix-race-in-state-machine.patch \ +file://0016-ivshmem-net-Remove-unused-variable.patch \ +file://0017-ivshmem-net-Enable-INTx.patch \ +file://0018-ivshmem-net-Improve-identification-of-resources.patch \ +file://0019-ivshmem-net-Switch-to-reset-state-on-each-net-stop-a.patch \ +file://0020-ivshmem-net-Add-ethtool-register-dump.patch \ +file://0021-ivshmem-net-Fix-stuck-state-machine-during-setup.patch \ +file://0022-ivshmem-net-Switch-to-relative-descriptor-addresses.patch \ +file://0023-ivshmem-net-Switch-to-pci_alloc_irq_vectors.patch \ +file://0024-ivshmem-net-fill-in-and-check-used-descriptor-chain-.patch \ +file://0025-ivshmem-net-slightly-improve-debug-output.patch \ +file://0026-ivshmem-net-set-and-check-descriptor-flags.patch \ +file://0027-ivshmem-net-add-MAC-changing-interface.patch \ +file://0028-ivshmem-net-Silence-compiler-warning.patch \ +file://0029-ivshmem-net-Fix-bogus-transition-to-RESET-state.patch \ +file://0030-ivshmem-net-Refactor-and-comment-ivshm_net_state_cha.patch \ +file://0031-ivshmem-net-Switch-to-netdev_xmit_more-helper.patch \ +file://0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch \ +" + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux-yocto_5.4%.bbappend b/meta-agl-jailhouse/recipes-kernel/linux/linux-yocto_5.4%.bbappend new file mode 100644 index 00000000..b13f1eb1 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux-yocto_5.4%.bbappend @@ -0,0 +1 @@ +require recipes-kernel/linux/linux-jailhouse-5.4.inc diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0001-x86-jailhouse-Improve-setup-data-version-comparison.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0001-x86-jailhouse-Improve-setup-data-version-comparison.patch new file mode 100644 index 00000000..6b5032df --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0001-x86-jailhouse-Improve-setup-data-version-comparison.patch @@ -0,0 +1,198 @@ +From d47ad4c29f1cd34aff896a88b3dfc4a861a15a6a Mon Sep 17 00:00:00 2001 +From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Date: Thu, 10 Oct 2019 12:21:01 +0200 +Subject: [PATCH 01/32] x86/jailhouse: Improve setup data version comparison + +Soon, setup_data will contain information on passed-through platform +UARTs. This requires some preparational work for the sanity check of the +header and the check of the version. + +Use the following strategy: + + 1. Ensure that the header declares at least enough space for the + version and the compatible_version as it must hold that fields for + any version. The location and semantics of header+version fields + will never change. + + 2. Copy over data -- as much as as possible. The length is either + limited by the header length or the length of setup_data. + + 3. Things are now in place -- sanity check if the header length + complies the actual version. + +For future versions of the setup_data, only step 3 requires alignment. + +Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Signed-off-by: Borislav Petkov <bp@suse.de> +Reviewed-by: Jan Kiszka <jan.kiszka@siemens.com> +Cc: Baoquan He <bhe@redhat.com> +Cc: "H. Peter Anvin" <hpa@zytor.com> +Cc: Ingo Molnar <mingo@redhat.com> +Cc: jailhouse-dev@googlegroups.com +Cc: Juergen Gross <jgross@suse.com> +Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> +Cc: Thomas Gleixner <tglx@linutronix.de> +Cc: x86-ml <x86@kernel.org> +Link: https://lkml.kernel.org/r/20191010102102.421035-2-ralf.ramsauer@oth-regensburg.de +--- + arch/x86/include/uapi/asm/bootparam.h | 22 ++++++++------- + arch/x86/kernel/jailhouse.c | 51 ++++++++++++++++++++++------------- + 2 files changed, 45 insertions(+), 28 deletions(-) + +diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h +index c895df5482c5..43be437c9c71 100644 +--- a/arch/x86/include/uapi/asm/bootparam.h ++++ b/arch/x86/include/uapi/asm/bootparam.h +@@ -139,15 +139,19 @@ struct boot_e820_entry { + * setup data structure. + */ + struct jailhouse_setup_data { +- __u16 version; +- __u16 compatible_version; +- __u16 pm_timer_address; +- __u16 num_cpus; +- __u64 pci_mmconfig_base; +- __u32 tsc_khz; +- __u32 apic_khz; +- __u8 standard_ioapic; +- __u8 cpu_ids[255]; ++ struct { ++ __u16 version; ++ __u16 compatible_version; ++ } __attribute__((packed)) hdr; ++ struct { ++ __u16 pm_timer_address; ++ __u16 num_cpus; ++ __u64 pci_mmconfig_base; ++ __u32 tsc_khz; ++ __u32 apic_khz; ++ __u8 standard_ioapic; ++ __u8 cpu_ids[255]; ++ } __attribute__((packed)) v1; + } __attribute__((packed)); + + /* The so-called "zeropage" */ +diff --git a/arch/x86/kernel/jailhouse.c b/arch/x86/kernel/jailhouse.c +index 3ad34f01de2a..cf4eb37ad97b 100644 +--- a/arch/x86/kernel/jailhouse.c ++++ b/arch/x86/kernel/jailhouse.c +@@ -22,6 +22,8 @@ + #include <asm/jailhouse_para.h> + + static __initdata struct jailhouse_setup_data setup_data; ++#define SETUP_DATA_V1_LEN (sizeof(setup_data.hdr) + sizeof(setup_data.v1)) ++ + static unsigned int precalibrated_tsc_khz; + + static uint32_t jailhouse_cpuid_base(void) +@@ -45,7 +47,7 @@ static void jailhouse_get_wallclock(struct timespec64 *now) + + static void __init jailhouse_timer_init(void) + { +- lapic_timer_period = setup_data.apic_khz * (1000 / HZ); ++ lapic_timer_period = setup_data.v1.apic_khz * (1000 / HZ); + } + + static unsigned long jailhouse_get_tsc(void) +@@ -88,14 +90,14 @@ static void __init jailhouse_get_smp_config(unsigned int early) + + register_lapic_address(0xfee00000); + +- for (cpu = 0; cpu < setup_data.num_cpus; cpu++) { +- generic_processor_info(setup_data.cpu_ids[cpu], ++ for (cpu = 0; cpu < setup_data.v1.num_cpus; cpu++) { ++ generic_processor_info(setup_data.v1.cpu_ids[cpu], + boot_cpu_apic_version); + } + + smp_found_config = 1; + +- if (setup_data.standard_ioapic) { ++ if (setup_data.v1.standard_ioapic) { + mp_register_ioapic(0, 0xfec00000, gsi_top, &ioapic_cfg); + + /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */ +@@ -126,9 +128,9 @@ static int __init jailhouse_pci_arch_init(void) + pcibios_last_bus = 0xff; + + #ifdef CONFIG_PCI_MMCONFIG +- if (setup_data.pci_mmconfig_base) { ++ if (setup_data.v1.pci_mmconfig_base) { + pci_mmconfig_add(0, 0, pcibios_last_bus, +- setup_data.pci_mmconfig_base); ++ setup_data.v1.pci_mmconfig_base); + pci_mmcfg_arch_init(); + } + #endif +@@ -139,6 +141,7 @@ static int __init jailhouse_pci_arch_init(void) + static void __init jailhouse_init_platform(void) + { + u64 pa_data = boot_params.hdr.setup_data; ++ unsigned long setup_data_len; + struct setup_data header; + void *mapping; + +@@ -163,16 +166,8 @@ static void __init jailhouse_init_platform(void) + memcpy(&header, mapping, sizeof(header)); + early_memunmap(mapping, sizeof(header)); + +- if (header.type == SETUP_JAILHOUSE && +- header.len >= sizeof(setup_data)) { +- pa_data += offsetof(struct setup_data, data); +- +- mapping = early_memremap(pa_data, sizeof(setup_data)); +- memcpy(&setup_data, mapping, sizeof(setup_data)); +- early_memunmap(mapping, sizeof(setup_data)); +- ++ if (header.type == SETUP_JAILHOUSE) + break; +- } + + pa_data = header.next; + } +@@ -180,13 +175,27 @@ static void __init jailhouse_init_platform(void) + if (!pa_data) + panic("Jailhouse: No valid setup data found"); + +- if (setup_data.compatible_version > JAILHOUSE_SETUP_REQUIRED_VERSION) +- panic("Jailhouse: Unsupported setup data structure"); ++ /* setup data must at least contain the header */ ++ if (header.len < sizeof(setup_data.hdr)) ++ goto unsupported; + +- pmtmr_ioport = setup_data.pm_timer_address; ++ pa_data += offsetof(struct setup_data, data); ++ setup_data_len = min_t(unsigned long, sizeof(setup_data), ++ (unsigned long)header.len); ++ mapping = early_memremap(pa_data, setup_data_len); ++ memcpy(&setup_data, mapping, setup_data_len); ++ early_memunmap(mapping, setup_data_len); ++ ++ if (setup_data.hdr.version == 0 || ++ setup_data.hdr.compatible_version != ++ JAILHOUSE_SETUP_REQUIRED_VERSION || ++ (setup_data.hdr.version >= 1 && header.len < SETUP_DATA_V1_LEN)) ++ goto unsupported; ++ ++ pmtmr_ioport = setup_data.v1.pm_timer_address; + pr_debug("Jailhouse: PM-Timer IO Port: %#x\n", pmtmr_ioport); + +- precalibrated_tsc_khz = setup_data.tsc_khz; ++ precalibrated_tsc_khz = setup_data.v1.tsc_khz; + setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); + + pci_probe = 0; +@@ -196,6 +205,10 @@ static void __init jailhouse_init_platform(void) + * are none in a non-root cell. + */ + disable_acpi(); ++ return; ++ ++unsupported: ++ panic("Jailhouse: Unsupported setup data structure"); + } + + bool jailhouse_paravirt(void) +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0002-x86-jailhouse-Only-enable-platform-UARTs-if-availabl.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0002-x86-jailhouse-Only-enable-platform-UARTs-if-availabl.patch new file mode 100644 index 00000000..d1db6c71 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0002-x86-jailhouse-Only-enable-platform-UARTs-if-availabl.patch @@ -0,0 +1,200 @@ +From 7f87114a29351547ffb9bd16c4cafb37524806c6 Mon Sep 17 00:00:00 2001 +From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Date: Thu, 10 Oct 2019 12:21:02 +0200 +Subject: [PATCH 02/32] x86/jailhouse: Only enable platform UARTs if available + +ACPI tables aren't available if Linux runs as guest of the hypervisor +Jailhouse. This makes the 8250 driver probe for all platform UARTs as it +assumes that all UARTs are present in case of !ACPI. Jailhouse will stop +execution of Linux guest due to port access violation. + +So far, these access violations were solved by tuning the 8250.nr_uarts +cmdline parameter, but this has limitations: Only consecutive platform +UARTs can be mapped to Linux, and only in the sequence 0x3f8, 0x2f8, +0x3e8, 0x2e8. + +Beginning from setup_data version 2, Jailhouse will place information of +available platform UARTs in setup_data. This allows for selective +activation of platform UARTs. + +Query setup_data version and only activate available UARTS. This +patch comes with backward compatibility, and will still support older +setup_data versions. In case of older setup_data versions, Linux falls +back to the old behaviour. + +Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Signed-off-by: Borislav Petkov <bp@suse.de> +Reviewed-by: Jan Kiszka <jan.kiszka@siemens.com> +Cc: Baoquan He <bhe@redhat.com> +Cc: "H. Peter Anvin" <hpa@zytor.com> +Cc: Ingo Molnar <mingo@redhat.com> +Cc: jailhouse-dev@googlegroups.com +Cc: Juergen Gross <jgross@suse.com> +Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> +Cc: Thomas Gleixner <tglx@linutronix.de> +Cc: x86-ml <x86@kernel.org> +Link: https://lkml.kernel.org/r/20191010102102.421035-3-ralf.ramsauer@oth-regensburg.de +--- + arch/x86/include/uapi/asm/bootparam.h | 3 ++ + arch/x86/kernel/jailhouse.c | 85 +++++++++++++++++++++++++++++------ + 2 files changed, 75 insertions(+), 13 deletions(-) + +diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h +index 43be437c9c71..db1e24e56e94 100644 +--- a/arch/x86/include/uapi/asm/bootparam.h ++++ b/arch/x86/include/uapi/asm/bootparam.h +@@ -152,6 +152,9 @@ struct jailhouse_setup_data { + __u8 standard_ioapic; + __u8 cpu_ids[255]; + } __attribute__((packed)) v1; ++ struct { ++ __u32 flags; ++ } __attribute__((packed)) v2; + } __attribute__((packed)); + + /* The so-called "zeropage" */ +diff --git a/arch/x86/kernel/jailhouse.c b/arch/x86/kernel/jailhouse.c +index cf4eb37ad97b..6eb8b50ea07e 100644 +--- a/arch/x86/kernel/jailhouse.c ++++ b/arch/x86/kernel/jailhouse.c +@@ -11,6 +11,7 @@ + #include <linux/acpi_pmtmr.h> + #include <linux/kernel.h> + #include <linux/reboot.h> ++#include <linux/serial_8250.h> + #include <asm/apic.h> + #include <asm/cpu.h> + #include <asm/hypervisor.h> +@@ -21,11 +22,24 @@ + #include <asm/setup.h> + #include <asm/jailhouse_para.h> + +-static __initdata struct jailhouse_setup_data setup_data; ++static struct jailhouse_setup_data setup_data; + #define SETUP_DATA_V1_LEN (sizeof(setup_data.hdr) + sizeof(setup_data.v1)) ++#define SETUP_DATA_V2_LEN (SETUP_DATA_V1_LEN + sizeof(setup_data.v2)) + + static unsigned int precalibrated_tsc_khz; + ++static void jailhouse_setup_irq(unsigned int irq) ++{ ++ struct mpc_intsrc mp_irq = { ++ .type = MP_INTSRC, ++ .irqtype = mp_INT, ++ .irqflag = MP_IRQPOL_ACTIVE_HIGH | MP_IRQTRIG_EDGE, ++ .srcbusirq = irq, ++ .dstirq = irq, ++ }; ++ mp_save_irq(&mp_irq); ++} ++ + static uint32_t jailhouse_cpuid_base(void) + { + if (boot_cpu_data.cpuid_level < 0 || +@@ -79,11 +93,6 @@ static void __init jailhouse_get_smp_config(unsigned int early) + .type = IOAPIC_DOMAIN_STRICT, + .ops = &mp_ioapic_irqdomain_ops, + }; +- struct mpc_intsrc mp_irq = { +- .type = MP_INTSRC, +- .irqtype = mp_INT, +- .irqflag = MP_IRQPOL_ACTIVE_HIGH | MP_IRQTRIG_EDGE, +- }; + unsigned int cpu; + + jailhouse_x2apic_init(); +@@ -100,12 +109,12 @@ static void __init jailhouse_get_smp_config(unsigned int early) + if (setup_data.v1.standard_ioapic) { + mp_register_ioapic(0, 0xfec00000, gsi_top, &ioapic_cfg); + +- /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */ +- mp_irq.srcbusirq = mp_irq.dstirq = 3; +- mp_save_irq(&mp_irq); +- +- mp_irq.srcbusirq = mp_irq.dstirq = 4; +- mp_save_irq(&mp_irq); ++ if (IS_ENABLED(CONFIG_SERIAL_8250) && ++ setup_data.hdr.version < 2) { ++ /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */ ++ jailhouse_setup_irq(3); ++ jailhouse_setup_irq(4); ++ } + } + } + +@@ -138,6 +147,53 @@ static int __init jailhouse_pci_arch_init(void) + return 0; + } + ++#ifdef CONFIG_SERIAL_8250 ++static inline bool jailhouse_uart_enabled(unsigned int uart_nr) ++{ ++ return setup_data.v2.flags & BIT(uart_nr); ++} ++ ++static void jailhouse_serial_fixup(int port, struct uart_port *up, ++ u32 *capabilities) ++{ ++ static const u16 pcuart_base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; ++ unsigned int n; ++ ++ for (n = 0; n < ARRAY_SIZE(pcuart_base); n++) { ++ if (pcuart_base[n] != up->iobase) ++ continue; ++ ++ if (jailhouse_uart_enabled(n)) { ++ pr_info("Enabling UART%u (port 0x%lx)\n", n, ++ up->iobase); ++ jailhouse_setup_irq(up->irq); ++ } else { ++ /* Deactivate UART if access isn't allowed */ ++ up->iobase = 0; ++ } ++ break; ++ } ++} ++ ++static void __init jailhouse_serial_workaround(void) ++{ ++ /* ++ * There are flags inside setup_data that indicate availability of ++ * platform UARTs since setup data version 2. ++ * ++ * In case of version 1, we don't know which UARTs belong Linux. In ++ * this case, unconditionally register 1:1 mapping for legacy UART IRQs ++ * 3 and 4. ++ */ ++ if (setup_data.hdr.version > 1) ++ serial8250_set_isa_configurator(jailhouse_serial_fixup); ++} ++#else /* !CONFIG_SERIAL_8250 */ ++static inline void jailhouse_serial_workaround(void) ++{ ++} ++#endif /* CONFIG_SERIAL_8250 */ ++ + static void __init jailhouse_init_platform(void) + { + u64 pa_data = boot_params.hdr.setup_data; +@@ -189,7 +245,8 @@ static void __init jailhouse_init_platform(void) + if (setup_data.hdr.version == 0 || + setup_data.hdr.compatible_version != + JAILHOUSE_SETUP_REQUIRED_VERSION || +- (setup_data.hdr.version >= 1 && header.len < SETUP_DATA_V1_LEN)) ++ (setup_data.hdr.version == 1 && header.len < SETUP_DATA_V1_LEN) || ++ (setup_data.hdr.version >= 2 && header.len < SETUP_DATA_V2_LEN)) + goto unsupported; + + pmtmr_ioport = setup_data.v1.pm_timer_address; +@@ -205,6 +262,8 @@ static void __init jailhouse_init_platform(void) + * are none in a non-root cell. + */ + disable_acpi(); ++ ++ jailhouse_serial_workaround(); + return; + + unsupported: +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0003-jailhouse-Add-simple-debug-console-via-the-hyperviso.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0003-jailhouse-Add-simple-debug-console-via-the-hyperviso.patch new file mode 100644 index 00000000..289f54ac --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0003-jailhouse-Add-simple-debug-console-via-the-hyperviso.patch @@ -0,0 +1,174 @@ +From faa349f4d096554c6d5bfe74634599d2e26f64a7 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 11 Sep 2016 23:30:04 +0200 +Subject: [PATCH 03/32] jailhouse: Add simple debug console via the hypervisor + +Jailhouse allows explicitly enabled cells to write character-wise +messages to the hypervisor debug console. Make use of this for a +platform-agnostic boot diagnosis channel, specifically for non-root +cells. This also comes with earlycon support. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + MAINTAINERS | 1 + + drivers/virt/Kconfig | 11 +++++ + drivers/virt/Makefile | 1 + + drivers/virt/jailhouse_dbgcon.c | 103 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 116 insertions(+) + create mode 100644 drivers/virt/jailhouse_dbgcon.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 9d3a5c54a41d..07cb4d674c93 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8761,6 +8761,7 @@ L: jailhouse-dev@googlegroups.com + S: Maintained + F: arch/x86/kernel/jailhouse.c + F: arch/x86/include/asm/jailhouse_para.h ++F: drivers/virt/jailhouse_dbgcon.c + + JC42.4 TEMPERATURE SENSOR DRIVER + M: Guenter Roeck <linux@roeck-us.net> +diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig +index 363af2eaf2ba..99c5eaca6952 100644 +--- a/drivers/virt/Kconfig ++++ b/drivers/virt/Kconfig +@@ -31,5 +31,16 @@ config FSL_HV_MANAGER + 4) A kernel interface for receiving callbacks when a managed + partition shuts down. + ++config JAILHOUSE_DBGCON ++ tristate "Jailhouse console driver" ++ depends on X86 || ARM || ARM64 ++ help ++ The Jailhouse hypervisor provides a simple write-only console for ++ debugging the bootstrap process of its cells. This driver registers ++ a console with the kernel to make use of it. ++ ++ Note that Jailhouse has to be configured to permit a cell the usage ++ of the console interface. ++ + source "drivers/virt/vboxguest/Kconfig" + endif +diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile +index fd331247c27a..89e86a1d0f19 100644 +--- a/drivers/virt/Makefile ++++ b/drivers/virt/Makefile +@@ -4,4 +4,5 @@ + # + + obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o ++obj-$(CONFIG_JAILHOUSE_DBGCON) += jailhouse_dbgcon.o + obj-y += vboxguest/ +diff --git a/drivers/virt/jailhouse_dbgcon.c b/drivers/virt/jailhouse_dbgcon.c +new file mode 100644 +index 000000000000..1fd201ea1460 +--- /dev/null ++++ b/drivers/virt/jailhouse_dbgcon.c +@@ -0,0 +1,103 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Console driver for running over the Jailhouse partitioning hypervisor ++ * ++ * Copyright (c) Siemens AG, 2016-2018 ++ * ++ * Authors: ++ * Jan Kiszka <jan.kiszka@siemens.com> ++ */ ++ ++#include <linux/console.h> ++#include <linux/hypervisor.h> ++#include <linux/module.h> ++#include <linux/serial_core.h> ++#ifdef CONFIG_X86 ++#include <asm/alternative.h> ++#endif ++#ifdef CONFIG_ARM ++#include <asm/opcodes-virt.h> ++#endif ++ ++#define JAILHOUSE_HC_DEBUG_CONSOLE_PUTC 8 ++ ++static void hypervisor_putc(char c) ++{ ++#if defined(CONFIG_X86) ++ int result; ++ ++ asm volatile( ++ ALTERNATIVE(".byte 0x0f,0x01,0xc1", ".byte 0x0f,0x01,0xd9", ++ X86_FEATURE_VMMCALL) ++ : "=a" (result) ++ : "a" (JAILHOUSE_HC_DEBUG_CONSOLE_PUTC), "D" (c) ++ : "memory"); ++#elif defined(CONFIG_ARM) ++ register u32 num_res asm("r0") = JAILHOUSE_HC_DEBUG_CONSOLE_PUTC; ++ register u32 arg1 asm("r1") = c; ++ ++ asm volatile( ++ __HVC(0x4a48) ++ : "=r" (num_res) ++ : "r" (num_res), "r" (arg1) ++ : "memory"); ++#elif defined(CONFIG_ARM64) ++ register u64 num_res asm("x0") = JAILHOUSE_HC_DEBUG_CONSOLE_PUTC; ++ register u64 arg1 asm("x1") = c; ++ ++ asm volatile( ++ "hvc #0x4a48\n\t" ++ : "=r" (num_res) ++ : "r" (num_res), "r" (arg1) ++ : "memory"); ++#else ++#error Unsupported architecture. ++#endif ++} ++ ++static void jailhouse_dbgcon_write(struct console *con, const char *s, ++ unsigned count) ++{ ++ while (count > 0) { ++ hypervisor_putc(*s); ++ count--; ++ s++; ++ } ++} ++ ++static int __init early_jailhouse_dbgcon_setup(struct earlycon_device *device, ++ const char *options) ++{ ++ device->con->write = jailhouse_dbgcon_write; ++ return 0; ++} ++ ++EARLYCON_DECLARE(jailhouse, early_jailhouse_dbgcon_setup); ++ ++static struct console jailhouse_dbgcon = { ++ .name = "jailhouse", ++ .write = jailhouse_dbgcon_write, ++ .flags = CON_PRINTBUFFER | CON_ANYTIME, ++ .index = -1, ++}; ++ ++static int __init jailhouse_dbgcon_init(void) ++{ ++ if (!jailhouse_paravirt()) ++ return -ENODEV; ++ ++ register_console(&jailhouse_dbgcon); ++ return 0; ++} ++ ++static void __exit jailhouse_dbgcon_exit(void) ++{ ++ unregister_console(&jailhouse_dbgcon); ++} ++ ++module_init(jailhouse_dbgcon_init); ++module_exit(jailhouse_dbgcon_exit); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Jailhouse debug console driver"); ++MODULE_AUTHOR("Jan Kiszka <jan.kiszka@siemens.com>"); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0004-arm-Export-__boot_cpu_mode-for-use-in-Jailhouse-driv.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0004-arm-Export-__boot_cpu_mode-for-use-in-Jailhouse-driv.patch new file mode 100644 index 00000000..6e4470f0 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0004-arm-Export-__boot_cpu_mode-for-use-in-Jailhouse-driv.patch @@ -0,0 +1,43 @@ +From 56e5aace5a675af0557b87a137b98e40454d8e22 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 3 Jul 2016 10:02:40 +0200 +Subject: [PATCH 04/32] arm: Export __boot_cpu_mode for use in Jailhouse driver + module + +Onlining a CPU while Jailhouse was running sets BOOT_CPU_MODE_MISMATCH +because the kernel detect that the CPU will now only come up in SVC +mode. Therefore, we need to fix up the flag after disabling Jailhouse +again. + +Moreover, exporting the symbol allows to use is_hyp_mode_available() in +the driver, thus prevents us from crashing during Jailhouse activation +when there is no hyp stub installed. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + arch/arm/kernel/armksyms.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c +index 98bdea51089d..f2fa635bccf7 100644 +--- a/arch/arm/kernel/armksyms.c ++++ b/arch/arm/kernel/armksyms.c +@@ -17,6 +17,7 @@ + + #include <asm/checksum.h> + #include <asm/ftrace.h> ++#include <asm/virt.h> + + /* + * libgcc functions - functions that are used internally by the +@@ -176,3 +177,7 @@ EXPORT_SYMBOL(__pv_offset); + EXPORT_SYMBOL(__arm_smccc_smc); + EXPORT_SYMBOL(__arm_smccc_hvc); + #endif ++ ++#ifdef CONFIG_ARM_VIRT_EXT ++EXPORT_SYMBOL_GPL(__boot_cpu_mode); ++#endif +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0005-mm-Re-export-ioremap_page_range.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0005-mm-Re-export-ioremap_page_range.patch new file mode 100644 index 00000000..e6bf1c35 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0005-mm-Re-export-ioremap_page_range.patch @@ -0,0 +1,25 @@ +From cf5d27beb6aad2b69d716b0aee08f43619c338a9 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 7 Feb 2017 17:52:00 +0100 +Subject: [PATCH 05/32] mm: Re-export ioremap_page_range + +We need this in Jailhouse to map at specific virtual addresses, at +least for the moment. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + lib/ioremap.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/ioremap.c b/lib/ioremap.c +index 0a2ffadc6d71..baefdda8f32c 100644 +--- a/lib/ioremap.c ++++ b/lib/ioremap.c +@@ -231,3 +231,4 @@ int ioremap_page_range(unsigned long addr, + + return err; + } ++EXPORT_SYMBOL_GPL(ioremap_page_range); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0006-arm-arm64-export-__hyp_stub_vectors.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0006-arm-arm64-export-__hyp_stub_vectors.patch new file mode 100644 index 00000000..42f3ea48 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0006-arm-arm64-export-__hyp_stub_vectors.patch @@ -0,0 +1,57 @@ +From 27588702a6792ff86a57317ef60d6e218796598e Mon Sep 17 00:00:00 2001 +From: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Date: Wed, 7 Jun 2017 15:48:43 +0200 +Subject: [PATCH 06/32] arm, arm64: export __hyp_stub_vectors + +HVC_GET_VECTORS got removed. External hypervisors, like Jailhouse, need +this address when they are deactivated, in order to restore original +state. + +Signed-off-by: Ralf Ramsauer <ralf.ramsauer@oth-regensburg.de> +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + arch/arm/kernel/hyp-stub.S | 2 ++ + arch/arm64/kernel/hyp-stub.S | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/arch/arm/kernel/hyp-stub.S b/arch/arm/kernel/hyp-stub.S +index ae5020302de4..463366ccd2c9 100644 +--- a/arch/arm/kernel/hyp-stub.S ++++ b/arch/arm/kernel/hyp-stub.S +@@ -6,6 +6,7 @@ + #include <linux/init.h> + #include <linux/irqchip/arm-gic-v3.h> + #include <linux/linkage.h> ++#include <asm-generic/export.h> + #include <asm/assembler.h> + #include <asm/virt.h> + +@@ -269,4 +270,5 @@ __hyp_stub_trap: W(b) __hyp_stub_do_trap + __hyp_stub_irq: W(b) . + __hyp_stub_fiq: W(b) . + ENDPROC(__hyp_stub_vectors) ++EXPORT_SYMBOL_GPL(__hyp_stub_vectors) + +diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S +index 73d46070b315..ef2503bba71d 100644 +--- a/arch/arm64/kernel/hyp-stub.S ++++ b/arch/arm64/kernel/hyp-stub.S +@@ -10,6 +10,7 @@ + #include <linux/linkage.h> + #include <linux/irqchip/arm-gic-v3.h> + ++#include <asm-generic/export.h> + #include <asm/assembler.h> + #include <asm/kvm_arm.h> + #include <asm/kvm_asm.h> +@@ -42,6 +43,7 @@ ENTRY(__hyp_stub_vectors) + ventry el1_fiq_invalid // FIQ 32-bit EL1 + ventry el1_error_invalid // Error 32-bit EL1 + ENDPROC(__hyp_stub_vectors) ++EXPORT_SYMBOL_GPL(__hyp_stub_vectors) + + .align 11 + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0007-x86-Export-lapic_timer_period.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0007-x86-Export-lapic_timer_period.patch new file mode 100644 index 00000000..9b2a2f2c --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0007-x86-Export-lapic_timer_period.patch @@ -0,0 +1,28 @@ +From 3f87075ce9d3e04e8e43de2e88dd7728b3777eb8 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Thu, 23 Nov 2017 07:12:57 +0100 +Subject: [PATCH 07/32] x86: Export lapic_timer_period + +Required for the Jailhouse driver in order to forward the calibration +value to other cells. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + arch/x86/kernel/apic/apic.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c +index 2b0faf86da1b..0428ad289899 100644 +--- a/arch/x86/kernel/apic/apic.c ++++ b/arch/x86/kernel/apic/apic.c +@@ -196,6 +196,7 @@ static struct resource lapic_resource = { + }; + + unsigned int lapic_timer_period = 0; ++EXPORT_SYMBOL_GPL(lapic_timer_period); + + static void apic_pm_activate(void); + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0008-arm64-dts-marvell-armada-37xx-Set-pci-domain.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0008-arm64-dts-marvell-armada-37xx-Set-pci-domain.patch new file mode 100644 index 00000000..963989cf --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0008-arm64-dts-marvell-armada-37xx-Set-pci-domain.patch @@ -0,0 +1,31 @@ +From 764a3a5da899b596474edf916b44dfc034443445 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Mon, 17 Sep 2018 08:08:08 +0200 +Subject: [PATCH 08/32] arm64: dts: marvell: armada-37xx: Set pci-domain + +This is a nop for normal operation but allows the device tree overlay +that the Jailhouse hypervisor injects to use pci-domain as well +(linux,pci-domain has to be applied consistently in a system). That will +assign a stable PCI domain to the secondary, virtual host controller of +Jailhouse. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +index 000c135e39b7..d839cea9d361 100644 +--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +@@ -482,6 +482,7 @@ + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x00 0xff>; ++ linux,pci-domain = <0>; + interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + msi-parent = <&pcie0>; +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0009-arm64-dts-marvell-armada-8030-mcbin-Set-pci-domain.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0009-arm64-dts-marvell-armada-8030-mcbin-Set-pci-domain.patch new file mode 100644 index 00000000..8e99fa12 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0009-arm64-dts-marvell-armada-8030-mcbin-Set-pci-domain.patch @@ -0,0 +1,31 @@ +From 1f916502347d2b902002b430cffe18b11685f211 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 30 Sep 2018 21:22:32 +0200 +Subject: [PATCH 09/32] arm64: dts: marvell: armada-8030-mcbin: Set pci-domain + +This is a nop for normal operation but allows the device tree overlay +that the Jailhouse hypervisor injects to use pci-domain as well +(linux,pci-domain has to be applied consistently in a system). That will +assign a stable PCI domain to the secondary, virtual host controller of +Jailhouse. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi +index d250f4b2bfed..58bac50b06eb 100644 +--- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dtsi +@@ -174,6 +174,7 @@ + }; + + &cp0_pcie0 { ++ linux,pci-domain = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&cp0_pcie_pins>; + num-lanes = <4>; +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0010-uio-Enable-read-only-mappings.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0010-uio-Enable-read-only-mappings.patch new file mode 100644 index 00000000..2fa65641 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0010-uio-Enable-read-only-mappings.patch @@ -0,0 +1,57 @@ +From 9c8885c6e020451e4a4578be9db318e5c07227ea Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 4 Jun 2019 14:40:09 +0200 +Subject: [PATCH 10/32] uio: Enable read-only mappings + +This allows to tag memory regions read-only, denying userspace to map +them writable. Default remains read/write. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/uio/uio.c | 9 +++++++++ + include/linux/uio_driver.h | 2 ++ + 2 files changed, 11 insertions(+) + +diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c +index a57698985f9c..ac18542ee4fe 100644 +--- a/drivers/uio/uio.c ++++ b/drivers/uio/uio.c +@@ -790,6 +790,15 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma) + goto out; + } + ++ if (idev->info->mem[mi].readonly) { ++ if (vma->vm_flags & VM_WRITE) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ vma->vm_flags &= ~VM_MAYWRITE; ++ } ++ + if (idev->info->mmap) { + ret = idev->info->mmap(idev->info, vma); + goto out; +diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h +index 01081c4726c0..ebfc06e36ca2 100644 +--- a/include/linux/uio_driver.h ++++ b/include/linux/uio_driver.h +@@ -31,6 +31,7 @@ struct uio_map; + * @offs: offset of device memory within the page + * @size: size of IO (multiple of page size) + * @memtype: type of memory addr points to ++ * @readonly: true of region is read-only + * @internal_addr: ioremap-ped version of addr, for driver internal use + * @map: for use by the UIO core only. + */ +@@ -40,6 +41,7 @@ struct uio_mem { + unsigned long offs; + resource_size_t size; + int memtype; ++ bool readonly; + void __iomem *internal_addr; + struct uio_map *map; + }; +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0011-ivshmem-Add-header-file.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0011-ivshmem-Add-header-file.patch new file mode 100644 index 00000000..f143d150 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0011-ivshmem-Add-header-file.patch @@ -0,0 +1,52 @@ +From 61d003be018fb5b874e6ffbf746684c53556c00e Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 1 Oct 2019 12:33:25 +0200 +Subject: [PATCH 11/32] ivshmem: Add header file + +Common defines and structures for the ivshmem device. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + include/linux/ivshmem.h | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + create mode 100644 include/linux/ivshmem.h + +diff --git a/include/linux/ivshmem.h b/include/linux/ivshmem.h +new file mode 100644 +index 000000000000..bad8547f071b +--- /dev/null ++++ b/include/linux/ivshmem.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++#ifndef _LINUX_IVSHMEM_H ++#define _LINUX_IVSHMEM_H ++ ++#include <linux/types.h> ++ ++#define IVSHM_PROTO_UNDEFINED 0x0000 ++#define IVSHM_PROTO_NET 0x0001 ++#define IVSHM_PROTO_VIRTIO_FRONT 0x8000 ++#define IVSHM_PROTO_VIRTIO_BACK 0xc000 ++#define IVSHM_PROTO_VIRTIO_DEVID_MASK 0x7fff ++ ++#define IVSHM_CFG_PRIV_CNTL 0x03 ++# define IVSHM_PRIV_CNTL_ONESHOT_INT BIT(0) ++#define IVSHM_CFG_STATE_TAB_SZ 0x04 ++#define IVSHM_CFG_RW_SECTION_SZ 0x08 ++#define IVSHM_CFG_OUTPUT_SECTION_SZ 0x10 ++#define IVSHM_CFG_ADDRESS 0x18 ++ ++struct ivshm_regs { ++ u32 id; ++ u32 max_peers; ++ u32 int_control; ++ u32 doorbell; ++ u32 state; ++}; ++ ++#define IVSHM_INT_ENABLE BIT(0) ++ ++#endif /* _LINUX_IVSHMEM_H */ +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0012-uio-Add-driver-for-inter-VM-shared-memory-device.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0012-uio-Add-driver-for-inter-VM-shared-memory-device.patch new file mode 100644 index 00000000..f98670c3 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0012-uio-Add-driver-for-inter-VM-shared-memory-device.patch @@ -0,0 +1,311 @@ +From 205cdad2dc9fc8a6a7204b2a71408b43085dd45f Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 4 Jun 2019 18:40:25 +0200 +Subject: [PATCH 12/32] uio: Add driver for inter-VM shared memory device + +This adds a UIO driver the ivshmem device, found in QEMU and the +Jailhouse hypervisor. It exposes the MMIO register region and all shared +memory section to userspace. Interrupts are configured in one-shot mode +so that userspace needs to re-enable them after each event via the +Interrupt Control register. The driver registers all possible MSI-X +vectors, coalescing them into the single notifier UIO provides. + +Note: Specification work for the interface is ongoing, so details may +still change. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/uio/Kconfig | 7 ++ + drivers/uio/Makefile | 1 + + drivers/uio/uio_ivshmem.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/pci_ids.h | 1 + + 4 files changed, 250 insertions(+) + create mode 100644 drivers/uio/uio_ivshmem.c + +diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig +index 202ee81cfc2b..a130500f46b8 100644 +--- a/drivers/uio/Kconfig ++++ b/drivers/uio/Kconfig +@@ -165,4 +165,11 @@ config UIO_HV_GENERIC + to network and storage devices from userspace. + + If you compile this as a module, it will be called uio_hv_generic. ++ ++config UIO_IVSHMEM ++ tristate "Inter-VM Shared Memory driver" ++ depends on PCI ++ help ++ Userspace I/O driver for the inter-VM shared memory PCI device ++ as provided by QEMU and the Jailhouse hypervisor. + endif +diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile +index c285dd2a4539..3911fefb2a7e 100644 +--- a/drivers/uio/Makefile ++++ b/drivers/uio/Makefile +@@ -11,3 +11,4 @@ obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o + obj-$(CONFIG_UIO_MF624) += uio_mf624.o + obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o + obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o ++obj-$(CONFIG_UIO_IVSHMEM) += uio_ivshmem.o +diff --git a/drivers/uio/uio_ivshmem.c b/drivers/uio/uio_ivshmem.c +new file mode 100644 +index 000000000000..0c16d428c6ed +--- /dev/null ++++ b/drivers/uio/uio_ivshmem.c +@@ -0,0 +1,241 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * UIO driver for Inter-VM shared memory PCI device ++ * ++ * Copyright (c) Siemens AG, 2019 ++ * ++ * Authors: ++ * Jan Kiszka <jan.kiszka@siemens.com> ++ */ ++ ++#include <linux/ivshmem.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/uio_driver.h> ++ ++#define DRV_NAME "uio_ivshmem" ++ ++struct ivshm_dev { ++ struct uio_info info; ++ struct pci_dev *pdev; ++ struct ivshm_regs __iomem *regs; ++ int vectors; ++}; ++ ++static irqreturn_t ivshm_irq_handler(int irq, void *dev_id) ++{ ++ struct ivshm_dev *ivshm_dev = (struct ivshm_dev *)dev_id; ++ ++ /* nothing else to do, we configured one-shot interrupt mode */ ++ uio_event_notify(&ivshm_dev->info); ++ ++ return IRQ_HANDLED; ++} ++ ++static u64 get_config_qword(struct pci_dev *pdev, unsigned int pos) ++{ ++ u32 lo, hi; ++ ++ pci_read_config_dword(pdev, pos, &lo); ++ pci_read_config_dword(pdev, pos + 4, &hi); ++ return lo | ((u64)hi << 32); ++} ++ ++static int ivshm_release(struct uio_info *info, struct inode *inode) ++{ ++ struct ivshm_dev *ivshm_dev = ++ container_of(info, struct ivshm_dev, info); ++ ++ writel(0, &ivshm_dev->regs->state); ++ return 0; ++} ++ ++static int ivshm_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ resource_size_t rw_section_sz, output_section_sz; ++ struct ivshm_dev *ivshm_dev; ++ phys_addr_t section_addr; ++ int err, vendor_cap, i; ++ unsigned int cap_pos; ++ struct uio_mem *mem; ++ char *device_name; ++ u32 dword; ++ ++ ivshm_dev = devm_kzalloc(&pdev->dev, sizeof(struct ivshm_dev), ++ GFP_KERNEL); ++ if (!ivshm_dev) ++ return -ENOMEM; ++ ++ err = pcim_enable_device(pdev); ++ if (err) ++ return err; ++ ++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s[%s]", DRV_NAME, ++ dev_name(&pdev->dev)); ++ if (!device_name) ++ return -ENOMEM; ++ ++ ivshm_dev->info.name = device_name; ++ ivshm_dev->info.version = "1"; ++ ivshm_dev->info.release = ivshm_release; ++ ++ err = pcim_iomap_regions(pdev, BIT(0), device_name); ++ if (err) ++ return err; ++ ivshm_dev->regs = pcim_iomap_table(pdev)[0]; ++ ++ mem = &ivshm_dev->info.mem[0]; ++ ++ mem->name = "registers"; ++ mem->addr = pci_resource_start(pdev, 0); ++ if (!mem->addr) ++ return -ENODEV; ++ mem->size = pci_resource_len(pdev, 0); ++ mem->memtype = UIO_MEM_PHYS; ++ ++ vendor_cap = pci_find_capability(pdev, PCI_CAP_ID_VNDR); ++ if (vendor_cap < 0) ++ return -ENODEV; ++ ++ if (pci_resource_len(pdev, 2) > 0) { ++ section_addr = pci_resource_start(pdev, 2); ++ } else { ++ cap_pos = vendor_cap + IVSHM_CFG_ADDRESS; ++ section_addr = get_config_qword(pdev, cap_pos); ++ } ++ ++ mem++; ++ mem->name = "state_table"; ++ mem->addr = section_addr; ++ cap_pos = vendor_cap + IVSHM_CFG_STATE_TAB_SZ; ++ pci_read_config_dword(pdev, cap_pos, &dword); ++ mem->size = dword; ++ mem->memtype = UIO_MEM_IOVA; ++ mem->readonly = true; ++ if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size, ++ device_name)) ++ return -EBUSY; ++ dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name, &mem->addr, ++ &mem->size); ++ ++ cap_pos = vendor_cap + IVSHM_CFG_RW_SECTION_SZ; ++ rw_section_sz = get_config_qword(pdev, cap_pos); ++ if (rw_section_sz > 0) { ++ section_addr += mem->size; ++ ++ mem++; ++ mem->name = "rw_section"; ++ mem->addr = section_addr; ++ mem->size = rw_section_sz; ++ mem->memtype = UIO_MEM_IOVA; ++ if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size, ++ device_name)) ++ return -EBUSY; ++ dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name, ++ &mem->addr, &mem->size); ++ } ++ ++ cap_pos = vendor_cap + IVSHM_CFG_OUTPUT_SECTION_SZ; ++ output_section_sz = get_config_qword(pdev, cap_pos); ++ if (output_section_sz > 0) { ++ section_addr += mem->size; ++ ++ mem++; ++ mem->name = "input_sections"; ++ mem->addr = section_addr; ++ mem->size = ++ readl(&ivshm_dev->regs->max_peers) * output_section_sz; ++ mem->memtype = UIO_MEM_IOVA; ++ mem->readonly = true; ++ if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size, ++ device_name)) ++ return -EBUSY; ++ dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name, ++ &mem->addr, &mem->size); ++ ++ mem++; ++ mem->name = "output_section"; ++ mem->addr = section_addr + ++ readl(&ivshm_dev->regs->id) * output_section_sz; ++ mem->size = output_section_sz; ++ mem->memtype = UIO_MEM_IOVA; ++ dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name, ++ &mem->addr, &mem->size); ++ } ++ ++ pci_write_config_byte(pdev, vendor_cap + IVSHM_CFG_PRIV_CNTL, ++ IVSHM_PRIV_CNTL_ONESHOT_INT); ++ ++ /* ++ * Grab all vectors although we can only coalesce them into a single ++ * notifier. This avoids missing any event. ++ */ ++ ivshm_dev->vectors = pci_msix_vec_count(pdev); ++ if (ivshm_dev->vectors < 0) ++ ivshm_dev->vectors = 1; ++ ++ err = pci_alloc_irq_vectors(pdev, ivshm_dev->vectors, ++ ivshm_dev->vectors, ++ PCI_IRQ_LEGACY | PCI_IRQ_MSIX); ++ if (err < 0) ++ return err; ++ ++ for (i = 0; i < ivshm_dev->vectors; i++) { ++ err = request_irq(pci_irq_vector(pdev, i), ivshm_irq_handler, ++ IRQF_SHARED, ivshm_dev->info.name, ivshm_dev); ++ if (err) ++ goto error; ++ } ++ ++ ivshm_dev->info.irq = UIO_IRQ_CUSTOM; ++ ++ err = uio_register_device(&pdev->dev, &ivshm_dev->info); ++ if (err) ++ goto error; ++ ++ pci_set_master(pdev); ++ ++ pci_set_drvdata(pdev, ivshm_dev); ++ ++ return 0; ++ ++error: ++ while (--i > 0) ++ free_irq(pci_irq_vector(pdev, i), ivshm_dev); ++ pci_free_irq_vectors(pdev); ++ return err; ++} ++ ++static void ivshm_remove(struct pci_dev *pdev) ++{ ++ struct ivshm_dev *ivshm_dev = pci_get_drvdata(pdev); ++ int i; ++ ++ writel(0, &ivshm_dev->regs->int_control); ++ pci_clear_master(pdev); ++ ++ uio_unregister_device(&ivshm_dev->info); ++ ++ for (i = 0; i < ivshm_dev->vectors; i++) ++ free_irq(pci_irq_vector(pdev, i), ivshm_dev); ++ ++ pci_free_irq_vectors(pdev); ++} ++ ++static const struct pci_device_id ivshm_device_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_IVSHMEM), ++ (PCI_CLASS_OTHERS << 16) | IVSHM_PROTO_UNDEFINED, 0xffffff }, ++ { 0 } ++}; ++MODULE_DEVICE_TABLE(pci, ivshm_device_id_table); ++ ++static struct pci_driver uio_ivshm_driver = { ++ .name = DRV_NAME, ++ .id_table = ivshm_device_id_table, ++ .probe = ivshm_probe, ++ .remove = ivshm_remove, ++}; ++module_pci_driver(uio_ivshm_driver); ++ ++MODULE_AUTHOR("Jan Kiszka <jan.kiszka@siemens.com>"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +index 21a572469a4e..e450458c8ba8 100644 +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -1477,6 +1477,7 @@ + + #define PCI_VENDOR_ID_SIEMENS 0x110A + #define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 ++#define PCI_DEVICE_ID_IVSHMEM 0x4106 + + #define PCI_VENDOR_ID_VORTEX 0x1119 + #define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0013-ivshmem-net-virtual-network-device-for-Jailhouse.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0013-ivshmem-net-virtual-network-device-for-Jailhouse.patch new file mode 100644 index 00000000..ae5ac475 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0013-ivshmem-net-virtual-network-device-for-Jailhouse.patch @@ -0,0 +1,968 @@ +From fa0e2362149bb814d6b7431a7c42989d33002f60 Mon Sep 17 00:00:00 2001 +From: Mans Rullgard <mans@mansr.com> +Date: Thu, 26 May 2016 16:04:02 +0100 +Subject: [PATCH 13/32] ivshmem-net: virtual network device for Jailhouse + +Work in progress. +--- + drivers/net/Kconfig | 4 + + drivers/net/Makefile | 2 + + drivers/net/ivshmem-net.c | 923 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 929 insertions(+) + create mode 100644 drivers/net/ivshmem-net.c + +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index df1c7989e13d..8c65f55163e3 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -527,4 +527,8 @@ config NET_FAILOVER + a VM with direct attached VF by failing over to the paravirtual + datapath when the VF is unplugged. + ++config IVSHMEM_NET ++ tristate "IVSHMEM virtual network device" ++ depends on PCI ++ + endif # NETDEVICES +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index 0d3ba056cda3..5041c293d4d0 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -79,3 +79,5 @@ thunderbolt-net-y += thunderbolt.o + obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o + obj-$(CONFIG_NETDEVSIM) += netdevsim/ + obj-$(CONFIG_NET_FAILOVER) += net_failover.o ++ ++obj-$(CONFIG_IVSHMEM_NET) += ivshmem-net.o +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +new file mode 100644 +index 000000000000..b676bed2cc2e +--- /dev/null ++++ b/drivers/net/ivshmem-net.c +@@ -0,0 +1,923 @@ ++/* ++ * Copyright 2016 Mans Rullgard <mans@mansr.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that 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/>. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pci.h> ++#include <linux/io.h> ++#include <linux/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/virtio_ring.h> ++ ++#define DRV_NAME "ivshmem-net" ++ ++#define JAILHOUSE_CFG_SHMEM_PTR 0x40 ++#define JAILHOUSE_CFG_SHMEM_SZ 0x48 ++ ++#define IVSHM_NET_STATE_RESET 0 ++#define IVSHM_NET_STATE_INIT 1 ++#define IVSHM_NET_STATE_READY 2 ++#define IVSHM_NET_STATE_RUN 3 ++ ++#define IVSHM_NET_MTU_MIN 256 ++#define IVSHM_NET_MTU_MAX 65535 ++#define IVSHM_NET_MTU_DEF 16384 ++ ++#define IVSHM_NET_FRAME_SIZE(s) ALIGN(18 + (s), SMP_CACHE_BYTES) ++ ++#define IVSHM_NET_VQ_ALIGN 64 ++ ++struct ivshmem_regs { ++ u32 imask; ++ u32 istat; ++ u32 ivpos; ++ u32 doorbell; ++ u32 lstate; ++ u32 rstate; ++}; ++ ++struct ivshm_net_queue { ++ struct vring vr; ++ u32 free_head; ++ u32 num_free; ++ u32 num_added; ++ u16 last_avail_idx; ++ u16 last_used_idx; ++ ++ void *data; ++ void *end; ++ u32 size; ++ u32 head; ++ u32 tail; ++}; ++ ++struct ivshm_net_stats { ++ u32 interrupts; ++ u32 tx_packets; ++ u32 tx_notify; ++ u32 tx_pause; ++ u32 rx_packets; ++ u32 rx_notify; ++ u32 napi_poll; ++ u32 napi_complete; ++ u32 napi_poll_n[10]; ++}; ++ ++struct ivshm_net { ++ struct ivshm_net_queue rx; ++ struct ivshm_net_queue tx; ++ ++ u32 vrsize; ++ u32 qlen; ++ u32 qsize; ++ ++ spinlock_t tx_free_lock; ++ spinlock_t tx_clean_lock; ++ ++ struct napi_struct napi; ++ ++ u32 lstate; ++ u32 rstate; ++ ++ struct workqueue_struct *state_wq; ++ struct work_struct state_work; ++ ++ struct ivshm_net_stats stats; ++ ++ struct ivshmem_regs __iomem *ivshm_regs; ++ void *shm; ++ phys_addr_t shmaddr; ++ resource_size_t shmlen; ++ u32 peer_id; ++ ++ struct pci_dev *pdev; ++ struct msix_entry msix; ++ bool using_msix; ++}; ++ ++static void *ivshm_net_desc_data(struct ivshm_net *in, ++ struct ivshm_net_queue *q, ++ struct vring_desc *desc, ++ u32 *len) ++{ ++ u64 addr = READ_ONCE(desc->addr); ++ u32 dlen = READ_ONCE(desc->len); ++ void *data; ++ ++ if (addr < in->shmaddr || desc->addr > in->shmaddr + in->shmlen) ++ return NULL; ++ ++ data = in->shm + (addr - in->shmaddr); ++ ++ if (data < q->data || data >= q->end) ++ return NULL; ++ ++ if (dlen > q->end - data) ++ return NULL; ++ ++ *len = dlen; ++ ++ return data; ++} ++ ++static void ivshm_net_init_queue(struct ivshm_net *in, ++ struct ivshm_net_queue *q, ++ void *mem, unsigned int len) ++{ ++ memset(q, 0, sizeof(*q)); ++ ++ vring_init(&q->vr, len, mem, IVSHM_NET_VQ_ALIGN); ++ q->data = mem + in->vrsize; ++ q->end = q->data + in->qsize; ++ q->size = in->qsize; ++} ++ ++static void ivshm_net_init_queues(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ int ivpos = readl(&in->ivshm_regs->ivpos); ++ void *tx; ++ void *rx; ++ int i; ++ ++ tx = in->shm + ivpos * in->shmlen / 2; ++ rx = in->shm + !ivpos * in->shmlen / 2; ++ ++ memset(tx, 0, in->shmlen / 2); ++ ++ ivshm_net_init_queue(in, &in->rx, rx, in->qlen); ++ ivshm_net_init_queue(in, &in->tx, tx, in->qlen); ++ ++ swap(in->rx.vr.used, in->tx.vr.used); ++ ++ in->tx.num_free = in->tx.vr.num; ++ ++ for (i = 0; i < in->tx.vr.num - 1; i++) ++ in->tx.vr.desc[i].next = i + 1; ++} ++ ++static int ivshm_net_calc_qsize(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ unsigned int vrsize; ++ unsigned int qsize; ++ unsigned int qlen; ++ ++ for (qlen = 4096; qlen > 32; qlen >>= 1) { ++ vrsize = vring_size(qlen, IVSHM_NET_VQ_ALIGN); ++ vrsize = ALIGN(vrsize, IVSHM_NET_VQ_ALIGN); ++ if (vrsize < in->shmlen / 16) ++ break; ++ } ++ ++ if (vrsize > in->shmlen / 2) ++ return -EINVAL; ++ ++ qsize = in->shmlen / 2 - vrsize; ++ ++ if (qsize < 4 * IVSHM_NET_MTU_MIN) ++ return -EINVAL; ++ ++ in->vrsize = vrsize; ++ in->qlen = qlen; ++ in->qsize = qsize; ++ ++ return 0; ++} ++ ++static void ivshm_net_notify_tx(struct ivshm_net *in, unsigned int num) ++{ ++ u16 evt, old, new; ++ ++ virt_mb(); ++ ++ evt = READ_ONCE(vring_avail_event(&in->tx.vr)); ++ old = in->tx.last_avail_idx - num; ++ new = in->tx.last_avail_idx; ++ ++ if (vring_need_event(evt, new, old)) { ++ writel(in->peer_id << 16, &in->ivshm_regs->doorbell); ++ in->stats.tx_notify++; ++ } ++} ++ ++static void ivshm_net_enable_rx_irq(struct ivshm_net *in) ++{ ++ vring_avail_event(&in->rx.vr) = in->rx.last_avail_idx; ++ virt_wmb(); ++} ++ ++static void ivshm_net_notify_rx(struct ivshm_net *in, unsigned int num) ++{ ++ u16 evt, old, new; ++ ++ virt_mb(); ++ ++ evt = vring_used_event(&in->rx.vr); ++ old = in->rx.last_used_idx - num; ++ new = in->rx.last_used_idx; ++ ++ if (vring_need_event(evt, new, old)) { ++ writel(in->peer_id << 16, &in->ivshm_regs->doorbell); ++ in->stats.rx_notify++; ++ } ++} ++ ++static void ivshm_net_enable_tx_irq(struct ivshm_net *in) ++{ ++ vring_used_event(&in->tx.vr) = in->tx.last_used_idx; ++ virt_wmb(); ++} ++ ++static bool ivshm_net_rx_avail(struct ivshm_net *in) ++{ ++ virt_mb(); ++ return READ_ONCE(in->rx.vr.avail->idx) != in->rx.last_avail_idx; ++} ++ ++static size_t ivshm_net_tx_space(struct ivshm_net *in) ++{ ++ struct ivshm_net_queue *tx = &in->tx; ++ u32 tail = tx->tail; ++ u32 head = tx->head; ++ u32 space; ++ ++ if (head < tail) ++ space = tail - head; ++ else ++ space = max(tx->size - head, tail); ++ ++ return space; ++} ++ ++static bool ivshm_net_tx_ok(struct ivshm_net *in, unsigned int mtu) ++{ ++ return in->tx.num_free >= 2 && ++ ivshm_net_tx_space(in) >= 2 * IVSHM_NET_FRAME_SIZE(mtu); ++} ++ ++static u32 ivshm_net_tx_advance(struct ivshm_net_queue *q, u32 *pos, u32 len) ++{ ++ u32 p = *pos; ++ ++ len = IVSHM_NET_FRAME_SIZE(len); ++ ++ if (q->size - p < len) ++ p = 0; ++ *pos = p + len; ++ ++ return p; ++} ++ ++static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ struct ivshm_net_queue *tx = &in->tx; ++ struct vring *vr = &tx->vr; ++ struct vring_desc *desc; ++ unsigned int desc_idx; ++ unsigned int avail; ++ u32 head; ++ void *buf; ++ ++ BUG_ON(tx->num_free < 1); ++ ++ spin_lock(&in->tx_free_lock); ++ desc_idx = tx->free_head; ++ desc = &vr->desc[desc_idx]; ++ tx->free_head = desc->next; ++ tx->num_free--; ++ spin_unlock(&in->tx_free_lock); ++ ++ head = ivshm_net_tx_advance(tx, &tx->head, skb->len); ++ ++ buf = tx->data + head; ++ skb_copy_and_csum_dev(skb, buf); ++ ++ desc->addr = in->shmaddr + (buf - in->shm); ++ desc->len = skb->len; ++ ++ avail = tx->last_avail_idx++ & (vr->num - 1); ++ vr->avail->ring[avail] = desc_idx; ++ tx->num_added++; ++ ++ if (!skb->xmit_more) { ++ virt_store_release(&vr->avail->idx, tx->last_avail_idx); ++ ivshm_net_notify_tx(in, tx->num_added); ++ tx->num_added = 0; ++ } ++ ++ return 0; ++} ++ ++static void ivshm_net_tx_clean(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ struct ivshm_net_queue *tx = &in->tx; ++ struct vring *vr = &tx->vr; ++ struct vring_desc *desc; ++ struct vring_desc *fdesc; ++ unsigned int used; ++ unsigned int num; ++ u16 used_idx; ++ u16 last; ++ u32 fhead; ++ ++ if (!spin_trylock(&in->tx_clean_lock)) ++ return; ++ ++ used_idx = virt_load_acquire(&vr->used->idx); ++ last = tx->last_used_idx; ++ ++ fdesc = NULL; ++ num = 0; ++ ++ while (last != used_idx) { ++ void *data; ++ u32 len; ++ u32 tail; ++ ++ used = vr->used->ring[last & (vr->num - 1)].id; ++ if (used >= vr->num) { ++ netdev_err(ndev, "invalid tx used %d\n", used); ++ break; ++ } ++ ++ desc = &vr->desc[used]; ++ ++ data = ivshm_net_desc_data(in, &in->tx, desc, &len); ++ if (!data) { ++ netdev_err(ndev, "bad tx descriptor\n"); ++ break; ++ } ++ ++ tail = ivshm_net_tx_advance(tx, &tx->tail, len); ++ if (data != tx->data + tail) { ++ netdev_err(ndev, "bad tx descriptor\n"); ++ break; ++ } ++ ++ if (!num) ++ fdesc = desc; ++ else ++ desc->next = fhead; ++ ++ fhead = used; ++ last++; ++ num++; ++ } ++ ++ tx->last_used_idx = last; ++ ++ spin_unlock(&in->tx_clean_lock); ++ ++ if (num) { ++ spin_lock(&in->tx_free_lock); ++ fdesc->next = tx->free_head; ++ tx->free_head = fhead; ++ tx->num_free += num; ++ BUG_ON(tx->num_free > vr->num); ++ spin_unlock(&in->tx_free_lock); ++ } ++} ++ ++static struct vring_desc *ivshm_net_rx_desc(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ struct ivshm_net_queue *rx = &in->rx; ++ struct vring *vr = &rx->vr; ++ unsigned int avail; ++ u16 avail_idx; ++ ++ avail_idx = virt_load_acquire(&vr->avail->idx); ++ ++ if (avail_idx == rx->last_avail_idx) ++ return NULL; ++ ++ avail = vr->avail->ring[rx->last_avail_idx++ & (vr->num - 1)]; ++ if (avail >= vr->num) { ++ netdev_err(ndev, "invalid rx avail %d\n", avail); ++ return NULL; ++ } ++ ++ return &vr->desc[avail]; ++} ++ ++static void ivshm_net_rx_finish(struct ivshm_net *in, struct vring_desc *desc) ++{ ++ struct ivshm_net_queue *rx = &in->rx; ++ struct vring *vr = &rx->vr; ++ unsigned int desc_id = desc - vr->desc; ++ unsigned int used; ++ ++ used = rx->last_used_idx++ & (vr->num - 1); ++ vr->used->ring[used].id = desc_id; ++ ++ virt_store_release(&vr->used->idx, rx->last_used_idx); ++} ++ ++static int ivshm_net_poll(struct napi_struct *napi, int budget) ++{ ++ struct net_device *ndev = napi->dev; ++ struct ivshm_net *in = container_of(napi, struct ivshm_net, napi); ++ int received = 0; ++ ++ in->stats.napi_poll++; ++ ++ ivshm_net_tx_clean(ndev); ++ ++ while (received < budget) { ++ struct vring_desc *desc; ++ struct sk_buff *skb; ++ void *data; ++ u32 len; ++ ++ desc = ivshm_net_rx_desc(ndev); ++ if (!desc) ++ break; ++ ++ data = ivshm_net_desc_data(in, &in->rx, desc, &len); ++ if (!data) { ++ netdev_err(ndev, "bad rx descriptor\n"); ++ break; ++ } ++ ++ skb = napi_alloc_skb(napi, len); ++ ++ if (skb) { ++ memcpy(skb_put(skb, len), data, len); ++ skb->protocol = eth_type_trans(skb, ndev); ++ napi_gro_receive(napi, skb); ++ } ++ ++ ndev->stats.rx_packets++; ++ ndev->stats.rx_bytes += len; ++ ++ ivshm_net_rx_finish(in, desc); ++ received++; ++ } ++ ++ if (received < budget) { ++ in->stats.napi_complete++; ++ napi_complete_done(napi, received); ++ ivshm_net_enable_rx_irq(in); ++ if (ivshm_net_rx_avail(in)) ++ napi_schedule(napi); ++ } ++ ++ if (received) ++ ivshm_net_notify_rx(in, received); ++ ++ in->stats.rx_packets += received; ++ in->stats.napi_poll_n[received ? 1 + min(ilog2(received), 8) : 0]++; ++ ++ if (ivshm_net_tx_ok(in, ndev->mtu)) ++ netif_wake_queue(ndev); ++ ++ return received; ++} ++ ++static netdev_tx_t ivshm_net_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ ivshm_net_tx_clean(ndev); ++ ++ if (!ivshm_net_tx_ok(in, ndev->mtu)) { ++ ivshm_net_enable_tx_irq(in); ++ netif_stop_queue(ndev); ++ skb->xmit_more = 0; ++ in->stats.tx_pause++; ++ } ++ ++ ivshm_net_tx_frame(ndev, skb); ++ ++ in->stats.tx_packets++; ++ ndev->stats.tx_packets++; ++ ndev->stats.tx_bytes += skb->len; ++ ++ dev_consume_skb_any(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static void ivshm_net_set_state(struct ivshm_net *in, u32 state) ++{ ++ virt_wmb(); ++ WRITE_ONCE(in->lstate, state); ++ writel(state, &in->ivshm_regs->lstate); ++} ++ ++static void ivshm_net_run(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ netif_start_queue(ndev); ++ napi_enable(&in->napi); ++ napi_schedule(&in->napi); ++ ivshm_net_set_state(in, IVSHM_NET_STATE_RUN); ++} ++ ++static void ivshm_net_state_change(struct work_struct *work) ++{ ++ struct ivshm_net *in = container_of(work, struct ivshm_net, state_work); ++ struct net_device *ndev = in->napi.dev; ++ u32 rstate = readl(&in->ivshm_regs->rstate); ++ ++ ++ switch (in->lstate) { ++ case IVSHM_NET_STATE_RESET: ++ if (rstate < IVSHM_NET_STATE_READY) ++ ivshm_net_set_state(in, IVSHM_NET_STATE_INIT); ++ break; ++ ++ case IVSHM_NET_STATE_INIT: ++ if (rstate > IVSHM_NET_STATE_RESET) { ++ ivshm_net_init_queues(ndev); ++ ivshm_net_set_state(in, IVSHM_NET_STATE_READY); ++ ++ rtnl_lock(); ++ call_netdevice_notifiers(NETDEV_CHANGEADDR, ndev); ++ rtnl_unlock(); ++ } ++ break; ++ ++ case IVSHM_NET_STATE_READY: ++ if (rstate >= IVSHM_NET_STATE_READY) { ++ netif_carrier_on(ndev); ++ if (ndev->flags & IFF_UP) ++ ivshm_net_run(ndev); ++ } else { ++ netif_carrier_off(ndev); ++ ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); ++ } ++ break; ++ ++ case IVSHM_NET_STATE_RUN: ++ if (rstate < IVSHM_NET_STATE_READY) { ++ netif_stop_queue(ndev); ++ napi_disable(&in->napi); ++ netif_carrier_off(ndev); ++ ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); ++ } ++ break; ++ } ++ ++ virt_wmb(); ++ WRITE_ONCE(in->rstate, rstate); ++} ++ ++static bool ivshm_net_check_state(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ u32 rstate = readl(&in->ivshm_regs->rstate); ++ ++ if (rstate != READ_ONCE(in->rstate) || ++ in->lstate != IVSHM_NET_STATE_RUN) { ++ queue_work(in->state_wq, &in->state_work); ++ return false; ++ } ++ ++ return true; ++} ++ ++static irqreturn_t ivshm_net_int(int irq, void *data) ++{ ++ struct net_device *ndev = data; ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ in->stats.interrupts++; ++ ++ ivshm_net_check_state(ndev); ++ napi_schedule_irqoff(&in->napi); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ivshm_net_open(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ netdev_reset_queue(ndev); ++ ndev->operstate = IF_OPER_UP; ++ ++ if (in->lstate == IVSHM_NET_STATE_READY) ++ ivshm_net_run(ndev); ++ ++ return 0; ++} ++ ++static int ivshm_net_stop(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ ndev->operstate = IF_OPER_DOWN; ++ ++ if (in->lstate == IVSHM_NET_STATE_RUN) { ++ napi_disable(&in->napi); ++ netif_stop_queue(ndev); ++ ivshm_net_set_state(in, IVSHM_NET_STATE_READY); ++ } ++ ++ return 0; ++} ++ ++static int ivshm_net_change_mtu(struct net_device *ndev, int mtu) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ struct ivshm_net_queue *tx = &in->tx; ++ ++ if (mtu < IVSHM_NET_MTU_MIN || mtu > IVSHM_NET_MTU_MAX) ++ return -EINVAL; ++ ++ if (in->tx.size / mtu < 4) ++ return -EINVAL; ++ ++ if (ivshm_net_tx_space(in) < 2 * IVSHM_NET_FRAME_SIZE(mtu)) ++ return -EBUSY; ++ ++ if (in->tx.size - tx->head < IVSHM_NET_FRAME_SIZE(mtu) && ++ tx->head < tx->tail) ++ return -EBUSY; ++ ++ netif_tx_lock_bh(ndev); ++ if (in->tx.size - tx->head < IVSHM_NET_FRAME_SIZE(mtu)) ++ tx->head = 0; ++ netif_tx_unlock_bh(ndev); ++ ++ ndev->mtu = mtu; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++static void ivshm_net_poll_controller(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ napi_schedule(&in->napi); ++} ++#endif ++ ++static const struct net_device_ops ivshm_net_ops = { ++ .ndo_open = ivshm_net_open, ++ .ndo_stop = ivshm_net_stop, ++ .ndo_start_xmit = ivshm_net_xmit, ++ .ndo_change_mtu = ivshm_net_change_mtu, ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ .ndo_poll_controller = ivshm_net_poll_controller, ++#endif ++}; ++ ++static const char ivshm_net_stats[][ETH_GSTRING_LEN] = { ++ "interrupts", ++ "tx_packets", ++ "tx_notify", ++ "tx_pause", ++ "rx_packets", ++ "rx_notify", ++ "napi_poll", ++ "napi_complete", ++ "napi_poll_0", ++ "napi_poll_1", ++ "napi_poll_2", ++ "napi_poll_4", ++ "napi_poll_8", ++ "napi_poll_16", ++ "napi_poll_32", ++ "napi_poll_64", ++ "napi_poll_128", ++ "napi_poll_256", ++}; ++ ++#define NUM_STATS ARRAY_SIZE(ivshm_net_stats) ++ ++static int ivshm_net_get_sset_count(struct net_device *ndev, int sset) ++{ ++ if (sset == ETH_SS_STATS) ++ return NUM_STATS; ++ ++ return -EOPNOTSUPP; ++} ++ ++static void ivshm_net_get_strings(struct net_device *ndev, u32 sset, u8 *buf) ++{ ++ if (sset == ETH_SS_STATS) ++ memcpy(buf, &ivshm_net_stats, sizeof(ivshm_net_stats)); ++} ++ ++static void ivshm_net_get_ethtool_stats(struct net_device *ndev, ++ struct ethtool_stats *estats, u64 *st) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ unsigned int n = 0; ++ unsigned int i; ++ ++ st[n++] = in->stats.interrupts; ++ st[n++] = in->stats.tx_packets; ++ st[n++] = in->stats.tx_notify; ++ st[n++] = in->stats.tx_pause; ++ st[n++] = in->stats.rx_packets; ++ st[n++] = in->stats.rx_notify; ++ st[n++] = in->stats.napi_poll; ++ st[n++] = in->stats.napi_complete; ++ ++ for (i = 0; i < ARRAY_SIZE(in->stats.napi_poll_n); i++) ++ st[n++] = in->stats.napi_poll_n[i]; ++ ++ memset(&in->stats, 0, sizeof(in->stats)); ++} ++ ++static const struct ethtool_ops ivshm_net_ethtool_ops = { ++ .get_sset_count = ivshm_net_get_sset_count, ++ .get_strings = ivshm_net_get_strings, ++ .get_ethtool_stats = ivshm_net_get_ethtool_stats, ++}; ++ ++static int ivshm_net_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ struct net_device *ndev; ++ struct ivshm_net *in; ++ struct ivshmem_regs __iomem *regs; ++ resource_size_t shmaddr; ++ resource_size_t shmlen; ++ int interrupt; ++ void *shm; ++ u32 ivpos; ++ int err; ++ ++ err = pcim_enable_device(pdev); ++ if (err) { ++ dev_err(&pdev->dev, "pci_enable_device: %d\n", err); ++ return err; ++ } ++ ++ err = pcim_iomap_regions(pdev, BIT(0), DRV_NAME); ++ if (err) { ++ dev_err(&pdev->dev, "pcim_iomap_regions: %d\n", err); ++ return err; ++ } ++ ++ regs = pcim_iomap_table(pdev)[0]; ++ ++ shmlen = pci_resource_len(pdev, 2); ++ ++ if (shmlen) { ++ shmaddr = pci_resource_start(pdev, 2); ++ } else { ++ union { u64 v; u32 hl[2]; } val; ++ ++ pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR, ++ &val.hl[0]); ++ pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR + 4, ++ &val.hl[1]); ++ shmaddr = val.v; ++ ++ pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ, ++ &val.hl[0]); ++ pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ + 4, ++ &val.hl[1]); ++ shmlen = val.v; ++ } ++ ++ ++ if (!devm_request_mem_region(&pdev->dev, shmaddr, shmlen, DRV_NAME)) ++ return -EBUSY; ++ ++ shm = devm_memremap(&pdev->dev, shmaddr, shmlen, MEMREMAP_WC); ++ if (!shm) ++ return -ENOMEM; ++ ++ ivpos = readl(®s->ivpos); ++ if (ivpos > 1) { ++ dev_err(&pdev->dev, "invalid IVPosition %d\n", ivpos); ++ return -EINVAL; ++ } ++ ++ dev_info(&pdev->dev, "shared memory size %pa\n", &shmlen); ++ ++ ndev = alloc_etherdev(sizeof(*in)); ++ if (!ndev) ++ return -ENOMEM; ++ ++ pci_set_drvdata(pdev, ndev); ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ in = netdev_priv(ndev); ++ in->ivshm_regs = regs; ++ in->shm = shm; ++ in->shmaddr = shmaddr; ++ in->shmlen = shmlen; ++ in->peer_id = !ivpos; ++ in->pdev = pdev; ++ spin_lock_init(&in->tx_free_lock); ++ spin_lock_init(&in->tx_clean_lock); ++ ++ err = ivshm_net_calc_qsize(ndev); ++ if (err) ++ goto err_free; ++ ++ in->state_wq = alloc_ordered_workqueue(DRV_NAME, 0); ++ if (!in->state_wq) ++ goto err_free; ++ ++ INIT_WORK(&in->state_work, ivshm_net_state_change); ++ ++ eth_random_addr(ndev->dev_addr); ++ ndev->netdev_ops = &ivshm_net_ops; ++ ndev->ethtool_ops = &ivshm_net_ethtool_ops; ++ ndev->mtu = min_t(u32, IVSHM_NET_MTU_DEF, in->qsize / 16); ++ ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG; ++ ndev->features = ndev->hw_features; ++ ++ netif_carrier_off(ndev); ++ netif_napi_add(ndev, &in->napi, ivshm_net_poll, NAPI_POLL_WEIGHT); ++ ++ err = register_netdev(ndev); ++ if (err) ++ goto err_wq; ++ ++ err = pci_enable_msix(pdev, &in->msix, 1); ++ if (!err) { ++ interrupt = in->msix.vector; ++ in->using_msix = true; ++ } else { ++ interrupt = pdev->irq; ++ in->using_msix = false; ++ } ++ ++ err = request_irq(interrupt, ivshm_net_int, 0, DRV_NAME, ndev); ++ if (err) ++ goto err_int; ++ ++ pci_set_master(pdev); ++ ++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); ++ ++ return 0; ++ ++err_int: ++ if (in->using_msix) ++ pci_disable_msix(pdev); ++ unregister_netdev(ndev); ++err_wq: ++ destroy_workqueue(in->state_wq); ++err_free: ++ free_netdev(ndev); ++ ++ return err; ++} ++ ++static void ivshm_net_remove(struct pci_dev *pdev) ++{ ++ struct net_device *ndev = pci_get_drvdata(pdev); ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ if (in->using_msix) { ++ free_irq(in->msix.vector, ndev); ++ pci_disable_msix(pdev); ++ } else { ++ free_irq(pdev->irq, ndev); ++ } ++ ++ unregister_netdev(ndev); ++ cancel_work_sync(&in->state_work); ++ destroy_workqueue(in->state_wq); ++ free_netdev(ndev); ++} ++ ++static const struct pci_device_id ivshm_net_id_table[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1110), ++ (PCI_CLASS_OTHERS << 16) | (0x01 << 8), 0xffff00 }, ++ { 0 } ++}; ++MODULE_DEVICE_TABLE(pci, ivshm_net_id_table); ++ ++static struct pci_driver ivshm_net_driver = { ++ .name = DRV_NAME, ++ .id_table = ivshm_net_id_table, ++ .probe = ivshm_net_probe, ++ .remove = ivshm_net_remove, ++}; ++module_pci_driver(ivshm_net_driver); ++ ++MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); ++MODULE_LICENSE("GPL"); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0014-ivshmem-net-Map-shmem-region-as-RAM.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0014-ivshmem-net-Map-shmem-region-as-RAM.patch new file mode 100644 index 00000000..de66d7a7 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0014-ivshmem-net-Map-shmem-region-as-RAM.patch @@ -0,0 +1,30 @@ +From b70f2ecb71b212225f403a8f26d3d5bdca70a107 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Thu, 24 Nov 2016 08:27:45 +0100 +Subject: [PATCH 14/32] ivshmem-net: Map shmem region as RAM + +No need for special caching, simply map the shared memory region like +RAM, thus write-back. This gives us another order of magnitude in +throughput. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index b676bed2cc2e..a535cb71adde 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -802,7 +802,7 @@ static int ivshm_net_probe(struct pci_dev *pdev, + if (!devm_request_mem_region(&pdev->dev, shmaddr, shmlen, DRV_NAME)) + return -EBUSY; + +- shm = devm_memremap(&pdev->dev, shmaddr, shmlen, MEMREMAP_WC); ++ shm = devm_memremap(&pdev->dev, shmaddr, shmlen, MEMREMAP_WB); + if (!shm) + return -ENOMEM; + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0015-ivshmem-net-fix-race-in-state-machine.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0015-ivshmem-net-fix-race-in-state-machine.patch new file mode 100644 index 00000000..ecb5c485 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0015-ivshmem-net-fix-race-in-state-machine.patch @@ -0,0 +1,140 @@ +From 1349a457e5e33580fb26afab087e6b932b1a4372 Mon Sep 17 00:00:00 2001 +From: Mans Rullgard <mans@mansr.com> +Date: Thu, 24 Nov 2016 18:46:41 +0000 +Subject: [PATCH 15/32] ivshmem-net: fix race in state machine + +--- + drivers/net/ivshmem-net.c | 60 ++++++++++++++++++++++++----------------------- + 1 file changed, 31 insertions(+), 29 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index a535cb71adde..acd00e47acd7 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -36,6 +36,8 @@ + #define IVSHM_NET_STATE_READY 2 + #define IVSHM_NET_STATE_RUN 3 + ++#define IVSHM_NET_FLAG_RUN 0 ++ + #define IVSHM_NET_MTU_MIN 256 + #define IVSHM_NET_MTU_MAX 65535 + #define IVSHM_NET_MTU_DEF 16384 +@@ -96,6 +98,8 @@ struct ivshm_net { + u32 lstate; + u32 rstate; + ++ unsigned long flags; ++ + struct workqueue_struct *state_wq; + struct work_struct state_work; + +@@ -529,12 +533,32 @@ static void ivshm_net_run(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); + ++ if (in->lstate < IVSHM_NET_STATE_READY) ++ return; ++ ++ if (!netif_running(ndev)) ++ return; ++ ++ if (test_and_set_bit(IVSHM_NET_FLAG_RUN, &in->flags)) ++ return; ++ + netif_start_queue(ndev); + napi_enable(&in->napi); + napi_schedule(&in->napi); + ivshm_net_set_state(in, IVSHM_NET_STATE_RUN); + } + ++static void ivshm_net_do_stop(struct net_device *ndev) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ ++ if (!test_and_clear_bit(IVSHM_NET_FLAG_RUN, &in->flags)) ++ return; ++ ++ netif_stop_queue(ndev); ++ napi_disable(&in->napi); ++} ++ + static void ivshm_net_state_change(struct work_struct *work) + { + struct ivshm_net *in = container_of(work, struct ivshm_net, state_work); +@@ -560,21 +584,13 @@ static void ivshm_net_state_change(struct work_struct *work) + break; + + case IVSHM_NET_STATE_READY: ++ case IVSHM_NET_STATE_RUN: + if (rstate >= IVSHM_NET_STATE_READY) { + netif_carrier_on(ndev); +- if (ndev->flags & IFF_UP) +- ivshm_net_run(ndev); ++ ivshm_net_run(ndev); + } else { + netif_carrier_off(ndev); +- ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); +- } +- break; +- +- case IVSHM_NET_STATE_RUN: +- if (rstate < IVSHM_NET_STATE_READY) { +- netif_stop_queue(ndev); +- napi_disable(&in->napi); +- netif_carrier_off(ndev); ++ ivshm_net_do_stop(ndev); + ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); + } + break; +@@ -584,18 +600,13 @@ static void ivshm_net_state_change(struct work_struct *work) + WRITE_ONCE(in->rstate, rstate); + } + +-static bool ivshm_net_check_state(struct net_device *ndev) ++static void ivshm_net_check_state(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); + u32 rstate = readl(&in->ivshm_regs->rstate); + +- if (rstate != READ_ONCE(in->rstate) || +- in->lstate != IVSHM_NET_STATE_RUN) { ++ if (rstate != in->rstate || !test_bit(IVSHM_NET_FLAG_RUN, &in->flags)) + queue_work(in->state_wq, &in->state_work); +- return false; +- } +- +- return true; + } + + static irqreturn_t ivshm_net_int(int irq, void *data) +@@ -617,24 +628,15 @@ static int ivshm_net_open(struct net_device *ndev) + + netdev_reset_queue(ndev); + ndev->operstate = IF_OPER_UP; +- +- if (in->lstate == IVSHM_NET_STATE_READY) +- ivshm_net_run(ndev); ++ ivshm_net_run(ndev); + + return 0; + } + + static int ivshm_net_stop(struct net_device *ndev) + { +- struct ivshm_net *in = netdev_priv(ndev); +- + ndev->operstate = IF_OPER_DOWN; +- +- if (in->lstate == IVSHM_NET_STATE_RUN) { +- napi_disable(&in->napi); +- netif_stop_queue(ndev); +- ivshm_net_set_state(in, IVSHM_NET_STATE_READY); +- } ++ ivshm_net_do_stop(ndev); + + return 0; + } +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0016-ivshmem-net-Remove-unused-variable.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0016-ivshmem-net-Remove-unused-variable.patch new file mode 100644 index 00000000..dfaca493 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0016-ivshmem-net-Remove-unused-variable.patch @@ -0,0 +1,28 @@ +From 85362cf2e682d196fc3a88d05e21f0603cb0c48a Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Fri, 25 Nov 2016 17:31:51 +0100 +Subject: [PATCH 16/32] ivshmem-net: Remove unused variable + +Became unused by previous change. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index acd00e47acd7..ccef65d0f038 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -624,8 +624,6 @@ static irqreturn_t ivshm_net_int(int irq, void *data) + + static int ivshm_net_open(struct net_device *ndev) + { +- struct ivshm_net *in = netdev_priv(ndev); +- + netdev_reset_queue(ndev); + ndev->operstate = IF_OPER_UP; + ivshm_net_run(ndev); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0017-ivshmem-net-Enable-INTx.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0017-ivshmem-net-Enable-INTx.patch new file mode 100644 index 00000000..e0f4be44 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0017-ivshmem-net-Enable-INTx.patch @@ -0,0 +1,55 @@ +From 9683ab979a373932e9c332d2db8115b6c23303d0 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 27 Nov 2016 15:15:51 +0100 +Subject: [PATCH 17/32] ivshmem-net: Enable INTx + +Activate INTx notification when it has to be used instead of MSI-X, +disable it after use. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index ccef65d0f038..591d04195e57 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -31,6 +31,8 @@ + #define JAILHOUSE_CFG_SHMEM_PTR 0x40 + #define JAILHOUSE_CFG_SHMEM_SZ 0x48 + ++#define IVSHMEM_INTX_ENABLE 0x1 ++ + #define IVSHM_NET_STATE_RESET 0 + #define IVSHM_NET_STATE_INIT 1 + #define IVSHM_NET_STATE_READY 2 +@@ -47,7 +49,7 @@ + #define IVSHM_NET_VQ_ALIGN 64 + + struct ivshmem_regs { +- u32 imask; ++ u32 intxctrl; + u32 istat; + u32 ivpos; + u32 doorbell; +@@ -869,6 +871,8 @@ static int ivshm_net_probe(struct pci_dev *pdev, + goto err_int; + + pci_set_master(pdev); ++ if (!in->using_msix) ++ writel(IVSHMEM_INTX_ENABLE, &in->ivshm_regs->intxctrl); + + writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); + +@@ -895,6 +899,7 @@ static void ivshm_net_remove(struct pci_dev *pdev) + free_irq(in->msix.vector, ndev); + pci_disable_msix(pdev); + } else { ++ writel(0, &in->ivshm_regs->intxctrl); + free_irq(pdev->irq, ndev); + } + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0018-ivshmem-net-Improve-identification-of-resources.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0018-ivshmem-net-Improve-identification-of-resources.patch new file mode 100644 index 00000000..e51b94aa --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0018-ivshmem-net-Improve-identification-of-resources.patch @@ -0,0 +1,59 @@ +From d60c7b69f68961c6868ca870d645ed7d4f73e751 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Wed, 21 Dec 2016 08:20:18 +0100 +Subject: [PATCH 18/32] ivshmem-net: Improve identification of resources + +Pass a device name consisting of driver name and PCI ID to request_irq +and alloc_ordered_workqueue. This helps correlating resources with +devices in case there are multiple of them. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 591d04195e57..cff6aa0be71d 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -762,6 +762,7 @@ static int ivshm_net_probe(struct pci_dev *pdev, + resource_size_t shmaddr; + resource_size_t shmlen; + int interrupt; ++ char *device_name; + void *shm; + u32 ivpos; + int err; +@@ -814,7 +815,10 @@ static int ivshm_net_probe(struct pci_dev *pdev, + return -EINVAL; + } + +- dev_info(&pdev->dev, "shared memory size %pa\n", &shmlen); ++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s[%s]", DRV_NAME, ++ dev_name(&pdev->dev)); ++ if (!device_name) ++ return -ENOMEM; + + ndev = alloc_etherdev(sizeof(*in)); + if (!ndev) +@@ -837,7 +841,7 @@ static int ivshm_net_probe(struct pci_dev *pdev, + if (err) + goto err_free; + +- in->state_wq = alloc_ordered_workqueue(DRV_NAME, 0); ++ in->state_wq = alloc_ordered_workqueue(device_name, 0); + if (!in->state_wq) + goto err_free; + +@@ -866,7 +870,7 @@ static int ivshm_net_probe(struct pci_dev *pdev, + in->using_msix = false; + } + +- err = request_irq(interrupt, ivshm_net_int, 0, DRV_NAME, ndev); ++ err = request_irq(interrupt, ivshm_net_int, 0, device_name, ndev); + if (err) + goto err_int; + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0019-ivshmem-net-Switch-to-reset-state-on-each-net-stop-a.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0019-ivshmem-net-Switch-to-reset-state-on-each-net-stop-a.patch new file mode 100644 index 00000000..512bcaf2 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0019-ivshmem-net-Switch-to-reset-state-on-each-net-stop-a.patch @@ -0,0 +1,47 @@ +From a16c80c305b2e11fe3efd0905bbe7db8388bf545 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 1 Jan 2017 15:43:37 +0100 +Subject: [PATCH 19/32] ivshmem-net: Switch to reset state on each net stop and + on driver removal + +Improves the state signaling to the remote side after ifconfig down and +driver removal. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index cff6aa0be71d..09484d652add 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -554,6 +554,8 @@ static void ivshm_net_do_stop(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); + ++ ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); ++ + if (!test_and_clear_bit(IVSHM_NET_FLAG_RUN, &in->flags)) + return; + +@@ -593,7 +595,6 @@ static void ivshm_net_state_change(struct work_struct *work) + } else { + netif_carrier_off(ndev); + ivshm_net_do_stop(ndev); +- ivshm_net_set_state(in, IVSHM_NET_STATE_RESET); + } + break; + } +@@ -899,6 +900,8 @@ static void ivshm_net_remove(struct pci_dev *pdev) + struct net_device *ndev = pci_get_drvdata(pdev); + struct ivshm_net *in = netdev_priv(ndev); + ++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); ++ + if (in->using_msix) { + free_irq(in->msix.vector, ndev); + pci_disable_msix(pdev); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0020-ivshmem-net-Add-ethtool-register-dump.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0020-ivshmem-net-Add-ethtool-register-dump.patch new file mode 100644 index 00000000..d0a562a7 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0020-ivshmem-net-Add-ethtool-register-dump.patch @@ -0,0 +1,61 @@ +From 98f68e69e2e950b44e7324bbcc94700705193443 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 1 Jan 2017 15:46:26 +0100 +Subject: [PATCH 20/32] ivshmem-net: Add ethtool register dump + +Helps debugging inconsistent states. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 09484d652add..c52727ef40c1 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -748,10 +748,41 @@ static void ivshm_net_get_ethtool_stats(struct net_device *ndev, + memset(&in->stats, 0, sizeof(in->stats)); + } + ++#define IVSHM_NET_REGS_LEN (3 * sizeof(u32) + 6 * sizeof(u16)) ++ ++static int ivshm_net_get_regs_len(struct net_device *ndev) ++{ ++ return IVSHM_NET_REGS_LEN; ++} ++ ++static void ivshm_net_get_regs(struct net_device *ndev, ++ struct ethtool_regs *regs, void *p) ++{ ++ struct ivshm_net *in = netdev_priv(ndev); ++ u32 *reg32 = p; ++ u16 *reg16; ++ ++ *reg32++ = in->lstate; ++ *reg32++ = in->rstate; ++ *reg32++ = in->qlen; ++ ++ reg16 = (u16 *)reg32; ++ ++ *reg16++ = in->tx.vr.avail ? in->tx.vr.avail->idx : 0; ++ *reg16++ = in->tx.vr.used ? in->tx.vr.used->idx : 0; ++ *reg16++ = in->tx.vr.avail ? vring_avail_event(&in->tx.vr) : 0; ++ ++ *reg16++ = in->rx.vr.avail ? in->rx.vr.avail->idx : 0; ++ *reg16++ = in->rx.vr.used ? in->rx.vr.used->idx : 0; ++ *reg16++ = in->rx.vr.avail ? vring_avail_event(&in->rx.vr) : 0; ++} ++ + static const struct ethtool_ops ivshm_net_ethtool_ops = { + .get_sset_count = ivshm_net_get_sset_count, + .get_strings = ivshm_net_get_strings, + .get_ethtool_stats = ivshm_net_get_ethtool_stats, ++ .get_regs_len = ivshm_net_get_regs_len, ++ .get_regs = ivshm_net_get_regs, + }; + + static int ivshm_net_probe(struct pci_dev *pdev, +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0021-ivshmem-net-Fix-stuck-state-machine-during-setup.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0021-ivshmem-net-Fix-stuck-state-machine-during-setup.patch new file mode 100644 index 00000000..4e40341e --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0021-ivshmem-net-Fix-stuck-state-machine-during-setup.patch @@ -0,0 +1,30 @@ +From b8d8821cfa8aa53aa29c0b230330afcd79ee7f60 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 1 Jan 2017 15:54:55 +0100 +Subject: [PATCH 21/32] ivshmem-net: Fix stuck state machine during setup + +If the remote side is already in INIT state (or even higher) and has a +cached rstate of RESET, we won't make progress when signaling RESET +again because the remote side won't send a state update. Fix this by +enforcing a local check after probe completion. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index c52727ef40c1..9f307ec4d677 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -911,6 +911,7 @@ static int ivshm_net_probe(struct pci_dev *pdev, + writel(IVSHMEM_INTX_ENABLE, &in->ivshm_regs->intxctrl); + + writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); ++ ivshm_net_check_state(ndev); + + return 0; + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0022-ivshmem-net-Switch-to-relative-descriptor-addresses.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0022-ivshmem-net-Switch-to-relative-descriptor-addresses.patch new file mode 100644 index 00000000..aca00e26 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0022-ivshmem-net-Switch-to-relative-descriptor-addresses.patch @@ -0,0 +1,49 @@ +From d1a0011e9857e105e38d9a0ea6fd7b8e7e6d8dc9 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 3 Jan 2017 08:50:01 +0100 +Subject: [PATCH 22/32] ivshmem-net: Switch to relative descriptor addresses + +Make sure that we do not depend on identity-mapped shared memory +regions. + +This also fixes an off-by-one in the range check of ivshm_net_desc_data. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 9f307ec4d677..0e770ca293a4 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -123,14 +123,14 @@ static void *ivshm_net_desc_data(struct ivshm_net *in, + struct vring_desc *desc, + u32 *len) + { +- u64 addr = READ_ONCE(desc->addr); ++ u64 offs = READ_ONCE(desc->addr); + u32 dlen = READ_ONCE(desc->len); + void *data; + +- if (addr < in->shmaddr || desc->addr > in->shmaddr + in->shmlen) ++ if (offs >= in->shmlen) + return NULL; + +- data = in->shm + (addr - in->shmaddr); ++ data = in->shm + offs; + + if (data < q->data || data >= q->end) + return NULL; +@@ -317,7 +317,7 @@ static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb) + buf = tx->data + head; + skb_copy_and_csum_dev(skb, buf); + +- desc->addr = in->shmaddr + (buf - in->shm); ++ desc->addr = buf - in->shm; + desc->len = skb->len; + + avail = tx->last_avail_idx++ & (vr->num - 1); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0023-ivshmem-net-Switch-to-pci_alloc_irq_vectors.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0023-ivshmem-net-Switch-to-pci_alloc_irq_vectors.patch new file mode 100644 index 00000000..fcc2cac3 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0023-ivshmem-net-Switch-to-pci_alloc_irq_vectors.patch @@ -0,0 +1,146 @@ +From 77920ddb91fa49f7085875d29dd2a2c7e783af3a Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 23 May 2017 17:41:00 +0200 +Subject: [PATCH 23/32] ivshmem-net: Switch to pci_alloc_irq_vectors + +Required by 4.12, and it also simplifies our code. Needs to be folded +into the initial patch eventually. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 66 ++++++++++++++++++++--------------------------- + 1 file changed, 28 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 0e770ca293a4..fd7d78b84576 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -114,8 +114,6 @@ struct ivshm_net { + u32 peer_id; + + struct pci_dev *pdev; +- struct msix_entry msix; +- bool using_msix; + }; + + static void *ivshm_net_desc_data(struct ivshm_net *in, +@@ -793,22 +791,21 @@ static int ivshm_net_probe(struct pci_dev *pdev, + struct ivshmem_regs __iomem *regs; + resource_size_t shmaddr; + resource_size_t shmlen; +- int interrupt; + char *device_name; + void *shm; + u32 ivpos; +- int err; ++ int ret; + +- err = pcim_enable_device(pdev); +- if (err) { +- dev_err(&pdev->dev, "pci_enable_device: %d\n", err); +- return err; ++ ret = pcim_enable_device(pdev); ++ if (ret) { ++ dev_err(&pdev->dev, "pci_enable_device: %d\n", ret); ++ return ret; + } + +- err = pcim_iomap_regions(pdev, BIT(0), DRV_NAME); +- if (err) { +- dev_err(&pdev->dev, "pcim_iomap_regions: %d\n", err); +- return err; ++ ret = pcim_iomap_regions(pdev, BIT(0), DRV_NAME); ++ if (ret) { ++ dev_err(&pdev->dev, "pcim_iomap_regions: %d\n", ret); ++ return ret; + } + + regs = pcim_iomap_table(pdev)[0]; +@@ -869,8 +866,8 @@ static int ivshm_net_probe(struct pci_dev *pdev, + spin_lock_init(&in->tx_free_lock); + spin_lock_init(&in->tx_clean_lock); + +- err = ivshm_net_calc_qsize(ndev); +- if (err) ++ ret = ivshm_net_calc_qsize(ndev); ++ if (ret) + goto err_free; + + in->state_wq = alloc_ordered_workqueue(device_name, 0); +@@ -889,25 +886,21 @@ static int ivshm_net_probe(struct pci_dev *pdev, + netif_carrier_off(ndev); + netif_napi_add(ndev, &in->napi, ivshm_net_poll, NAPI_POLL_WEIGHT); + +- err = register_netdev(ndev); +- if (err) ++ ret = register_netdev(ndev); ++ if (ret) + goto err_wq; + +- err = pci_enable_msix(pdev, &in->msix, 1); +- if (!err) { +- interrupt = in->msix.vector; +- in->using_msix = true; +- } else { +- interrupt = pdev->irq; +- in->using_msix = false; +- } ++ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSIX); ++ if (ret < 0) ++ goto err_alloc_irq; + +- err = request_irq(interrupt, ivshm_net_int, 0, device_name, ndev); +- if (err) +- goto err_int; ++ ret = request_irq(pci_irq_vector(pdev, 0), ivshm_net_int, 0, ++ device_name, ndev); ++ if (ret) ++ goto err_request_irq; + + pci_set_master(pdev); +- if (!in->using_msix) ++ if (!pdev->msix_enabled) + writel(IVSHMEM_INTX_ENABLE, &in->ivshm_regs->intxctrl); + + writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); +@@ -915,16 +908,16 @@ static int ivshm_net_probe(struct pci_dev *pdev, + + return 0; + +-err_int: +- if (in->using_msix) +- pci_disable_msix(pdev); ++err_request_irq: ++ pci_free_irq_vectors(pdev); ++err_alloc_irq: + unregister_netdev(ndev); + err_wq: + destroy_workqueue(in->state_wq); + err_free: + free_netdev(ndev); + +- return err; ++ return ret; + } + + static void ivshm_net_remove(struct pci_dev *pdev) +@@ -934,13 +927,10 @@ static void ivshm_net_remove(struct pci_dev *pdev) + + writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); + +- if (in->using_msix) { +- free_irq(in->msix.vector, ndev); +- pci_disable_msix(pdev); +- } else { ++ if (!pdev->msix_enabled) + writel(0, &in->ivshm_regs->intxctrl); +- free_irq(pdev->irq, ndev); +- } ++ free_irq(pci_irq_vector(pdev, 0), ndev); ++ pci_free_irq_vectors(pdev); + + unregister_netdev(ndev); + cancel_work_sync(&in->state_work); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0024-ivshmem-net-fill-in-and-check-used-descriptor-chain-.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0024-ivshmem-net-fill-in-and-check-used-descriptor-chain-.patch new file mode 100644 index 00000000..7620d682 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0024-ivshmem-net-fill-in-and-check-used-descriptor-chain-.patch @@ -0,0 +1,70 @@ +From be71104c3d4d682f88a4e56e408683b2e8edaef5 Mon Sep 17 00:00:00 2001 +From: Henning Schild <henning.schild@siemens.com> +Date: Mon, 18 Sep 2017 18:02:08 +0200 +Subject: [PATCH 24/32] ivshmem-net: fill in and check used descriptor chain + len + +We are using chains of len==1 make that explicit and expect that from +the remote. + +Signed-off-by: Henning Schild <henning.schild@siemens.com> +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index fd7d78b84576..556670e779e7 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -335,10 +335,10 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); + struct ivshm_net_queue *tx = &in->tx; ++ struct vring_used_elem *used; + struct vring *vr = &tx->vr; + struct vring_desc *desc; + struct vring_desc *fdesc; +- unsigned int used; + unsigned int num; + u16 used_idx; + u16 last; +@@ -358,13 +358,14 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + u32 len; + u32 tail; + +- used = vr->used->ring[last & (vr->num - 1)].id; +- if (used >= vr->num) { +- netdev_err(ndev, "invalid tx used %d\n", used); ++ used = vr->used->ring + (last % vr->num); ++ if (used->id >= vr->num || used->len != 1) { ++ netdev_err(ndev, "invalid tx used->id %d ->len %d\n", ++ used->id, used->len); + break; + } + +- desc = &vr->desc[used]; ++ desc = &vr->desc[used->id]; + + data = ivshm_net_desc_data(in, &in->tx, desc, &len); + if (!data) { +@@ -383,7 +384,7 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + else + desc->next = fhead; + +- fhead = used; ++ fhead = used->id; + last++; + num++; + } +@@ -433,6 +434,7 @@ static void ivshm_net_rx_finish(struct ivshm_net *in, struct vring_desc *desc) + + used = rx->last_used_idx++ & (vr->num - 1); + vr->used->ring[used].id = desc_id; ++ vr->used->ring[used].len = 1; + + virt_store_release(&vr->used->idx, rx->last_used_idx); + } +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0025-ivshmem-net-slightly-improve-debug-output.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0025-ivshmem-net-slightly-improve-debug-output.patch new file mode 100644 index 00000000..cc20c2fd --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0025-ivshmem-net-slightly-improve-debug-output.patch @@ -0,0 +1,29 @@ +From 5776a8083d73bd137a74ab33f0f7652a2c6ee83a Mon Sep 17 00:00:00 2001 +From: Henning Schild <henning.schild@siemens.com> +Date: Mon, 18 Sep 2017 18:02:10 +0200 +Subject: [PATCH 25/32] ivshmem-net: slightly improve debug output + +There where two lines with the same error message, change one of them. + +Signed-off-by: Henning Schild <henning.schild@siemens.com> +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 556670e779e7..abc50553e644 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -369,7 +369,7 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + + data = ivshm_net_desc_data(in, &in->tx, desc, &len); + if (!data) { +- netdev_err(ndev, "bad tx descriptor\n"); ++ netdev_err(ndev, "bad tx descriptor, data == NULL\n"); + break; + } + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0026-ivshmem-net-set-and-check-descriptor-flags.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0026-ivshmem-net-set-and-check-descriptor-flags.patch new file mode 100644 index 00000000..a7d7ab24 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0026-ivshmem-net-set-and-check-descriptor-flags.patch @@ -0,0 +1,43 @@ +From 6b095510765e054c4ee641f115ab72798d97ac21 Mon Sep 17 00:00:00 2001 +From: Henning Schild <henning.schild@siemens.com> +Date: Mon, 18 Sep 2017 18:02:11 +0200 +Subject: [PATCH 26/32] ivshmem-net: set and check descriptor flags + +We do not support the use of any flags. Make sure the remote does not +confuse us using flags. + +Signed-off-by: Henning Schild <henning.schild@siemens.com> +[Jan: Remove wrong removal of next field initialization] +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index abc50553e644..9ecf1d0f0d2d 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -123,8 +123,12 @@ static void *ivshm_net_desc_data(struct ivshm_net *in, + { + u64 offs = READ_ONCE(desc->addr); + u32 dlen = READ_ONCE(desc->len); ++ u16 flags = READ_ONCE(desc->flags); + void *data; + ++ if (flags) ++ return NULL; ++ + if (offs >= in->shmlen) + return NULL; + +@@ -317,6 +321,7 @@ static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb) + + desc->addr = buf - in->shm; + desc->len = skb->len; ++ desc->flags = 0; + + avail = tx->last_avail_idx++ & (vr->num - 1); + vr->avail->ring[avail] = desc_idx; +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0027-ivshmem-net-add-MAC-changing-interface.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0027-ivshmem-net-add-MAC-changing-interface.patch new file mode 100644 index 00000000..61172afd --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0027-ivshmem-net-add-MAC-changing-interface.patch @@ -0,0 +1,41 @@ +From 73b98d39ceacd025ae4aaff1b2cbb537e852a03e Mon Sep 17 00:00:00 2001 +From: Henning Schild <henning.schild@siemens.com> +Date: Wed, 27 Sep 2017 12:59:49 +0200 +Subject: [PATCH 27/32] ivshmem-net: add MAC changing interface + +Allow ifconfig, ip and other such tools to change the MAC of the +virtual NIC. + +Signed-off-by: Henning Schild <henning.schild@siemens.com> +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 9ecf1d0f0d2d..712dde0ee6f9 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -685,12 +685,14 @@ static void ivshm_net_poll_controller(struct net_device *ndev) + #endif + + static const struct net_device_ops ivshm_net_ops = { +- .ndo_open = ivshm_net_open, +- .ndo_stop = ivshm_net_stop, +- .ndo_start_xmit = ivshm_net_xmit, +- .ndo_change_mtu = ivshm_net_change_mtu, ++ .ndo_open = ivshm_net_open, ++ .ndo_stop = ivshm_net_stop, ++ .ndo_start_xmit = ivshm_net_xmit, ++ .ndo_change_mtu = ivshm_net_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, + #ifdef CONFIG_NET_POLL_CONTROLLER +- .ndo_poll_controller = ivshm_net_poll_controller, ++ .ndo_poll_controller = ivshm_net_poll_controller, + #endif + }; + +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0028-ivshmem-net-Silence-compiler-warning.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0028-ivshmem-net-Silence-compiler-warning.patch new file mode 100644 index 00000000..6a1c212f --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0028-ivshmem-net-Silence-compiler-warning.patch @@ -0,0 +1,28 @@ +From fbefa0a6dcc6011aef4444c771f300c40fca30f1 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Tue, 3 Oct 2017 12:24:59 +0200 +Subject: [PATCH 28/32] ivshmem-net: Silence compiler warning + +At least Linaro's gcc 6.3 does not see the initialization and usage +dependency of fhead and num. Let's silence this false positive. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 712dde0ee6f9..6fa7a6c81bf1 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -356,6 +356,7 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + last = tx->last_used_idx; + + fdesc = NULL; ++ fhead = 0; + num = 0; + + while (last != used_idx) { +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0029-ivshmem-net-Fix-bogus-transition-to-RESET-state.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0029-ivshmem-net-Fix-bogus-transition-to-RESET-state.patch new file mode 100644 index 00000000..335c6bdb --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0029-ivshmem-net-Fix-bogus-transition-to-RESET-state.patch @@ -0,0 +1,31 @@ +From 1a0998da61deead1dbb38393fedaefee69f59044 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 4 Mar 2018 13:16:04 +0100 +Subject: [PATCH 29/32] ivshmem-net: Fix bogus transition to RESET state + +If we are in READY but the remote is still in INIT, we so far fell back +to RESET which caused the setup to get stuck. Fix this by only +transitioning from READY/RUN to RESET in ivshm_net_state_change if the +remote is in RESET as well. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 6fa7a6c81bf1..0c503194b4ec 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -598,7 +598,7 @@ static void ivshm_net_state_change(struct work_struct *work) + if (rstate >= IVSHM_NET_STATE_READY) { + netif_carrier_on(ndev); + ivshm_net_run(ndev); +- } else { ++ } else if (rstate == IVSHM_NET_STATE_RESET) { + netif_carrier_off(ndev); + ivshm_net_do_stop(ndev); + } +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0030-ivshmem-net-Refactor-and-comment-ivshm_net_state_cha.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0030-ivshmem-net-Refactor-and-comment-ivshm_net_state_cha.patch new file mode 100644 index 00000000..42ca05a0 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0030-ivshmem-net-Refactor-and-comment-ivshm_net_state_cha.patch @@ -0,0 +1,68 @@ +From a3ee1ba8e4948ac4e6e4eae3061b72b3bf867122 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 4 Mar 2018 13:50:24 +0100 +Subject: [PATCH 30/32] ivshmem-net: Refactor and comment + ivshm_net_state_change + +This should make the state transitioning logic clearer. Also avoid the +harmless but redundant netif_carrier_on/ivshm_net_run in RUN state. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 0c503194b4ec..aba77c232c48 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -575,14 +575,21 @@ static void ivshm_net_state_change(struct work_struct *work) + struct net_device *ndev = in->napi.dev; + u32 rstate = readl(&in->ivshm_regs->rstate); + +- + switch (in->lstate) { + case IVSHM_NET_STATE_RESET: ++ /* ++ * Wait for the remote to leave READY/RUN before transitioning ++ * to INIT. ++ */ + if (rstate < IVSHM_NET_STATE_READY) + ivshm_net_set_state(in, IVSHM_NET_STATE_INIT); + break; + + case IVSHM_NET_STATE_INIT: ++ /* ++ * Wait for the remote to leave RESET before performing the ++ * initialization and moving to READY. ++ */ + if (rstate > IVSHM_NET_STATE_RESET) { + ivshm_net_init_queues(ndev); + ivshm_net_set_state(in, IVSHM_NET_STATE_READY); +@@ -594,11 +601,21 @@ static void ivshm_net_state_change(struct work_struct *work) + break; + + case IVSHM_NET_STATE_READY: +- case IVSHM_NET_STATE_RUN: ++ /* ++ * Link is up and we are running once the remote is in READY or ++ * RUN. ++ */ + if (rstate >= IVSHM_NET_STATE_READY) { + netif_carrier_on(ndev); + ivshm_net_run(ndev); +- } else if (rstate == IVSHM_NET_STATE_RESET) { ++ break; ++ } ++ /* fall through */ ++ case IVSHM_NET_STATE_RUN: ++ /* ++ * If the remote goes to RESET, we need to follow immediately. ++ */ ++ if (rstate == IVSHM_NET_STATE_RESET) { + netif_carrier_off(ndev); + ivshm_net_do_stop(ndev); + } +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0031-ivshmem-net-Switch-to-netdev_xmit_more-helper.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0031-ivshmem-net-Switch-to-netdev_xmit_more-helper.patch new file mode 100644 index 00000000..bedbc59f --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0031-ivshmem-net-Switch-to-netdev_xmit_more-helper.patch @@ -0,0 +1,59 @@ +From 6c86f9ef9fa5029b8f87867f47fe51d6cc1960a5 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Sun, 2 Jun 2019 11:58:20 +0200 +Subject: [PATCH 31/32] ivshmem-net: Switch to netdev_xmit_more helper + +The skb field has been removed by 4f296edeb9d4. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index aba77c232c48..9946cef63c1f 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -294,7 +294,8 @@ static u32 ivshm_net_tx_advance(struct ivshm_net_queue *q, u32 *pos, u32 len) + return p; + } + +-static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb) ++static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb, ++ bool xmit_more) + { + struct ivshm_net *in = netdev_priv(ndev); + struct ivshm_net_queue *tx = &in->tx; +@@ -327,7 +328,7 @@ static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb) + vr->avail->ring[avail] = desc_idx; + tx->num_added++; + +- if (!skb->xmit_more) { ++ if (!xmit_more) { + virt_store_release(&vr->avail->idx, tx->last_avail_idx); + ivshm_net_notify_tx(in, tx->num_added); + tx->num_added = 0; +@@ -509,17 +510,18 @@ static int ivshm_net_poll(struct napi_struct *napi, int budget) + static netdev_tx_t ivshm_net_xmit(struct sk_buff *skb, struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); ++ bool xmit_more = netdev_xmit_more(); + + ivshm_net_tx_clean(ndev); + + if (!ivshm_net_tx_ok(in, ndev->mtu)) { + ivshm_net_enable_tx_irq(in); + netif_stop_queue(ndev); +- skb->xmit_more = 0; ++ xmit_more = false; + in->stats.tx_pause++; + } + +- ivshm_net_tx_frame(ndev, skb); ++ ivshm_net_tx_frame(ndev, skb, xmit_more); + + in->stats.tx_packets++; + ndev->stats.tx_packets++; +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch b/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch new file mode 100644 index 00000000..dc10cd2f --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch @@ -0,0 +1,650 @@ +From 9caa6a8cab0d7f46475990aaeb7dcc7721547ef0 Mon Sep 17 00:00:00 2001 +From: Jan Kiszka <jan.kiszka@siemens.com> +Date: Mon, 5 Dec 2016 15:43:53 +0100 +Subject: [PATCH 32/32] ivshmem-net: Adjust to reworked version of ivshmem in + Jailhouse + +This contains the changes required to work with the new revision of +ivshmem in Jailhouse, namely: + +- changed PCI vendor and device ID +- vendor capability to communicate region location +- new MMIO register layout +- common interrupt control register +- state table support, removal of rstate register +- unidirectional shared memory regions +- vector value has to be written to doorbell register +- support for multiple vectors, used to split config from tx-rx + +Note: Specification work for the interface is ongoing, so details may +still change. + +Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> +--- + drivers/net/ivshmem-net.c | 335 ++++++++++++++++++++++++++++++---------------- + 1 file changed, 223 insertions(+), 112 deletions(-) + +diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c +index 9946cef63c1f..18d5a15dbec2 100644 +--- a/drivers/net/ivshmem-net.c ++++ b/drivers/net/ivshmem-net.c +@@ -1,5 +1,6 @@ + /* + * Copyright 2016 Mans Rullgard <mans@mansr.com> ++ * Copyright (c) Siemens AG, 2016-2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -15,6 +16,7 @@ + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + ++#include <linux/ivshmem.h> + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> +@@ -28,34 +30,28 @@ + + #define DRV_NAME "ivshmem-net" + +-#define JAILHOUSE_CFG_SHMEM_PTR 0x40 +-#define JAILHOUSE_CFG_SHMEM_SZ 0x48 ++#define IVSHM_NET_STATE_RESET 0 ++#define IVSHM_NET_STATE_INIT 1 ++#define IVSHM_NET_STATE_READY 2 ++#define IVSHM_NET_STATE_RUN 3 + +-#define IVSHMEM_INTX_ENABLE 0x1 ++#define IVSHM_NET_FLAG_RUN 0 + +-#define IVSHM_NET_STATE_RESET 0 +-#define IVSHM_NET_STATE_INIT 1 +-#define IVSHM_NET_STATE_READY 2 +-#define IVSHM_NET_STATE_RUN 3 +- +-#define IVSHM_NET_FLAG_RUN 0 +- +-#define IVSHM_NET_MTU_MIN 256 +-#define IVSHM_NET_MTU_MAX 65535 +-#define IVSHM_NET_MTU_DEF 16384 ++#define IVSHM_NET_MTU_MIN 256 ++#define IVSHM_NET_MTU_MAX 65535 ++#define IVSHM_NET_MTU_DEF 16384 + + #define IVSHM_NET_FRAME_SIZE(s) ALIGN(18 + (s), SMP_CACHE_BYTES) + + #define IVSHM_NET_VQ_ALIGN 64 + +-struct ivshmem_regs { +- u32 intxctrl; +- u32 istat; +- u32 ivpos; +- u32 doorbell; +- u32 lstate; +- u32 rstate; +-}; ++#define IVSHM_NET_SECTION_TX 0 ++#define IVSHM_NET_SECTION_RX 1 ++ ++#define IVSHM_NET_MSIX_STATE 0 ++#define IVSHM_NET_MSIX_TX_RX 1 ++ ++#define IVSHM_NET_NUM_VECTORS 2 + + struct ivshm_net_queue { + struct vring vr; +@@ -73,7 +69,7 @@ struct ivshm_net_queue { + }; + + struct ivshm_net_stats { +- u32 interrupts; ++ u32 tx_rx_interrupts; + u32 tx_packets; + u32 tx_notify; + u32 tx_pause; +@@ -97,8 +93,9 @@ struct ivshm_net { + + struct napi_struct napi; + +- u32 lstate; +- u32 rstate; ++ u32 state; ++ u32 last_peer_state; ++ u32 *state_table; + + unsigned long flags; + +@@ -107,17 +104,19 @@ struct ivshm_net { + + struct ivshm_net_stats stats; + +- struct ivshmem_regs __iomem *ivshm_regs; +- void *shm; +- phys_addr_t shmaddr; ++ struct ivshm_regs __iomem *ivshm_regs; ++ void *shm[2]; + resource_size_t shmlen; + u32 peer_id; + ++ u32 tx_rx_vector; ++ + struct pci_dev *pdev; + }; + + static void *ivshm_net_desc_data(struct ivshm_net *in, + struct ivshm_net_queue *q, ++ unsigned int region, + struct vring_desc *desc, + u32 *len) + { +@@ -132,7 +131,7 @@ static void *ivshm_net_desc_data(struct ivshm_net *in, + if (offs >= in->shmlen) + return NULL; + +- data = in->shm + offs; ++ data = in->shm[region] + offs; + + if (data < q->data || data >= q->end) + return NULL; +@@ -160,18 +159,17 @@ static void ivshm_net_init_queue(struct ivshm_net *in, + static void ivshm_net_init_queues(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); +- int ivpos = readl(&in->ivshm_regs->ivpos); + void *tx; + void *rx; + int i; + +- tx = in->shm + ivpos * in->shmlen / 2; +- rx = in->shm + !ivpos * in->shmlen / 2; ++ tx = in->shm[IVSHM_NET_SECTION_TX]; ++ rx = in->shm[IVSHM_NET_SECTION_RX]; + +- memset(tx, 0, in->shmlen / 2); ++ memset(tx, 0, in->shmlen); + +- ivshm_net_init_queue(in, &in->rx, rx, in->qlen); + ivshm_net_init_queue(in, &in->tx, tx, in->qlen); ++ ivshm_net_init_queue(in, &in->rx, rx, in->qlen); + + swap(in->rx.vr.used, in->tx.vr.used); + +@@ -191,14 +189,14 @@ static int ivshm_net_calc_qsize(struct net_device *ndev) + for (qlen = 4096; qlen > 32; qlen >>= 1) { + vrsize = vring_size(qlen, IVSHM_NET_VQ_ALIGN); + vrsize = ALIGN(vrsize, IVSHM_NET_VQ_ALIGN); +- if (vrsize < in->shmlen / 16) ++ if (vrsize < in->shmlen / 8) + break; + } + +- if (vrsize > in->shmlen / 2) ++ if (vrsize > in->shmlen) + return -EINVAL; + +- qsize = in->shmlen / 2 - vrsize; ++ qsize = in->shmlen - vrsize; + + if (qsize < 4 * IVSHM_NET_MTU_MIN) + return -EINVAL; +@@ -221,7 +219,8 @@ static void ivshm_net_notify_tx(struct ivshm_net *in, unsigned int num) + new = in->tx.last_avail_idx; + + if (vring_need_event(evt, new, old)) { +- writel(in->peer_id << 16, &in->ivshm_regs->doorbell); ++ writel(in->tx_rx_vector | (in->peer_id << 16), ++ &in->ivshm_regs->doorbell); + in->stats.tx_notify++; + } + } +@@ -243,7 +242,8 @@ static void ivshm_net_notify_rx(struct ivshm_net *in, unsigned int num) + new = in->rx.last_used_idx; + + if (vring_need_event(evt, new, old)) { +- writel(in->peer_id << 16, &in->ivshm_regs->doorbell); ++ writel(in->tx_rx_vector | (in->peer_id << 16), ++ &in->ivshm_regs->doorbell); + in->stats.rx_notify++; + } + } +@@ -320,7 +320,7 @@ static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb, + buf = tx->data + head; + skb_copy_and_csum_dev(skb, buf); + +- desc->addr = buf - in->shm; ++ desc->addr = buf - in->shm[IVSHM_NET_SECTION_TX]; + desc->len = skb->len; + desc->flags = 0; + +@@ -374,7 +374,8 @@ static void ivshm_net_tx_clean(struct net_device *ndev) + + desc = &vr->desc[used->id]; + +- data = ivshm_net_desc_data(in, &in->tx, desc, &len); ++ data = ivshm_net_desc_data(in, &in->tx, IVSHM_NET_SECTION_TX, ++ desc, &len); + if (!data) { + netdev_err(ndev, "bad tx descriptor, data == NULL\n"); + break; +@@ -466,7 +467,8 @@ static int ivshm_net_poll(struct napi_struct *napi, int budget) + if (!desc) + break; + +- data = ivshm_net_desc_data(in, &in->rx, desc, &len); ++ data = ivshm_net_desc_data(in, &in->rx, IVSHM_NET_SECTION_RX, ++ desc, &len); + if (!data) { + netdev_err(ndev, "bad rx descriptor\n"); + break; +@@ -535,15 +537,15 @@ static netdev_tx_t ivshm_net_xmit(struct sk_buff *skb, struct net_device *ndev) + static void ivshm_net_set_state(struct ivshm_net *in, u32 state) + { + virt_wmb(); +- WRITE_ONCE(in->lstate, state); +- writel(state, &in->ivshm_regs->lstate); ++ WRITE_ONCE(in->state, state); ++ writel(state, &in->ivshm_regs->state); + } + + static void ivshm_net_run(struct net_device *ndev) + { + struct ivshm_net *in = netdev_priv(ndev); + +- if (in->lstate < IVSHM_NET_STATE_READY) ++ if (in->state < IVSHM_NET_STATE_READY) + return; + + if (!netif_running(ndev)) +@@ -575,15 +577,15 @@ static void ivshm_net_state_change(struct work_struct *work) + { + struct ivshm_net *in = container_of(work, struct ivshm_net, state_work); + struct net_device *ndev = in->napi.dev; +- u32 rstate = readl(&in->ivshm_regs->rstate); ++ u32 peer_state = READ_ONCE(in->state_table[in->peer_id]); + +- switch (in->lstate) { ++ switch (in->state) { + case IVSHM_NET_STATE_RESET: + /* + * Wait for the remote to leave READY/RUN before transitioning + * to INIT. + */ +- if (rstate < IVSHM_NET_STATE_READY) ++ if (peer_state < IVSHM_NET_STATE_READY) + ivshm_net_set_state(in, IVSHM_NET_STATE_INIT); + break; + +@@ -592,7 +594,7 @@ static void ivshm_net_state_change(struct work_struct *work) + * Wait for the remote to leave RESET before performing the + * initialization and moving to READY. + */ +- if (rstate > IVSHM_NET_STATE_RESET) { ++ if (peer_state > IVSHM_NET_STATE_RESET) { + ivshm_net_init_queues(ndev); + ivshm_net_set_state(in, IVSHM_NET_STATE_READY); + +@@ -607,7 +609,7 @@ static void ivshm_net_state_change(struct work_struct *work) + * Link is up and we are running once the remote is in READY or + * RUN. + */ +- if (rstate >= IVSHM_NET_STATE_READY) { ++ if (peer_state >= IVSHM_NET_STATE_READY) { + netif_carrier_on(ndev); + ivshm_net_run(ndev); + break; +@@ -617,7 +619,7 @@ static void ivshm_net_state_change(struct work_struct *work) + /* + * If the remote goes to RESET, we need to follow immediately. + */ +- if (rstate == IVSHM_NET_STATE_RESET) { ++ if (peer_state == IVSHM_NET_STATE_RESET) { + netif_carrier_off(ndev); + ivshm_net_do_stop(ndev); + } +@@ -625,31 +627,44 @@ static void ivshm_net_state_change(struct work_struct *work) + } + + virt_wmb(); +- WRITE_ONCE(in->rstate, rstate); ++ WRITE_ONCE(in->last_peer_state, peer_state); + } + +-static void ivshm_net_check_state(struct net_device *ndev) ++static void ivshm_net_check_state(struct ivshm_net *in) + { +- struct ivshm_net *in = netdev_priv(ndev); +- u32 rstate = readl(&in->ivshm_regs->rstate); +- +- if (rstate != in->rstate || !test_bit(IVSHM_NET_FLAG_RUN, &in->flags)) ++ if (in->state_table[in->peer_id] != in->last_peer_state || ++ !test_bit(IVSHM_NET_FLAG_RUN, &in->flags)) + queue_work(in->state_wq, &in->state_work); + } + +-static irqreturn_t ivshm_net_int(int irq, void *data) ++static irqreturn_t ivshm_net_int_state(int irq, void *data) + { +- struct net_device *ndev = data; +- struct ivshm_net *in = netdev_priv(ndev); ++ struct ivshm_net *in = data; ++ ++ ivshm_net_check_state(in); ++ ++ return IRQ_HANDLED; ++} + +- in->stats.interrupts++; ++static irqreturn_t ivshm_net_int_tx_rx(int irq, void *data) ++{ ++ struct ivshm_net *in = data; ++ ++ in->stats.tx_rx_interrupts++; + +- ivshm_net_check_state(ndev); + napi_schedule_irqoff(&in->napi); + + return IRQ_HANDLED; + } + ++static irqreturn_t ivshm_net_intx(int irq, void *data) ++{ ++ ivshm_net_int_state(irq, data); ++ ivshm_net_int_tx_rx(irq, data); ++ ++ return IRQ_HANDLED; ++} ++ + static int ivshm_net_open(struct net_device *ndev) + { + netdev_reset_queue(ndev); +@@ -717,7 +732,7 @@ static const struct net_device_ops ivshm_net_ops = { + }; + + static const char ivshm_net_stats[][ETH_GSTRING_LEN] = { +- "interrupts", ++ "tx_rx_interrupts", + "tx_packets", + "tx_notify", + "tx_pause", +@@ -760,7 +775,7 @@ static void ivshm_net_get_ethtool_stats(struct net_device *ndev, + unsigned int n = 0; + unsigned int i; + +- st[n++] = in->stats.interrupts; ++ st[n++] = in->stats.tx_rx_interrupts; + st[n++] = in->stats.tx_packets; + st[n++] = in->stats.tx_notify; + st[n++] = in->stats.tx_pause; +@@ -789,8 +804,8 @@ static void ivshm_net_get_regs(struct net_device *ndev, + u32 *reg32 = p; + u16 *reg16; + +- *reg32++ = in->lstate; +- *reg32++ = in->rstate; ++ *reg32++ = in->state; ++ *reg32++ = in->last_peer_state; + *reg32++ = in->qlen; + + reg16 = (u16 *)reg32; +@@ -812,17 +827,28 @@ static const struct ethtool_ops ivshm_net_ethtool_ops = { + .get_regs = ivshm_net_get_regs, + }; + ++static u64 get_config_qword(struct pci_dev *pdev, unsigned int pos) ++{ ++ u32 lo, hi; ++ ++ pci_read_config_dword(pdev, pos, &lo); ++ pci_read_config_dword(pdev, pos + 4, &hi); ++ return lo | ((u64)hi << 32); ++} ++ + static int ivshm_net_probe(struct pci_dev *pdev, +- const struct pci_device_id *id) ++ const struct pci_device_id *pci_id) + { ++ phys_addr_t output_sections_addr, section_addr; ++ resource_size_t section_sz, output_section_sz; ++ void *state_table, *output_sections; ++ struct ivshm_regs __iomem *regs; + struct net_device *ndev; + struct ivshm_net *in; +- struct ivshmem_regs __iomem *regs; +- resource_size_t shmaddr; +- resource_size_t shmlen; ++ unsigned int cap_pos; + char *device_name; +- void *shm; +- u32 ivpos; ++ int vendor_cap; ++ u32 id, dword; + int ret; + + ret = pcim_enable_device(pdev); +@@ -839,40 +865,75 @@ static int ivshm_net_probe(struct pci_dev *pdev, + + regs = pcim_iomap_table(pdev)[0]; + +- shmlen = pci_resource_len(pdev, 2); ++ id = readl(®s->id); ++ if (id > 1) { ++ dev_err(&pdev->dev, "invalid ID %d\n", id); ++ return -EINVAL; ++ } ++ if (readl(®s->max_peers) > 2) { ++ dev_err(&pdev->dev, "only 2 peers supported\n"); ++ return -EINVAL; ++ } ++ ++ vendor_cap = pci_find_capability(pdev, PCI_CAP_ID_VNDR); ++ if (vendor_cap < 0) { ++ dev_err(&pdev->dev, "missing vendor capability\n"); ++ return -EINVAL; ++ } + +- if (shmlen) { +- shmaddr = pci_resource_start(pdev, 2); ++ if (pci_resource_len(pdev, 2) > 0) { ++ section_addr = pci_resource_start(pdev, 2); + } else { +- union { u64 v; u32 hl[2]; } val; +- +- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR, +- &val.hl[0]); +- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR + 4, +- &val.hl[1]); +- shmaddr = val.v; +- +- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ, +- &val.hl[0]); +- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ + 4, +- &val.hl[1]); +- shmlen = val.v; ++ cap_pos = vendor_cap + IVSHM_CFG_ADDRESS; ++ section_addr = get_config_qword(pdev, cap_pos); + } + ++ cap_pos = vendor_cap + IVSHM_CFG_STATE_TAB_SZ; ++ pci_read_config_dword(pdev, cap_pos, &dword); ++ section_sz = dword; + +- if (!devm_request_mem_region(&pdev->dev, shmaddr, shmlen, DRV_NAME)) ++ if (!devm_request_mem_region(&pdev->dev, section_addr, section_sz, ++ DRV_NAME)) + return -EBUSY; + +- shm = devm_memremap(&pdev->dev, shmaddr, shmlen, MEMREMAP_WB); +- if (!shm) ++ state_table = devm_memremap(&pdev->dev, section_addr, section_sz, ++ MEMREMAP_WB); ++ if (!state_table) + return -ENOMEM; + +- ivpos = readl(®s->ivpos); +- if (ivpos > 1) { +- dev_err(&pdev->dev, "invalid IVPosition %d\n", ivpos); ++ output_sections_addr = section_addr + section_sz; ++ ++ cap_pos = vendor_cap + IVSHM_CFG_RW_SECTION_SZ; ++ section_sz = get_config_qword(pdev, cap_pos); ++ if (section_sz > 0) { ++ dev_info(&pdev->dev, "R/W section detected - " ++ "unused by this driver version\n"); ++ output_sections_addr += section_sz; ++ } ++ ++ cap_pos = vendor_cap + IVSHM_CFG_OUTPUT_SECTION_SZ; ++ output_section_sz = get_config_qword(pdev, cap_pos); ++ if (output_section_sz == 0) { ++ dev_err(&pdev->dev, "Missing input/output sections\n"); + return -EINVAL; + } + ++ if (!devm_request_mem_region(&pdev->dev, output_sections_addr, ++ output_section_sz * 2, DRV_NAME)) ++ return -EBUSY; ++ ++ output_sections = devm_memremap(&pdev->dev, output_sections_addr, ++ output_section_sz * 2, MEMREMAP_WB); ++ if (!output_sections) ++ return -ENOMEM; ++ ++ section_addr = output_sections_addr + output_section_sz * id; ++ dev_info(&pdev->dev, "TX memory at %pa, size %pa\n", ++ §ion_addr, &output_section_sz); ++ section_addr = output_sections_addr + output_section_sz * !id; ++ dev_info(&pdev->dev, "RX memory at %pa, size %pa\n", ++ §ion_addr, &output_section_sz); ++ + device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s[%s]", DRV_NAME, + dev_name(&pdev->dev)); + if (!device_name) +@@ -887,10 +948,16 @@ static int ivshm_net_probe(struct pci_dev *pdev, + + in = netdev_priv(ndev); + in->ivshm_regs = regs; +- in->shm = shm; +- in->shmaddr = shmaddr; +- in->shmlen = shmlen; +- in->peer_id = !ivpos; ++ in->state_table = state_table; ++ ++ in->shm[IVSHM_NET_SECTION_TX] = ++ output_sections + output_section_sz * id; ++ in->shm[IVSHM_NET_SECTION_RX] = ++ output_sections + output_section_sz * !id; ++ ++ in->shmlen = output_section_sz; ++ ++ in->peer_id = !id; + in->pdev = pdev; + spin_lock_init(&in->tx_free_lock); + spin_lock_init(&in->tx_clean_lock); +@@ -919,24 +986,64 @@ static int ivshm_net_probe(struct pci_dev *pdev, + if (ret) + goto err_wq; + +- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSIX); ++ ret = pci_alloc_irq_vectors(pdev, 1, 2, PCI_IRQ_LEGACY | PCI_IRQ_MSIX); + if (ret < 0) + goto err_alloc_irq; + +- ret = request_irq(pci_irq_vector(pdev, 0), ivshm_net_int, 0, +- device_name, ndev); +- if (ret) +- goto err_request_irq; ++ if (pdev->msix_enabled) { ++ if (ret != 2) { ++ ret = -EBUSY; ++ goto err_request_irq; ++ } ++ ++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, ++ "%s-state[%s]", DRV_NAME, ++ dev_name(&pdev->dev)); ++ if (!device_name) { ++ ret = -ENOMEM; ++ goto err_request_irq; ++ } ++ ++ ret = request_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE), ++ ivshm_net_int_state, 0, device_name, in); ++ if (ret) ++ goto err_request_irq; ++ ++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, ++ "%s-tx-rx[%s]", DRV_NAME, ++ dev_name(&pdev->dev)); ++ if (!device_name) { ++ ret = -ENOMEM; ++ goto err_request_irq2; ++ } ++ ++ ret = request_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_TX_RX), ++ ivshm_net_int_tx_rx, 0, device_name, in); ++ if (ret) ++ goto err_request_irq2; ++ ++ in->tx_rx_vector = IVSHM_NET_MSIX_TX_RX; ++ } else { ++ ret = request_irq(pci_irq_vector(pdev, 0), ivshm_net_intx, 0, ++ device_name, in); ++ if (ret) ++ goto err_request_irq; ++ ++ in->tx_rx_vector = 0; ++ } + + pci_set_master(pdev); +- if (!pdev->msix_enabled) +- writel(IVSHMEM_INTX_ENABLE, &in->ivshm_regs->intxctrl); + +- writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); +- ivshm_net_check_state(ndev); ++ pci_write_config_byte(pdev, vendor_cap + IVSHM_CFG_PRIV_CNTL, 0); ++ writel(IVSHM_INT_ENABLE, &in->ivshm_regs->int_control); ++ ++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->state); ++ ivshm_net_check_state(in); + + return 0; + ++err_request_irq2: ++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE), in); + err_request_irq: + pci_free_irq_vectors(pdev); + err_alloc_irq: +@@ -954,11 +1061,15 @@ static void ivshm_net_remove(struct pci_dev *pdev) + struct net_device *ndev = pci_get_drvdata(pdev); + struct ivshm_net *in = netdev_priv(ndev); + +- writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate); ++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->state); ++ writel(0, &in->ivshm_regs->int_control); + +- if (!pdev->msix_enabled) +- writel(0, &in->ivshm_regs->intxctrl); +- free_irq(pci_irq_vector(pdev, 0), ndev); ++ if (pdev->msix_enabled) { ++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE), in); ++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_TX_RX), in); ++ } else { ++ free_irq(pci_irq_vector(pdev, 0), in); ++ } + pci_free_irq_vectors(pdev); + + unregister_netdev(ndev); +@@ -968,8 +1079,8 @@ static void ivshm_net_remove(struct pci_dev *pdev) + } + + static const struct pci_device_id ivshm_net_id_table[] = { +- { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1110), +- (PCI_CLASS_OTHERS << 16) | (0x01 << 8), 0xffff00 }, ++ { PCI_DEVICE(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_IVSHMEM), ++ (PCI_CLASS_OTHERS << 16) | IVSHM_PROTO_NET, 0xffffff }, + { 0 } + }; + MODULE_DEVICE_TABLE(pci, ivshm_net_id_table); +-- +2.11.0 + diff --git a/meta-agl-jailhouse/recipes-kernel/linux/linux/jailhouse.cfg b/meta-agl-jailhouse/recipes-kernel/linux/linux/jailhouse.cfg new file mode 100644 index 00000000..d417e556 --- /dev/null +++ b/meta-agl-jailhouse/recipes-kernel/linux/linux/jailhouse.cfg @@ -0,0 +1,10 @@ +CONFIG_HOTPLUG_CPU=y +CONFIG_PCI_HOST_GENERIC=y + +CONFIG_HOTPLUG_PCI=y +CONFIG_IVSHMEM_NET=y + +CONFIG_UIO=y +CONFIG_UIO_IVSHMEM=y +CONFIG_VIRT_DRIVERS=y +CONFIG_JAILHOUSE_DBGCON=y |