diff --git a/share/man/man4/pv.4 b/share/man/man4/pv.4 new file mode 100644 index 00000000000..f25ba4a2475 --- /dev/null +++ b/share/man/man4/pv.4 @@ -0,0 +1,44 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2024 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" ported from OpenBSD +.Dd January 2024 +.Dt PV 4 +.Os +.Sh NAME +.Nm pv +.Nd paravirtual device tree root +.Sh SYNOPSIS +.Cd "pv* at pvbus?" +.Sh DESCRIPTION +.Nm +driver is used on virtual machines that are running on hypervisors. +It provides a pseudo-bus for all paravirtual devices that do not +attach to a well-known bus like +.Xr pci 4 . +.Sh SEE ALSO +.Xr virtio 4 , +.Xr mmio_virtio 4 diff --git a/share/man/man4/virtio_mmio.4 b/share/man/man4/virtio_mmio.4 new file mode 100644 index 00000000000..6f92cb3fdc4 --- /dev/null +++ b/share/man/man4/virtio_mmio.4 @@ -0,0 +1,75 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2024 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd January 2024 +.Dt VIRTIO_MMIO 4 +.Os NetBSD +.Sh NAME +.Nm virtio_mmio +.Nd VirtIO over memory mapped device. +.Sh SYNOPSIS +.Cd "pv* at pvbus?" +.Cd "virtio* at pv?" +.Pp +.Cd "acpi0 at mainbus0" +.Cd "virtio* at acpi?" +.Sh DESCRIPTION +.Nm +can be used in virtual environments without +.Xr pci 4 +support (a common situation in embedded devices models) might use simple +memory mapped device ( +.Nm +) instead of the +.Xr pci 4 +device. +.Pp +The memory mapped +.Xr virtio 4 +device behaviour is based on the +.Xr pci 4 +device specification. Therefore most operations including device initialization, +queues configuration and buffer transfers are nearly identical. +.Pp +Unlike +.Xr pci 4 +, +.Nm +provides no generic device discovery mechanism. For each device, the guest OS will +need to know the location of the registers and interrupt(s) used. +.Pp +Device location can be read from either +.Xr acpi 4 +or via kernel command line parameters, implemented as a +.Xr pv 4 +virtual device. +.Sh SEE ALSO +.Xr virtio 4 +.Pp +.Rs +.%T Virtual I/O Device (VIRTIO) Version 1.2 +.%U https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html +.Re diff --git a/sys/arch/amd64/amd64/amd64_mainbus.c b/sys/arch/amd64/amd64/amd64_mainbus.c index 8a1bb0c55c6..f9d1118a54f 100644 --- a/sys/arch/amd64/amd64/amd64_mainbus.c +++ b/sys/arch/amd64/amd64/amd64_mainbus.c @@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: amd64_mainbus.c,v 1.7 2021/08/07 16:18:41 thorpej Ex #include "isadma.h" #include "acpica.h" #include "ipmi.h" +#include "pvbus.h" #include "opt_acpi.h" #include "opt_mpbios.h" @@ -79,6 +80,9 @@ __KERNEL_RCSID(0, "$NetBSD: amd64_mainbus.c,v 1.7 2021/08/07 16:18:41 thorpej Ex #include <arch/x86/pci/msipic.h> #endif /* __HAVE_PCI_MSI_MSIX */ #endif +#if NPVBUS > 0 +#include <dev/pv/pvvar.h> +#endif /* * XXXfvdl ACPI @@ -100,6 +104,9 @@ union amd64_mainbus_attach_args { #if NIPMI > 0 struct ipmi_attach_args mba_ipmi; #endif +#if NPVBUS > 0 + struct pvbus_attach_args mba_pvba; +#endif }; /* @@ -155,7 +162,7 @@ amd64_mainbus_match(device_t parent, cfdata_t match, void *aux) void amd64_mainbus_attach(device_t parent, device_t self, void *aux) { -#if NISA > 0 || NPCI > 0 || NACPICA > 0 || NIPMI > 0 +#if NISA > 0 || NPCI > 0 || NACPICA > 0 || NIPMI > 0 || NPVBUS > 0 union amd64_mainbus_attach_args mba; #endif @@ -237,6 +244,12 @@ amd64_mainbus_attach(device_t parent, device_t self, void *aux) } #endif +#if NPVBUS > 0 + mba.mba_pvba.pvba_busname = "pvbus"; + config_found(self, &mba.mba_pvba.pvba_busname, NULL, + CFARGS(.iattr = "pvbus")); +#endif + if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); } diff --git a/sys/arch/amd64/amd64/genassym.cf b/sys/arch/amd64/amd64/genassym.cf index b5c7330a229..facd9216f75 100644 --- a/sys/arch/amd64/amd64/genassym.cf +++ b/sys/arch/amd64/amd64/genassym.cf @@ -373,6 +373,7 @@ define BST_TYPE offsetof(struct bus_space_tag, bst_type) define VM_GUEST_XENPV VM_GUEST_XENPV define VM_GUEST_XENPVH VM_GUEST_XENPVH +define VM_GUEST_GENPVH VM_GUEST_GENPVH ifdef XEN define CPU_INFO_VCPU offsetof(struct cpu_info, ci_vcpu) @@ -381,6 +382,12 @@ define SIR_XENIPL_VM SIR_XENIPL_VM define SIR_XENIPL_SCHED SIR_XENIPL_SCHED define SIR_XENIPL_HIGH SIR_XENIPL_HIGH define EVTCHN_UPCALL_MASK offsetof(struct vcpu_info, evtchn_upcall_mask) +define HVM_START_INFO_SIZE sizeof(struct hvm_start_info) +define START_INFO_VERSION offsetof(struct hvm_start_info, version) +define MMAP_PADDR offsetof(struct hvm_start_info, memmap_paddr) +define MMAP_ENTRIES offsetof(struct hvm_start_info, memmap_entries) +define MMAP_ENTRY_SIZE sizeof(struct hvm_memmap_table_entry) +define CMDLINE_PADDR offsetof(struct hvm_start_info, cmdline_paddr) ifdef XENPV define XEN_PT_BASE offsetof(struct start_info, pt_base) define XEN_NR_PT_FRAMES offsetof(struct start_info, nr_pt_frames) diff --git a/sys/arch/amd64/amd64/locore.S b/sys/arch/amd64/amd64/locore.S index 089c7388e18..97519bcb6a1 100644 --- a/sys/arch/amd64/amd64/locore.S +++ b/sys/arch/amd64/amd64/locore.S @@ -278,11 +278,12 @@ #ifdef XEN #define __ASSEMBLY__ +#include <xen/include/public/arch-x86/cpuid.h> #include <xen/include/public/elfnote.h> #include <xen/include/public/xen.h> #define ELFNOTE(name, type, desctype, descdata...) \ -.pushsection .note.name ; \ +.pushsection .note.name, "a", @note ; \ .align 4 ; \ .long 2f - 1f /* namesz */ ; \ .long 4f - 3f /* descsz */ ; \ @@ -306,7 +307,7 @@ ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .quad, start) #else ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, .quad, 0) - ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long, RELOC(start_xen32)) + ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long, RELOC(start_genpvh)) #endif /* XENPV */ ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .quad, hypercall_page) ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW, .quad, HYPERVISOR_VIRT_START) @@ -973,7 +974,6 @@ longmode_hi: movl %eax,_C_LABEL(cpuid_level) movl $VM_GUEST_XENPV, _C_LABEL(vm_guest) - /* * Initialize cpu_info_primary.ci_self := &cpu_info_primary, * and initialize some MSRs with @@ -1035,7 +1035,7 @@ END(start) # if !defined(XENPV) /* entry point for Xen PVH */ .code32 -ENTRY(start_xen32) +ENTRY(start_genpvh) /* Xen doesn't start us with a valid gdt */ movl $RELOC(gdtdesc32), %eax lgdt (%eax) @@ -1051,13 +1051,98 @@ ENTRY(start_xen32) movl $RELOC(tmpstk),%esp /* clear BSS */ - xorl %eax,%eax + xorl %eax,%eax movl $RELOC(__bss_start),%edi movl $RELOC(_end),%ecx subl %edi,%ecx rep stosb + /* + * Here, we have 2 cases : + * + * 1) We have been started by Xen + * 2) We have been started by another VMM (Qemu, Firecracker, ...) + * + * The main difference is that, when we are started by Xen, + * %ebx (addr of the hvm_start_info structure) is pointing to a + * location that will be mapped correctly later. + * + * In the second case, we have to copy this structure (and all + * the information contained in it) to a location that will be + * mapped later : __kernel_end + * + * To distinguish between the 2 cases, we'll use the 'cpuid' instruction + */ + + push %ebx + xorl %eax, %eax + cpuid + cmpl $0x1, %eax /* Check if we can call CPUID with eax=1 */ + jb .start_genpvh + xorl %eax, %eax + inc %eax + cpuid + shr $31, %ecx + testb $1, %cl /* Check if bit 31 of ECX (hypervisor) is set */ + jz .start_genpvh + xorl %eax, %eax + inc %eax + shl $30, %eax + cpuid /* Calling cpuid with eax=0x40000000 */ + cmp $XEN_CPUID_SIGNATURE_EBX, %ebx /* "VneX" */ + je .start_xen32 + + /* We have been started by a VMM that is *not* Xen */ + +.start_genpvh: + + /* First, copy the hvm_start_info structure to __kernel_end */ + pop %ebx + movl %ebx, %esi + movl $RELOC(__kernel_end), %edi + movl $HVM_START_INFO_SIZE, %ecx + shrl $2, %ecx + rep movsl + + /* Copy cmdline_paddr after hvm_start_info */ + movl CMDLINE_PADDR(%ebx), %esi + movl $RELOC(__kernel_end), %ecx + movl %edi, CMDLINE_PADDR(%ecx) /* Set new cmdline_paddr in hvm_start_info */ + .cmdline_copy: + movb (%esi), %al + movsb + cmp $0, %al + jne .cmdline_copy + + /* Copy memmap_paddr after cmdline (only if hvm_start_info->version != 0) */ + xorl %eax, %eax + cmpl START_INFO_VERSION(%ebx), %eax + je .reload_ebx + movl MMAP_PADDR(%ebx), %esi + movl $RELOC(__kernel_end), %ecx + movl %edi, MMAP_PADDR(%ecx) /* Set new memmap_paddr in hvm_start_info */ + movl MMAP_ENTRIES(%ebx), %eax /* Get memmap_entries */ + movl $MMAP_ENTRY_SIZE, %ebx + mull %ebx /* eax * ebx => edx:eax */ + movl %eax, %ecx + shll $2, %ecx + rep movsl + +.reload_ebx: + movl $RELOC(__kernel_end), %ebx + + /* announce ourself */ + movl $VM_GUEST_GENPVH, RELOC(vm_guest) + + jmp .save_hvm_start_paddr + +.start_xen32: + pop %ebx + movl $VM_GUEST_XENPVH, RELOC(vm_guest) + +.save_hvm_start_paddr: + /* * save addr of the hvm_start_info structure. This is also the end * of the symbol table @@ -1069,6 +1154,9 @@ ENTRY(start_xen32) movl %eax,(%ebp) movl $KERNBASE_HI,4(%ebp) /* get a page for HYPERVISOR_shared_info */ + /* this is only needed if we are running on Xen */ + cmpl $VM_GUEST_XENPVH, RELOC(vm_guest) + jne .add_hvm_start_info_page addl $PAGE_SIZE, %ebx addl $PGOFSET,%ebx andl $~PGOFSET,%ebx @@ -1076,6 +1164,7 @@ ENTRY(start_xen32) movl %ebx,(%ebp) movl $0,4(%ebp) /* XXX assume hvm_start_info+dependant structure fits in a single page */ +.add_hvm_start_info_page: addl $PAGE_SIZE, %ebx addl $PGOFSET,%ebx andl $~PGOFSET,%ebx @@ -1084,10 +1173,8 @@ ENTRY(start_xen32) movl %ebx,(%ebp) movl $KERNBASE_HI,4(%ebp) - /* announce ourself */ - movl $VM_GUEST_XENPVH, RELOC(vm_guest) jmp .Lbiosbasemem_finished -END(start_xen32) +END(start_genpvh) .code64 # endif /* !XENPV */ /* space for the hypercall call page */ @@ -1099,6 +1186,7 @@ ENTRY(hypercall_page) /* Returns -1, on HYPERVISOR_xen_version() */ retq .align HYPERCALL_PAGE_OFFSET, 0x90 END(hypercall_page) + #endif /* XEN */ /* diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 375d7a41e7f..f1c8f5c66a6 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -1525,8 +1525,10 @@ init_x86_64_ksyms(void) } else { uintptr_t endp = (uintptr_t)(void *)&end; - ksyms_addsyms_elf(*(long *)endp, - ((long *)endp) + 1, esym); + if (vm_guest == VM_GUEST_GENPVH) + ksyms_addsyms_elf(0, ((long *)endp) + 1, esym); + else + ksyms_addsyms_elf(*(long *)endp, ((long *)endp) + 1, esym); } #endif } @@ -1710,7 +1712,7 @@ init_x86_64(paddr_t first_avail) #endif #ifdef XEN - if (vm_guest == VM_GUEST_XENPVH) + if (vm_guest == VM_GUEST_XENPVH || vm_guest == VM_GUEST_GENPVH) xen_parse_cmdline(XEN_PARSE_BOOTFLAGS, NULL); #endif init_pte(); diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64 index 99e0e512938..d4e2fc5f38b 100644 --- a/sys/arch/amd64/conf/files.amd64 +++ b/sys/arch/amd64/conf/files.amd64 @@ -94,7 +94,7 @@ include "dev/i2o/files.i2o" # XXX BIOS32 only if something that uses it is configured! device mainbus: isabus, pcibus, bios32, acpibus, cpubus, ioapicbus, - ipmibus, hypervisorbus + ipmibus, hypervisorbus, pvbus attach mainbus at root file arch/amd64/amd64/amd64_mainbus.c mainbus & !xenpv file arch/x86/x86/mainbus.c mainbus @@ -200,4 +200,7 @@ file dev/acpi/vmbus_acpi.c vmbus_acpi # VMEbus support include "dev/vme/files.vme" +# PVbus support +include "dev/pv/files.pv" + include "arch/amd64/conf/majors.amd64" diff --git a/sys/arch/x86/acpi/acpi_machdep.c b/sys/arch/x86/acpi/acpi_machdep.c index 9dbfe49ca5f..b75767fe615 100644 --- a/sys/arch/x86/acpi/acpi_machdep.c +++ b/sys/arch/x86/acpi/acpi_machdep.c @@ -158,7 +158,7 @@ out: } #else #ifdef XEN - if (vm_guest == VM_GUEST_XENPVH) { + if (vm_guest == VM_GUEST_XENPVH || vm_guest == VM_GUEST_GENPVH) { PhysicalAddress = hvm_start_info->rsdp_paddr; if (PhysicalAddress) return PhysicalAddress; diff --git a/sys/arch/x86/include/cpu.h b/sys/arch/x86/include/cpu.h index 0dae029202a..c82da831157 100644 --- a/sys/arch/x86/include/cpu.h +++ b/sys/arch/x86/include/cpu.h @@ -516,6 +516,7 @@ typedef enum vm_guest { VM_GUEST_VMWARE, VM_GUEST_KVM, VM_GUEST_VIRTUALBOX, + VM_GUEST_GENPVH, VM_LAST } vm_guest_t; extern vm_guest_t vm_guest; diff --git a/sys/arch/x86/x86/bus_dma.c b/sys/arch/x86/x86/bus_dma.c index b47b4b1bc2e..7d2753bb7c2 100644 --- a/sys/arch/x86/x86/bus_dma.c +++ b/sys/arch/x86/x86/bus_dma.c @@ -319,7 +319,7 @@ _bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, goto out; } - if (map->_dm_bounce_thresh != 0) + if (map->_dm_bounce_thresh != 0 || map->_dm_segcnt == 1) cookieflags |= X86_DMA_MIGHT_NEED_BOUNCE; if ((cookieflags & X86_DMA_MIGHT_NEED_BOUNCE) == 0) { diff --git a/sys/arch/x86/x86/consinit.c b/sys/arch/x86/x86/consinit.c index 7a65f1bfa09..f72f872ab54 100644 --- a/sys/arch/x86/x86/consinit.c +++ b/sys/arch/x86/x86/consinit.c @@ -171,6 +171,7 @@ consinit(void) #if (NCOM > 0) int rv; #endif + char console_devname[16] = ""; #ifdef XENPVHVM if (vm_guest == VM_GUEST_XENPVH) { @@ -178,6 +179,13 @@ consinit(void) return; /* fallback to native console selection, usefull for dom0 PVH */ } + if (vm_guest == VM_GUEST_GENPVH) { + union xen_cmdline_parseinfo xcp; + /* get console= parameter from generic PVH VMM */ + xen_parse_cmdline(XEN_PARSE_CONSOLE, &xcp); + strncpy(console_devname, xcp.xcp_console, + sizeof(console_devname)); + } #endif if (initted) return; @@ -188,7 +196,10 @@ consinit(void) if (!consinfo) #endif consinfo = &default_consinfo; - + /* console= parameter was not passed via a generic PVH VMM */ + if (!console_devname[0]) + strncpy(console_devname, consinfo->devname, + sizeof(console_devname)); #if (NGENFB > 0) #if defined(XENPVHVM) && defined(DOM0OPS) if (vm_guest == VM_GUEST_XENPVH && xendomain_is_dom0()) @@ -197,8 +208,7 @@ consinit(void) #endif /* XENPVHVM */ fbinfo = lookup_bootinfo(BTINFO_FRAMEBUFFER); #endif - - if (!strcmp(consinfo->devname, "pc")) { + if (!strcmp(console_devname, "pc")) { int error; #if (NGENFB > 0) if (fbinfo && fbinfo->physaddr > 0) { @@ -254,7 +264,7 @@ dokbd: return; } #if (NCOM > 0) - if (!strcmp(consinfo->devname, "com")) { + if (!strcmp(console_devname, "com")) { int addr = consinfo->addr; int speed = consinfo->speed; @@ -278,14 +288,14 @@ dokbd: } #endif #if (NNULLCONS > 0) - if (!strcmp(consinfo->devname, "nullcons")) { + if (!strcmp(console_devname, "nullcons")) { void nullcninit(struct consdev *cn); nullcninit(0); return; } #endif - panic("invalid console device %s", consinfo->devname); + panic("invalid console device %s", console_devname); } #ifdef KGDB diff --git a/sys/arch/x86/x86/identcpu.c b/sys/arch/x86/x86/identcpu.c index ec2fa0620b7..9856cf9fb15 100644 --- a/sys/arch/x86/x86/identcpu.c +++ b/sys/arch/x86/x86/identcpu.c @@ -1044,6 +1044,7 @@ static const struct vm_name_guest vm_bios_vendors[] = { { "BHYVE", VM_GUEST_VM }, /* bhyve */ { "Seabios", VM_GUEST_VM }, /* KVM */ { "innotek GmbH", VM_GUEST_VIRTUALBOX }, /* Oracle VirtualBox */ + { "Generic PVH", VM_GUEST_GENPVH}, /* Generic PVH */ }; static const struct vm_name_guest vm_system_products[] = { @@ -1065,6 +1066,7 @@ identify_hypervisor(void) switch (vm_guest) { case VM_GUEST_XENPV: case VM_GUEST_XENPVH: + case VM_GUEST_GENPVH: /* guest type already known, no bios info */ return; default: diff --git a/sys/arch/x86/x86/mpbios.c b/sys/arch/x86/x86/mpbios.c index a3565a44251..85b3e38e3e2 100644 --- a/sys/arch/x86/x86/mpbios.c +++ b/sys/arch/x86/x86/mpbios.c @@ -209,6 +209,9 @@ static void mpbios_int(const uint8_t *, int, struct mp_intr_map *); static const void *mpbios_map(paddr_t, int, struct mp_map *); static void mpbios_unmap(struct mp_map *); +#ifdef MPTABLE_LINUX_BUG_COMPAT +static uint16_t compute_entry_count(const uint8_t *, const uint8_t *); +#endif /* * globals to help us bounce our way through parsing the config table. */ @@ -333,6 +336,17 @@ mpbios_probe(device_t self) if (mp_fps != NULL) goto found; + /* + * Linux assumes that it always has 640 kB of base memory and + * searches for the MP table at 639k regardless of whether that + * address is present in the system memory map. Some VM systems + * rely on this buggy behaviour. + */ + mp_fps = mpbios_search(self, 639 * 1024, 1024 / 4, &mp_fp_map); + if (mp_fps != NULL) + goto found; + + /* nothing found */ return 0; @@ -533,6 +547,31 @@ static const uint8_t dflt_lint_tab[2] = { }; +#ifdef MPTABLE_LINUX_BUG_COMPAT +/* Compute the correct entry_count value. */ +static uint16_t +compute_entry_count(const uint8_t *entry, const uint8_t *end) +{ + size_t nentries = 0; + + while (entry < end) { + switch (*entry) { + case MPS_MCT_CPU: + case MPS_MCT_BUS: + case MPS_MCT_IOAPIC: + case MPS_MCT_IOINT: + case MPS_MCT_LINT: + break; + default: + panic("%s: Unknown MP Config Entry %d\n", __func__, + (int)*entry); + } + entry += mp_conf[*entry].length;; + nentries++; + } + return (uint16_t)(nentries); +} +#endif /* * 1st pass on BIOS's Intel MP specification table. * @@ -557,6 +596,9 @@ mpbios_scan(device_t self, int *ncpup) int intr_cnt, cur_intr; #if NLAPIC > 0 paddr_t lapic_base; +#endif +#ifdef MPTABLE_LINUX_BUG_COMPAT + uint16_t countfix = 0; #endif const struct dflt_conf_entry *dflt_conf; const int *dflt_bus_irq; @@ -677,6 +719,13 @@ mpbios_scan(device_t self, int *ncpup) position += sizeof(*mp_cth); count = mp_cth->entry_count; +#ifdef MPTABLE_LINUX_BUG_COMPAT + if (count == 0) { + /* count the correct entry_count */ + countfix = compute_entry_count(position, end); + count = countfix; + } +#endif intr_cnt = 0; while ((count--) && (position < end)) { @@ -721,6 +770,10 @@ mpbios_scan(device_t self, int *ncpup) /* re-walk the table, recording info of interest */ position = (const uint8_t *)mp_cth + sizeof(*mp_cth); count = mp_cth->entry_count; +#ifdef MPTABLE_LINUX_BUG_COMPAT + if (count == 0) + count = countfix; +#endif cur_intr = 0; while ((count--) && (position < end)) { diff --git a/sys/arch/x86/x86/x86_autoconf.c b/sys/arch/x86/x86/x86_autoconf.c index fd8688fe9cc..ba4b698d067 100644 --- a/sys/arch/x86/x86/x86_autoconf.c +++ b/sys/arch/x86/x86/x86_autoconf.c @@ -540,7 +540,7 @@ void cpu_bootconf(void) { #ifdef XEN - if (vm_guest == VM_GUEST_XENPVH) { + if (vm_guest == VM_GUEST_XENPVH || vm_guest == VM_GUEST_GENPVH) { xen_bootconf(); return; } diff --git a/sys/arch/x86/x86/x86_machdep.c b/sys/arch/x86/x86/x86_machdep.c index fabd76b1155..8a2364b6ecc 100644 --- a/sys/arch/x86/x86/x86_machdep.c +++ b/sys/arch/x86/x86/x86_machdep.c @@ -911,7 +911,7 @@ init_x86_clusters(void) * the boot program). */ #ifdef XEN - if (vm_guest == VM_GUEST_XENPVH) { + if (vm_guest == VM_GUEST_XENPVH || vm_guest == VM_GUEST_GENPVH) { x86_add_xen_clusters(); } #endif /* XEN */ diff --git a/sys/arch/xen/xen/hypervisor.c b/sys/arch/xen/xen/hypervisor.c index 47b7dc3ec0a..7e26b7696a6 100644 --- a/sys/arch/xen/xen/hypervisor.c +++ b/sys/arch/xen/xen/hypervisor.c @@ -241,10 +241,25 @@ void init_xen_early(void) { const char *cmd_line; + if (vm_guest != VM_GUEST_XENPVH && vm_guest != VM_GUEST_GENPVH) + return; + + hvm_start_info = (void *)((uintptr_t)hvm_start_paddr + KERNBASE); + + if (hvm_start_info->cmdline_paddr != 0) { + cmd_line = + (void *)((uintptr_t)hvm_start_info->cmdline_paddr + KERNBASE); + strlcpy(xen_start_info.cmd_line, cmd_line, + sizeof(xen_start_info.cmd_line)); + } else { + xen_start_info.cmd_line[0] = '\0'; + } + xen_start_info.flags = hvm_start_info->flags; + if (vm_guest != VM_GUEST_XENPVH) return; + xen_init_hypercall_page(); - hvm_start_info = (void *)((uintptr_t)hvm_start_paddr + KERNBASE); HYPERVISOR_shared_info = (void *)((uintptr_t)HYPERVISOR_shared_info_pa + KERNBASE); struct xen_add_to_physmap xmap = { @@ -262,15 +277,6 @@ init_xen_early(void) } delay_func = x86_delay = xen_delay; x86_initclock_func = xen_initclocks; - if (hvm_start_info->cmdline_paddr != 0) { - cmd_line = - (void *)((uintptr_t)hvm_start_info->cmdline_paddr + KERNBASE); - strlcpy(xen_start_info.cmd_line, cmd_line, - sizeof(xen_start_info.cmd_line)); - } else { - xen_start_info.cmd_line[0] = '\0'; - } - xen_start_info.flags = hvm_start_info->flags; } diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index 6763f983669..9f87bb3b638 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -273,7 +273,7 @@ attach amdccp at acpinodebus with amdccp_acpi file dev/acpi/amdccp_acpi.c amdccp_acpi # QEMU Virtio -attach virtio at acpinodebus with virtio_acpi +attach virtio at acpinodebus with virtio_acpi: virtio_mmio file dev/acpi/virtio_acpi.c virtio_acpi # OHCI-compliant USB controller diff --git a/sys/dev/ldvar.h b/sys/dev/ldvar.h index 0aa0c097a74..5b9d8bc2075 100644 --- a/sys/dev/ldvar.h +++ b/sys/dev/ldvar.h @@ -56,6 +56,7 @@ struct ld_softc { uint64_t sc_secperunit; /* # sectors in total */ int sc_secsize; /* sector size in bytes */ int sc_maxxfer; /* max xfer size in bytes */ + int sc_maxnsegs; /* maximum number of segments */ int sc_maxqueuecnt; /* maximum h/w queue depth */ char *sc_typename; /* inquiry data */ diff --git a/sys/dev/pci/ld_virtio.c b/sys/dev/pci/ld_virtio.c index 0cea794c9a0..afdc1e48aa8 100644 --- a/sys/dev/pci/ld_virtio.c +++ b/sys/dev/pci/ld_virtio.c @@ -196,6 +196,7 @@ ld_virtio_alloc_reqs(struct ld_virtio_softc *sc, int qsize) memset(vaddr, 0, allocsize); for (i = 0; i < qsize; i++) { struct virtio_blk_req *vr = &sc->sc_reqs[i]; + int nsegs; r = bus_dmamap_create(virtio_dmat(sc->sc_virtio), offsetof(struct virtio_blk_req, vr_bp), 1, @@ -219,10 +220,18 @@ ld_virtio_alloc_reqs(struct ld_virtio_softc *sc, int qsize) "error code %d\n", r); goto err_reqs; } + /* + * if d->sc_maxnsegs == VIRTIO_BLK_MIN_SEGMENTS + 1, the + * device only supports a single data segment. + */ + if (ld->sc_maxnsegs == VIRTIO_BLK_MIN_SEGMENTS + 1) + nsegs = ld->sc_maxnsegs - VIRTIO_BLK_MIN_SEGMENTS; + else + nsegs = (ld->sc_maxxfer / NBPG) + VIRTIO_BLK_MIN_SEGMENTS; + r = bus_dmamap_create(virtio_dmat(sc->sc_virtio), ld->sc_maxxfer, - (ld->sc_maxxfer / NBPG) + - VIRTIO_BLK_MIN_SEGMENTS, + nsegs, ld->sc_maxxfer, 0, BUS_DMA_WAITOK|BUS_DMA_ALLOCNOW, @@ -264,7 +273,7 @@ ld_virtio_attach(device_t parent, device_t self, void *aux) struct ld_softc *ld = &sc->sc_ld; struct virtio_softc *vsc = device_private(parent); uint64_t features; - int qsize, maxxfersize, maxnsegs; + int qsize, maxxfersize; if (virtio_child(vsc) != NULL) { aprint_normal(": child already attached for %s; " @@ -317,30 +326,33 @@ ld_virtio_attach(device_t parent, device_t self, void *aux) maxxfersize = MAXPHYS; if (features & VIRTIO_BLK_F_SEG_MAX) { - maxnsegs = virtio_read_device_config_4(vsc, + ld->sc_maxnsegs = virtio_read_device_config_4(vsc, VIRTIO_BLK_CONFIG_SEG_MAX); - if (maxnsegs < VIRTIO_BLK_MIN_SEGMENTS) { + if (ld->sc_maxnsegs < VIRTIO_BLK_MIN_SEGMENTS) { aprint_error_dev(sc->sc_dev, "Too small SEG_MAX %d minimum is %d\n", - maxnsegs, VIRTIO_BLK_MIN_SEGMENTS); - maxnsegs = maxxfersize / NBPG; + ld->sc_maxnsegs, VIRTIO_BLK_MIN_SEGMENTS); + ld->sc_maxnsegs = maxxfersize / NBPG; // goto err; } } else - maxnsegs = maxxfersize / NBPG; + /* + * if there is no VIRTIO_BLK_F_SEG_MAX feature advertised, the + * number of segments can be as low as 1 (i.e. Firecracker) + */ + ld->sc_maxnsegs = 1; /* 2 for the minimum size */ - maxnsegs += VIRTIO_BLK_MIN_SEGMENTS; + ld->sc_maxnsegs += VIRTIO_BLK_MIN_SEGMENTS; virtio_init_vq_vqdone(vsc, &sc->sc_vq, 0, ld_virtio_vq_done); - if (virtio_alloc_vq(vsc, &sc->sc_vq, maxxfersize, maxnsegs, + if (virtio_alloc_vq(vsc, &sc->sc_vq, maxxfersize, ld->sc_maxnsegs, "I/O request") != 0) { goto err; } qsize = sc->sc_vq.vq_num; - if (virtio_child_attach_finish(vsc, &sc->sc_vq, 1, NULL, VIRTIO_F_INTR_MSIX) != 0) goto err; diff --git a/sys/dev/pv/files.pv b/sys/dev/pv/files.pv new file mode 100644 index 00000000000..1775b9edf5a --- /dev/null +++ b/sys/dev/pv/files.pv @@ -0,0 +1,8 @@ +define pvbus {} + +device pv {} +attach pv at pvbus +file dev/pv/pvbus.c pvbus needs-flag + +attach virtio at pv with mmio_cmdline: virtio_mmio +file dev/virtio/virtio_mmio_cmdline.c mmio_cmdline diff --git a/sys/dev/pv/pvbus.c b/sys/dev/pv/pvbus.c new file mode 100644 index 00000000000..f1101d62305 --- /dev/null +++ b/sys/dev/pv/pvbus.c @@ -0,0 +1,71 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Emile 'iMil' Heitor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/systm.h> + +#include <machine/bus_private.h> + +#include <dev/pv/pvvar.h> + +struct x86_bus_dma_tag pvbus_bus_dma_tag = { + ._tag_needs_free = 0, + ._bounce_thresh = 0, + ._bounce_alloc_lo = 0, + ._bounce_alloc_hi = 0, + ._may_bounce = NULL, +}; + +static int +pv_match(device_t parent, cfdata_t match, void *aux) +{ + return 1; +} + +static void +pv_attach(device_t parent, device_t self, void *aux) { + struct pv_attach_args pvaa; + + pvaa.pvaa_memt = x86_bus_space_mem; + pvaa.pvaa_dmat = &pvbus_bus_dma_tag; + + aprint_naive("\n"); + aprint_normal("\n"); + + config_found(self, &pvaa, NULL, CFARGS_NONE); +} + +CFATTACH_DECL_NEW(pv, sizeof(struct pv_softc), + pv_match, pv_attach, NULL, NULL); diff --git a/sys/dev/pv/pvvar.h b/sys/dev/pv/pvvar.h new file mode 100644 index 00000000000..bba44b926dc --- /dev/null +++ b/sys/dev/pv/pvvar.h @@ -0,0 +1,48 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Emile 'iMil' Heitor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PVBUS_PVVAR_H_ +#define _PVBUS_PVVAR_H_ + +struct pv_softc { + device_t sc_dev; +}; + +struct pvbus_attach_args { + const char *pvba_busname; +}; + +struct pv_attach_args { + bus_space_tag_t pvaa_memt; + bus_dma_tag_t pvaa_dmat; +}; + +#endif diff --git a/sys/dev/virtio/virtio_mmio_cmdline.c b/sys/dev/virtio/virtio_mmio_cmdline.c new file mode 100644 index 00000000000..ec2fadc8066 --- /dev/null +++ b/sys/dev/virtio/virtio_mmio_cmdline.c @@ -0,0 +1,345 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Emile 'iMil' Heitor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2022 Colin Percival + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/systm.h> + +#define VIRTIO_PRIVATE +#include <dev/virtio/virtio_mmiovar.h> +#include <dev/pv/pvvar.h> +#include <xen/hypervisor.h> + +#include <machine/i82093var.h> +#include "ioapic.h" + +#define VMMIOSTR "virtio_mmio.device=" + +struct mmio_args { + uint64_t sz; + uint64_t baseaddr; + uint64_t irq; + uint64_t id; +}; + +struct virtio_mmio_cmdline_softc { + struct virtio_mmio_softc sc_msc; + struct mmio_args margs; +}; + +static int virtio_mmio_cmdline_match(device_t, cfdata_t, void *); +static void virtio_mmio_cmdline_attach(device_t, device_t, void *); +static int virtio_mmio_cmdline_do_attach(device_t, + struct pv_attach_args *, + struct mmio_args *); +static int virtio_mmio_cmdline_detach(device_t, int); +static int virtio_mmio_cmdline_rescan(device_t, const char *, const int *); +static int virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *); +static void virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *); + +CFATTACH_DECL3_NEW(mmio_cmdline, + sizeof(struct virtio_mmio_cmdline_softc), + virtio_mmio_cmdline_match, virtio_mmio_cmdline_attach, + virtio_mmio_cmdline_detach, NULL, + virtio_mmio_cmdline_rescan, (void *)voidop, DVF_DETACH_SHUTDOWN); + +static int +virtio_mmio_cmdline_match(device_t parent, cfdata_t match, void *aux) +{ + if (strstr(xen_start_info.cmd_line, VMMIOSTR) == NULL) + return 0; + + return 1; +} + +static void +parsearg(struct mmio_args *margs, const char *arg) +{ + char *p; + + /* <size> */ + margs->sz = strtoull(arg, (char **)&p, 0); + if ((margs->sz == 0) || (margs->sz == ULLONG_MAX)) + goto bad; + switch (*p) { + case 'E': case 'e': + margs->sz <<= 10; + /* FALLTHROUGH */ + case 'P': case 'p': + margs->sz <<= 10; + /* FALLTHROUGH */ + case 'T': case 't': + margs->sz <<= 10; + /* FALLTHROUGH */ + case 'G': case 'g': + margs->sz <<= 10; + /* FALLTHROUGH */ + case 'M': case 'm': + margs->sz <<= 10; + /* FALLTHROUGH */ + case 'K': case 'k': + margs->sz <<= 10; + p++; + break; + } + + /* @<baseaddr> */ + if (*p++ != '@') + goto bad; + margs->baseaddr = strtoull(p, (char **)&p, 0); + if ((margs->baseaddr == 0) || (margs->baseaddr == ULLONG_MAX)) + goto bad; + + /* :<irq> */ + if (*p++ != ':') + goto bad; + margs->irq = strtoull(p, (char **)&p, 0); + if ((margs->irq == 0) || (margs->irq == ULLONG_MAX)) + goto bad; + + /* Optionally, :<id> */ + if (*p) { + if (*p++ != ':') + goto bad; + margs->id = strtoull(p, (char **)&p, 0); + if ((margs->id == 0) || (margs->id == ULLONG_MAX)) + goto bad; + } else { + margs->id = 0; + } + + /* Should have reached the end of the string. */ + if (*p) + goto bad; + + return; + +bad: + aprint_error("Error parsing virtio_mmio parameter: %s\n", arg); +} + +static void +virtio_mmio_cmdline_attach(device_t parent, device_t self, void *aux) +{ + struct virtio_mmio_cmdline_softc *sc = device_private(self); + struct pv_attach_args *pvaa = aux; + struct mmio_args *margs = &sc->margs; + char *v, *n, cmdline[128]; + int error; + static char *p = NULL; + static int idx = 0; + bool hasnext; + + aprint_normal("\n"); + aprint_naive("\n"); + + if (idx == 0) { + strncpy(cmdline, xen_start_info.cmd_line, sizeof(cmdline)); + aprint_verbose("kernel parameters: %s\n", cmdline); + if ((p = strstr(cmdline, VMMIOSTR)) == NULL) + return; + } + + while (*p) { /* manual strtok() */ + hasnext = false; + v = p; /* start of VMMIOSTR or next param */ + while (*p && *p != ' ') /* find end of param */ + p++; + if (*p) { + n = p; /* record end of param */ + *p = '\0'; /* end it */ + hasnext = true; /* more params to come */ + } + if (strncmp(v, VMMIOSTR, strlen(VMMIOSTR)) != 0) + continue; /* this was not an MMIO parameter */ + p = v; /* start of VMMIOSTR */ + while (*p && *p != '=') /* point to the value */ + p++; + if (*p) { + p++; + aprint_normal("viommio: %s\n", p); + parsearg(margs, p); + + error = virtio_mmio_cmdline_do_attach(self, + pvaa, margs); + + if (error) + return; + } + if (hasnext) { + p = n+1; /* previously recorded end of param */ + idx++; + config_found(parent, pvaa, NULL, CFARGS_NONE); + } + } +} + +static int +virtio_mmio_cmdline_do_attach(device_t self, + struct pv_attach_args *pvaa, + struct mmio_args *margs) +{ + struct virtio_mmio_cmdline_softc *sc = device_private(self); + struct virtio_mmio_softc *const msc = &sc->sc_msc; + struct virtio_softc *const vsc = &msc->sc_sc; + int error; + + msc->sc_iot = pvaa->pvaa_memt; + vsc->sc_dmat = pvaa->pvaa_dmat; + msc->sc_iosize = margs->sz; + vsc->sc_dev = self; + + error = bus_space_map( + msc->sc_iot, margs->baseaddr, + margs->sz, 0, &msc->sc_ioh + ); + if (error) { + aprint_error_dev(self, + "couldn't map %#" PRIx64 ": %d", + (uint64_t)margs->baseaddr, error + ); + return error; + } + + msc->sc_alloc_interrupts = virtio_mmio_cmdline_alloc_interrupts; + msc->sc_free_interrupts = virtio_mmio_cmdline_free_interrupts; + + virtio_mmio_common_attach(msc); + virtio_mmio_cmdline_rescan(self, "virtio", NULL); + + return 0; +} + +static int +virtio_mmio_cmdline_detach(device_t self, int flags) +{ + struct virtio_mmio_cmdline_softc * const sc = device_private(self); + struct virtio_mmio_softc * const msc = &sc->sc_msc; + + return virtio_mmio_common_detach(msc, flags); +} + +static int +virtio_mmio_cmdline_rescan(device_t self, const char *ifattr, const int *locs) +{ + struct virtio_mmio_cmdline_softc *const sc = device_private(self); + struct virtio_mmio_softc *const msc = &sc->sc_msc; + struct virtio_softc *const vsc = &msc->sc_sc; + struct virtio_attach_args va; + + if (vsc->sc_child) + return 0; + + memset(&va, 0, sizeof(va)); + va.sc_childdevid = vsc->sc_childdevid; + + config_found(self, &va, NULL, CFARGS_NONE); + + if (virtio_attach_failed(vsc)) + return 0; + + return 0; +} + + +static int +virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *msc) +{ + struct virtio_mmio_cmdline_softc *const sc = + (struct virtio_mmio_cmdline_softc *)msc; + struct virtio_softc *const vsc = &msc->sc_sc; + struct ioapic_softc *ioapic; + struct pic *pic; + int irq = sc->margs.irq; + int pin = irq; + bool mpsafe; + + ioapic = ioapic_find_bybase(irq); + + if (ioapic != NULL) { + KASSERT(ioapic->sc_pic.pic_type == PIC_IOAPIC); + pic = &ioapic->sc_pic; + pin = irq - pic->pic_vecbase; + irq = -1; + } else + pic = &i8259_pic; + + mpsafe = (0 != (vsc->sc_flags & VIRTIO_F_INTR_MPSAFE)); + + msc->sc_ih = intr_establish_xname(irq, pic, pin, IST_LEVEL, vsc->sc_ipl, + virtio_mmio_intr, msc, mpsafe, device_xname(vsc->sc_dev)); + if (msc->sc_ih == NULL) { + aprint_error_dev(vsc->sc_dev, + "failed to establish interrupt\n"); + return -1; + } + aprint_normal_dev(vsc->sc_dev, "interrupting on %d\n", irq); + + return 0; +} + +static void +virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *msc) +{ + if (msc->sc_ih != NULL) { + intr_disestablish(msc->sc_ih); + msc->sc_ih = NULL; + } +} +