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
This commit is contained in:
Roger Pau Monné 2022-11-21 12:40:08 +01:00
parent 5489d7e93a
commit 6f80738b22
2 changed files with 76 additions and 0 deletions

View File

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

View File

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