-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds command `vbe` for setting up graphics mode using VESA VBE 2.0 real mode interface. The command checks whether a specified mode (preferred/fallback) is supported by the VGA controller, fetches its details, sets it and stores necessary info (width, height, bpp, framebuffer address, etc.) in the syspage. The info can be then queried using platformctl by userspace apps JIRA: RTOS-906
- Loading branch information
1 parent
ced2966
commit d27d2e9
Showing
8 changed files
with
356 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,315 @@ | ||
/* | ||
* Phoenix-RTOS | ||
* | ||
* Operating system loader | ||
* | ||
* VESA VBE modesetting | ||
* | ||
* Copyright 2024 Phoenix Systems | ||
* Author: Adam Greloch | ||
* | ||
* This file is part of Phoenix-RTOS. | ||
* | ||
* %LICENSE% | ||
*/ | ||
|
||
#include "cmd.h" | ||
|
||
#include <hal/hal.h> | ||
#include <lib/lib.h> | ||
#include <syspage.h> | ||
|
||
|
||
#define PTR_TO_SEG(ptr) ((addr_t)ptr >> 4) | ||
#define SEGOFS_TO_PTR(segofs) ((addr_t)((segofs & 0xffff0000) >> 12) + (segofs & 0xffff)) | ||
|
||
#define SUPPORTS_LFB (0x80) | ||
|
||
|
||
typedef struct { | ||
u16 width; | ||
u16 height; | ||
u16 bpp; | ||
} video_mode_t; | ||
|
||
|
||
typedef struct { | ||
char signature[4]; | ||
u16 version; | ||
u32 oem; | ||
u32 capabilities; | ||
u32 video_modes; | ||
u16 video_memory; | ||
u16 software_rev; | ||
u32 vendor; | ||
u32 product_name; | ||
u32 product_rev; | ||
char reserved[222]; | ||
char oem_data[256]; | ||
} __attribute__((packed)) vbe_info_t; | ||
|
||
|
||
typedef struct { | ||
u16 attributes; | ||
u8 window_a; | ||
u8 window_b; | ||
u16 granularity; | ||
u16 window_size; | ||
u16 segment_a; | ||
u16 segment_b; | ||
u32 win_func_ptr; | ||
u16 pitch; | ||
u16 width; | ||
u16 height; | ||
u8 w_char; | ||
u8 y_char; | ||
u8 planes; | ||
u8 bpp; | ||
u8 banks; | ||
u8 memory_model; | ||
u8 bank_size; | ||
u8 image_pages; | ||
u8 reserved0; | ||
|
||
u8 red_mask; | ||
u8 red_position; | ||
u8 green_mask; | ||
u8 green_position; | ||
u8 blue_mask; | ||
u8 blue_position; | ||
u8 reserved_mask; | ||
u8 reserved_position; | ||
u8 direct_color_attributes; | ||
|
||
u32 framebuffer; | ||
u32 off_screen_mem_off; | ||
u16 off_screen_mem_size; | ||
u8 reserved1[206]; | ||
} __attribute__((packed)) vbe_mode_info_t; | ||
|
||
|
||
static video_mode_t preferred_mode = { 1280, 800, 32 }; | ||
static video_mode_t fallback_mode = { 1024, 768, 32 }; | ||
|
||
|
||
/* ADDR_VBE_INFO, ADDR_VBE_MODE_INFO should be aligned to 0x10, so that they can | ||
* be referenced from 8086 emulation mode using segment register only (es=(ADDR >> 4), di=0) */ | ||
static vbe_info_t *vbe_info = (vbe_info_t *)ADDR_VBE_INFO; | ||
static vbe_mode_info_t *vbe_mode_info = (vbe_mode_info_t *)ADDR_VBE_MODE_INFO; | ||
|
||
|
||
static int _get_vbe_info(void) | ||
{ | ||
/* indicate support for VBE 2.0+ */ | ||
hal_memset(vbe_info, 0, sizeof(vbe_info_t)); | ||
hal_strcpy(vbe_info->signature, "VBE2"); | ||
|
||
int ax = 0x4f00; | ||
|
||
__asm__ volatile( | ||
"pushl $0x10; " | ||
"pushl $0x0; " | ||
"pushl %1; " | ||
"xorw %%di, %%di; " | ||
"call _interrupts_bios; " | ||
"addl $0xc, %%esp; " | ||
: "+a"(ax) | ||
: "r"(PTR_TO_SEG(vbe_info)) | ||
: "edi", "memory", "cc"); | ||
|
||
return ax == 0x4f ? 0 : -1; | ||
} | ||
|
||
|
||
static int _get_vbe_mode_info(u16 mode) | ||
{ | ||
hal_memset(vbe_mode_info, 0, sizeof(vbe_info_t)); | ||
|
||
int ax = 0x4f01; | ||
|
||
__asm__ volatile( | ||
"pushl $0x10; " | ||
"pushl $0x0; " | ||
"pushl %1; " | ||
"xorw %%di, %%di; " | ||
"call _interrupts_bios; " | ||
"addl $0xc, %%esp; " | ||
: "+a"(ax) | ||
: "r"(PTR_TO_SEG(vbe_mode_info)), "cx"(mode) | ||
: "edi", "memory", "cc"); | ||
|
||
return ax == 0x4f ? 0 : -1; | ||
} | ||
|
||
|
||
static int _set_vbe_mode(u16 mode) | ||
{ | ||
int ax = 0x4f02; | ||
|
||
mode |= (1 << 14); /* enable linear framebuffer */ | ||
mode &= ~(1 << 15); /* don't clear the screen */ | ||
|
||
__asm__ volatile( | ||
"pushl $0x10; " | ||
"pushl $0x0; " | ||
"pushl $0x0; " | ||
"xorw %%di, %%di; " | ||
"call _interrupts_bios; " | ||
"addl $0xc, %%esp; " | ||
: "+a"(ax) | ||
: "b"(mode) | ||
: "edi", "memory", "cc"); | ||
|
||
return ax == 0x4f ? 0 : -1; | ||
} | ||
|
||
|
||
static int _find_vbe_mode(graphmode_t *picked_graphmode) | ||
{ | ||
int err; | ||
|
||
u16 *mode_ids = (u16 *)SEGOFS_TO_PTR(vbe_info->video_modes); | ||
|
||
int fallback_mode_id = -1; | ||
graphmode_t fallback_graphmode = { 0 }; | ||
|
||
for (int i = 0; mode_ids[i] != 0xFFFF; i++) { | ||
err = _get_vbe_mode_info(mode_ids[i]); | ||
if (err < 0) { | ||
return -1; | ||
} | ||
|
||
if ((vbe_mode_info->attributes & SUPPORTS_LFB) != 0) { | ||
if (vbe_mode_info->width == preferred_mode.width && vbe_mode_info->height == preferred_mode.height && vbe_mode_info->bpp == preferred_mode.bpp) { | ||
picked_graphmode->width = vbe_mode_info->width; | ||
picked_graphmode->height = vbe_mode_info->height; | ||
picked_graphmode->bpp = vbe_mode_info->bpp; | ||
picked_graphmode->pitch = vbe_mode_info->pitch; | ||
picked_graphmode->framebuffer = vbe_mode_info->framebuffer; | ||
|
||
return mode_ids[i]; | ||
} | ||
|
||
if (vbe_mode_info->width == fallback_mode.width && vbe_mode_info->height == fallback_mode.height && vbe_mode_info->bpp == fallback_mode.bpp) { | ||
fallback_mode_id = mode_ids[i]; | ||
fallback_graphmode.width = vbe_mode_info->width; | ||
fallback_graphmode.height = vbe_mode_info->height; | ||
fallback_graphmode.bpp = vbe_mode_info->bpp; | ||
fallback_graphmode.pitch = vbe_mode_info->pitch; | ||
fallback_graphmode.framebuffer = vbe_mode_info->framebuffer; | ||
} | ||
} | ||
} | ||
|
||
hal_memcpy(picked_graphmode, &fallback_graphmode, sizeof(graphmode_t)); | ||
return fallback_mode_id; | ||
} | ||
|
||
|
||
static int _parse_video_mode(char *str, video_mode_t *video_mode) | ||
{ | ||
unsigned int val; | ||
char *ptr = str; | ||
|
||
if ((ptr == NULL) || (*ptr == '\0')) { | ||
return -EINVAL; | ||
} | ||
|
||
val = lib_strtoul(ptr, &ptr, 0); | ||
if (*ptr != 'x') { | ||
return -EINVAL; | ||
} | ||
video_mode->width = val; | ||
|
||
ptr++; | ||
val = lib_strtoul(ptr, &ptr, 0); | ||
if (*ptr != 'x') { | ||
return -EINVAL; | ||
} | ||
video_mode->height = val; | ||
|
||
ptr++; | ||
val = lib_strtoul(ptr, &ptr, 0); | ||
if (*ptr != '\0') { | ||
return -EINVAL; | ||
} | ||
video_mode->bpp = val; | ||
|
||
return 0; | ||
} | ||
|
||
|
||
static void print_usage(const char *name) | ||
{ | ||
lib_printf( | ||
"Usage: %s <options>\n" | ||
"\t-p W:H:BPP preferred mode (default: %dx%dx%d)\n" | ||
"\t-f W:H:BPP fallback mode (default: %dx%dx%d)\n" | ||
"\t-h prints help\n", | ||
name, preferred_mode.width, preferred_mode.height, preferred_mode.bpp, | ||
fallback_mode.width, fallback_mode.height, fallback_mode.bpp); | ||
} | ||
|
||
|
||
static void cmd_vbe_info(void) | ||
{ | ||
lib_printf("sets graphics mode using VESA VBE"); | ||
} | ||
|
||
|
||
static int cmd_vbe_main(int argc, char *argv[]) | ||
{ | ||
int opt; | ||
int err; | ||
|
||
lib_printf("\n"); | ||
|
||
for (;;) { | ||
opt = lib_getopt(argc, argv, "p:f:h"); | ||
if (opt == -1) { | ||
break; | ||
} | ||
|
||
if (opt == 'p') { | ||
_parse_video_mode(optarg, &preferred_mode); | ||
continue; | ||
} | ||
|
||
if (opt == 'f') { | ||
_parse_video_mode(optarg, &fallback_mode); | ||
continue; | ||
} | ||
|
||
print_usage(argv[0]); | ||
return CMD_EXIT_FAILURE; | ||
} | ||
|
||
do { | ||
err = _get_vbe_info(); | ||
if (err < 0) { | ||
break; | ||
} | ||
|
||
graphmode_t graphmode; | ||
|
||
err = _find_vbe_mode(&graphmode); | ||
if (err < 0) { | ||
break; | ||
} | ||
|
||
syspage_graphmodeSet(graphmode); | ||
|
||
_set_vbe_mode(err); | ||
} while (0); | ||
|
||
if (err < 0) { | ||
lib_printf("\n%s: not supported, skipping", argv[0]); | ||
} | ||
|
||
return CMD_EXIT_SUCCESS; | ||
} | ||
|
||
|
||
static const cmd_t vbe_cmd __attribute__((section("commands"), used)) = { | ||
.name = "vbe", .run = cmd_vbe_main, .info = cmd_vbe_info | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.