loader: create local copy of mode list provided by vbeinfoblock

Apparently some systems do corrupt mode list memory area, so we need
to use local copy instead.
This commit is contained in:
Toomas Soome 2021-01-16 12:18:32 +02:00
parent 0bc776f3da
commit ad1ebbe5ce

View File

@ -48,12 +48,14 @@
static struct vbeinfoblock *vbe; static struct vbeinfoblock *vbe;
static struct modeinfoblock *vbe_mode; static struct modeinfoblock *vbe_mode;
static uint16_t *vbe_mode_list;
static size_t vbe_mode_list_size;
/* The default VGA color palette format is 6 bits per primary color. */ /* The default VGA color palette format is 6 bits per primary color. */
int palette_format = 6; int palette_format = 6;
#define VESA_MODE_BASE 0x100 #define VESA_MODE_BASE 0x100
#define VESA_MODE_MAX 0x1ff
#define VESA_MODE_COUNT (VESA_MODE_MAX - VESA_MODE_BASE + 1)
/* /*
* palette array for 8-bit indexed colors. In this case, cmap does store * palette array for 8-bit indexed colors. In this case, cmap does store
@ -545,9 +547,17 @@ mode_set(struct env_var *ev, int flags __unused, const void *value)
return (0); return (0);
} }
static void *
vbe_farptr(uint32_t farptr)
{
return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
}
void void
vbe_init(void) vbe_init(void)
{ {
uint16_t *p, *ml;
/* First set FB for text mode. */ /* First set FB for text mode. */
gfx_state.tg_fb_type = FB_TEXT; gfx_state.tg_fb_type = FB_TEXT;
gfx_state.tg_fb.fb_height = TEXT_ROWS; gfx_state.tg_fb.fb_height = TEXT_ROWS;
@ -573,6 +583,27 @@ vbe_init(void)
vbe_mode = NULL; vbe_mode = NULL;
} }
/*
* Copy mode list. We must do this because some systems do
* corrupt the provided list (vbox 6.1 is one example).
*/
p = ml = vbe_farptr(vbe->VideoModePtr);
while(*p++ != 0xFFFF)
;
vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
vbe_mode_list = malloc(vbe_mode_list_size);
if (vbe_mode_list == NULL) {
free(vbe);
vbe = NULL;
free(vbe_mode);
vbe_mode = NULL;
}
bcopy(ml, vbe_mode_list, vbe_mode_list_size);
/* reset VideoModePtr, so we will not have chance to use bad data. */
vbe->VideoModePtr = 0;
env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set, env_setenv("screen.textmode", EV_VOLATILE, "1", mode_set,
env_nounset); env_nounset);
env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set, env_setenv("vbe_max_resolution", EV_VOLATILE, NULL, mode_set,
@ -717,12 +748,6 @@ vbe_set_mode(int modenum)
return (0); return (0);
} }
static void *
vbe_farptr(uint32_t farptr)
{
return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
}
/* /*
* Verify existance of mode number or find mode by * Verify existance of mode number or find mode by
* dimensions. If depth is not given, walk values 32, 24, 16, 8. * dimensions. If depth is not given, walk values 32, 24, 16, 8.
@ -731,15 +756,13 @@ static int
vbe_find_mode_xydm(int x, int y, int depth, int m) vbe_find_mode_xydm(int x, int y, int depth, int m)
{ {
struct modeinfoblock mi; struct modeinfoblock mi;
uint32_t farptr; uint16_t *farptr;
uint16_t mode; uint16_t mode;
int safety, i; int idx, nitems, i;
memset(vbe, 0, sizeof (*vbe)); memset(vbe, 0, sizeof (*vbe));
if (biosvbe_info(vbe) != VBE_SUCCESS) if (biosvbe_info(vbe) != VBE_SUCCESS)
return (0); return (0);
if (vbe->VideoModePtr == 0)
return (0);
if (m != -1) if (m != -1)
i = 8; i = 8;
@ -748,17 +771,17 @@ vbe_find_mode_xydm(int x, int y, int depth, int m)
else else
i = depth; i = depth;
nitems = vbe_mode_list_size / sizeof(*vbe_mode_list);
while (i > 0) { while (i > 0) {
farptr = vbe->VideoModePtr; for (idx = 0; idx < nitems; idx++) {
safety = 0; mode = vbe_mode_list[idx];
while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { if (mode == 0xffff)
safety++;
farptr += 2;
if (safety == VESA_MODE_COUNT)
break; break;
if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) { if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) {
continue; continue;
} }
/* we only care about linear modes here */ /* we only care about linear modes here */
if (vbe_mode_is_supported(&mi) == 0) if (vbe_mode_is_supported(&mi) == 0)
continue; continue;
@ -910,9 +933,8 @@ void
vbe_modelist(int depth) vbe_modelist(int depth)
{ {
struct modeinfoblock mi; struct modeinfoblock mi;
uint32_t farptr;
uint16_t mode; uint16_t mode;
int nmodes = 0, safety = 0; int nmodes, idx, nentries;
int ddc_caps; int ddc_caps;
uint32_t width, height; uint32_t width, height;
bool edid = false; bool edid = false;
@ -948,6 +970,7 @@ vbe_modelist(int depth)
if (vbe_get_flatpanel(&width, &height)) if (vbe_get_flatpanel(&width, &height))
printf(": Panel %dx%d\n", width, height); printf(": Panel %dx%d\n", width, height);
nmodes = 0;
memset(vbe, 0, sizeof (*vbe)); memset(vbe, 0, sizeof (*vbe));
memcpy(vbe->VbeSignature, "VBE2", 4); memcpy(vbe->VbeSignature, "VBE2", 4);
if (biosvbe_info(vbe) != VBE_SUCCESS) if (biosvbe_info(vbe) != VBE_SUCCESS)
@ -958,26 +981,19 @@ vbe_modelist(int depth)
vbe_print_vbe_info(vbe); vbe_print_vbe_info(vbe);
printf("Modes: "); printf("Modes: ");
farptr = vbe->VideoModePtr; nentries = vbe_mode_list_size / sizeof(*vbe_mode_list);
if (farptr == 0) for (idx = 0; idx < nentries; idx++) {
goto done; mode = vbe_mode_list[idx];
if (mode == 0xffff)
while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
safety++;
farptr += 2;
if (safety == VESA_MODE_COUNT) {
printf("[?] ");
break; break;
}
if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS) if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
continue; continue;
/* we only care about linear modes here */ /* we only care about linear modes here */
if (vbe_mode_is_supported(&mi) == 0) if (vbe_mode_is_supported(&mi) == 0)
continue; continue;
/* we found some mode so reset safety counter */
safety = 0;
/* apply requested filter */ /* apply requested filter */
if (depth != -1 && mi.BitsPerPixel != depth) if (depth != -1 && mi.BitsPerPixel != depth)
continue; continue;