diff options
author | Jakub Luzny <jakub@luzny.cz> | 2020-07-19 17:00:08 +0200 |
---|---|---|
committer | Jakub Luzny <jakub@luzny.cz> | 2020-07-24 12:46:57 +0200 |
commit | 3f6d193683449a323a3f1d689540ab697d3d7980 (patch) | |
tree | 093ad36c5bf3f031d469a635d0f2190a57c176e0 /meta-agl-jailhouse/recipes-kernel/linux | |
parent | c6889de97c1af6baa7ee8006aa75da170c9bd407 (diff) |
Add layer to support Jailhouse hypervisorjellyfish_9.99.2jellyfish/9.99.29.99.2
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.
This layer adds the Jailhouse package into AGL and also appends the BSPs
to allow it to run. Currently, Raspberry Pi 4 and QEMU x86-64 targets
are supported.
To enable Jailhouse and include it in the image, the AGL feature
agl-jailhouse must be enabled.
Bug-AGL: SPEC-3507
Signed-off-by: Jakub Luzny <jakub@luzny.cz>
Change-Id: I0fbc0b5d931c85d6f22b0222da8c2b106c4115e1
Diffstat (limited to 'meta-agl-jailhouse/recipes-kernel/linux')
35 files changed, 3889 insertions, 0 deletions
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 |