x86/xen: do video console fixup as part of early initialization

When FreeBSD is running as dom0 the video console metadata provided by the
bootloader might not be accurate, as Xen has very likely taken over the console
and possibly changed the mode.

Adjust the video console information in the kernel metadata as part of early
Xen initialization.

Sponsored by: Cloud Software Group
Reviewed by: imp
Differential revision: https://reviews.freebsd.org/D43934
This commit is contained in:
Roger Pau Monné 2024-02-02 11:29:57 +01:00
parent 5d62aba742
commit 027b66d64b
2 changed files with 87 additions and 83 deletions

View file

@ -30,6 +30,7 @@
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/smp.h>
@ -45,6 +46,7 @@
#include <machine/cpufunc.h>
#include <machine/cpu.h>
#include <machine/md_var.h>
#include <machine/metadata.h>
#include <machine/smp.h>
#include <x86/apicreg.h>
@ -246,6 +248,87 @@ map_shared_info(void)
return (rc);
}
static void
fixup_console(void)
{
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 size;
caddr_t kmdp;
kmdp = preload_search_by_type("elf kernel");
if (kmdp == NULL)
kmdp = preload_search_by_type("elf64 kernel");
if (kmdp == NULL) {
xc_printf("Unable to find kernel metadata\n");
return;
}
size = HYPERVISOR_platform_op(&op);
if (size < 0) {
xc_printf("Failed to get video console info: %d\n", size);
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;
if (size >
offsetof(xenpf_dom0_console_t, u.vesa_lfb.ext_lfb_base))
fb->efi.fb_addr |=
(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;
}
}
/* Early initialization when running as a Xen guest. */
void
xen_early_init(void)
@ -273,6 +356,10 @@ xen_early_init(void)
vm_guest = VM_GUEST_VM;
return;
}
if (xen_initial_domain())
/* Fixup video console information in case Xen changed the mode. */
fixup_console();
}
static void

View file

@ -319,87 +319,6 @@ xen_pvh_parse_symtab(void)
}
#endif
static void
fixup_console(void)
{
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 size;
caddr_t kmdp;
kmdp = preload_search_by_type("elf kernel");
if (kmdp == NULL)
kmdp = preload_search_by_type("elf64 kernel");
if (kmdp == NULL) {
xc_printf("Unable to find kernel metadata\n");
return;
}
size = HYPERVISOR_platform_op(&op);
if (size < 0) {
xc_printf("Failed to get video console info: %d\n", size);
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;
if (size >
offsetof(xenpf_dom0_console_t, u.vesa_lfb.ext_lfb_base))
fb->efi.fb_addr |=
(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)
{
@ -479,8 +398,6 @@ xen_pvh_parse_preload_data(uint64_t modulep)
strlcpy(bootmethod, "UEFI", sizeof(bootmethod));
else
strlcpy(bootmethod, "BIOS", sizeof(bootmethod));
fixup_console();
} else {
/* Parse the extra boot information given by Xen */
if (start_info->cmdline_paddr != 0)