git: 6f80738b228c - main - xen: fetch dom0 video console information from Xen

From: Roger Pau Monné <royger_at_FreeBSD.org>
Date: Thu, 09 Mar 2023 16:14:32 UTC
The branch main has been updated by royger:

URL: https://cgit.FreeBSD.org/src/commit/?id=6f80738b228c04e3ff3f2d14eea2161d2cf4f81c

commit 6f80738b228c04e3ff3f2d14eea2161d2cf4f81c
Author:     Roger Pau Monné <royger@FreeBSD.org>
AuthorDate: 2022-11-21 11:40:08 +0000
Commit:     Roger Pau Monné <royger@FreeBSD.org>
CommitDate: 2023-03-09 16:13:17 +0000

    xen: fetch dom0 video console information from Xen
    
    It's possible for Xen to switch the video mode set by the boot loader,
    so that the information passed in the kernel metadata is no longer
    valid.  Fetch the video mode used by Xen using an hypercall and update
    the medatada for the kernel to use the correct video mode.
    
    Sponsored by: Citrix Systems R&D
---
 sys/contrib/xen/platform.h |  5 ++++
 sys/x86/xen/pv.c           | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/sys/contrib/xen/platform.h b/sys/contrib/xen/platform.h
index 81001335094b..ccd6be741876 100644
--- a/sys/contrib/xen/platform.h
+++ b/sys/contrib/xen/platform.h
@@ -622,6 +622,10 @@ struct xenpf_symdata {
 typedef struct xenpf_symdata xenpf_symdata_t;
 DEFINE_XEN_GUEST_HANDLE(xenpf_symdata_t);
 
+#define XENPF_get_dom0_console 64
+typedef struct dom0_vga_console_info xenpf_dom0_console_t;
+DEFINE_XEN_GUEST_HANDLE(xenpf_dom0_console_t);
+
 /*
  * ` enum neg_errnoval
  * ` HYPERVISOR_platform_op(const struct xen_platform_op*);
@@ -652,6 +656,7 @@ struct xen_platform_op {
         xenpf_core_parking_t          core_parking;
         xenpf_resource_op_t           resource_op;
         xenpf_symdata_t               symdata;
+        xenpf_dom0_console_t          dom0_console;
         uint8_t                       pad[128];
     } u;
 };
diff --git a/sys/x86/xen/pv.c b/sys/x86/xen/pv.c
index 28794db98700..d721e9bb530e 100644
--- a/sys/x86/xen/pv.c
+++ b/sys/x86/xen/pv.c
@@ -336,6 +336,75 @@ xen_pvh_parse_symtab(void)
 }
 #endif
 
+static void
+fixup_console(caddr_t kmdp)
+{
+	struct xen_platform_op op = {
+		.cmd = XENPF_get_dom0_console,
+	};
+	xenpf_dom0_console_t *console = &op.u.dom0_console;
+	union {
+		struct efi_fb efi;
+		struct vbe_fb vbe;
+	} *fb = NULL;
+	int ret;
+
+	ret = HYPERVISOR_platform_op(&op);
+	if (ret != 0) {
+		xc_printf("Failed to get dom0 video console info\n");
+		return;
+	}
+
+	switch (console->video_type) {
+	case XEN_VGATYPE_VESA_LFB:
+		fb = (__typeof__ (fb))preload_search_info(kmdp,
+		    MODINFO_METADATA | MODINFOMD_VBE_FB);
+
+		if (fb == NULL) {
+			xc_printf("No VBE FB in kernel metadata\n");
+			return;
+		}
+
+		_Static_assert(offsetof(struct vbe_fb, fb_bpp) ==
+		    offsetof(struct efi_fb, fb_mask_reserved) +
+		    sizeof(fb->efi.fb_mask_reserved),
+		    "Bad structure overlay\n");
+		fb->vbe.fb_bpp = console->u.vesa_lfb.bits_per_pixel;
+		/* FALLTHROUGH */
+	case XEN_VGATYPE_EFI_LFB:
+		if (fb == NULL) {
+			fb = (__typeof__ (fb))preload_search_info(kmdp,
+			    MODINFO_METADATA | MODINFOMD_EFI_FB);
+			if (fb == NULL) {
+				xc_printf("No EFI FB in kernel metadata\n");
+				return;
+			}
+		}
+
+		fb->efi.fb_addr = console->u.vesa_lfb.lfb_base |
+		    ((uint64_t)console->u.vesa_lfb.ext_lfb_base << 32);
+		fb->efi.fb_size = console->u.vesa_lfb.lfb_size << 16;
+		fb->efi.fb_height = console->u.vesa_lfb.height;
+		fb->efi.fb_width = console->u.vesa_lfb.width;
+		fb->efi.fb_stride = (console->u.vesa_lfb.bytes_per_line << 3) /
+		    console->u.vesa_lfb.bits_per_pixel;
+#define FBMASK(c) \
+    ((~0u << console->u.vesa_lfb.c ## _pos) & \
+    (~0u >> (32 - console->u.vesa_lfb.c ## _pos - \
+    console->u.vesa_lfb.c ## _size)))
+		fb->efi.fb_mask_red = FBMASK(red);
+		fb->efi.fb_mask_green = FBMASK(green);
+		fb->efi.fb_mask_blue = FBMASK(blue);
+		fb->efi.fb_mask_reserved = FBMASK(rsvd);
+#undef FBMASK
+		break;
+
+	default:
+		xc_printf("Video console type unsupported\n");
+		return;
+	}
+}
+
 static caddr_t
 xen_pvh_parse_preload_data(uint64_t modulep)
 {
@@ -414,6 +483,8 @@ xen_pvh_parse_preload_data(uint64_t modulep)
 		    strlcpy(bootmethod, "UEFI", sizeof(bootmethod));
 		else
 		    strlcpy(bootmethod, "BIOS", sizeof(bootmethod));
+
+		fixup_console(kmdp);
 	} else {
 		/* Parse the extra boot information given by Xen */
 		if (start_info->cmdline_paddr != 0)