diff --git a/sys/arch/amd64/amd64/genassym.cf b/sys/arch/amd64/amd64/genassym.cf
index b5c7330a229..facd9216f75 100644

# Add convenient defines for locore.S address manipulation,
# these are the different locations of start_info structure
# and members.

--- 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

# locore.S expects start_info being passed by the calling hypervisor in
# %ebx to be located at the end of the symbol table. Qemu and Firecracker
# don't follow this rule which is not part of the official Xen ABI
# https://xenbits.xen.org/docs/unstable/misc/pvh.html
# This patch copies the start_info structure and sub-structures where
# they are expected.
# This work has been done in common with Gregory Cavelier <gcavelier@cleia.org>

--- 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
+	shrl $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

# The symbol table is not loaded when booting from qemu/Firecracker.
# The ELF headers are not available and we cannot compute a valid value for esym.
# We prefer having a "table missing" than "table invalid" warning by passing
# a 0 sized symsize.

--- 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/x86/acpi/acpi_machdep.c b/sys/arch/x86/acpi/acpi_machdep.c
index 9dbfe49ca5f..b75767fe615 100644

# When booting on GENPVH, ACPI address informations is in start_info

--- 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

# Add VM_GUEST_GENPVH as a vm_guest

--- 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/consinit.c b/sys/arch/x86/x86/consinit.c
index 7a65f1bfa09..f72f872ab54 100644

# GENPVH doesn't need nor use Xen console, but it got its console
# information through the start_info structure which holds the
# parameters passed to the kernel, this patch enables the use of both
# consinfo and xcp.xcp_console.

--- 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

# Add VM_GUEST_GENPVH as a valid hypervisor

--- 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/x86_autoconf.c b/sys/arch/x86/x86/x86_autoconf.c
index fd8688fe9cc..ba4b698d067 100644

# When booting in GENPVH mode, informations about the boot device are
# read from command line parameters in start_info, xen_bootconf must be
# called in order to get the right device.

--- 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

# When booting in GENPVH mode, memory mapping information is located in
# start_info, x86_add_xen_clusters() must be called in order to read
# them.

--- 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

# GENPVH doesn't use Xen hypercalls, this patch sets up start_info and
# returns immediately after that.

--- 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;
 }