Skip to content

Commit

Permalink
cmd: add vbe modesetting command
Browse files Browse the repository at this point in the history
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
adamgreloch committed Sep 24, 2024
1 parent ced2966 commit d27d2e9
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 2 deletions.
2 changes: 1 addition & 1 deletion cmds/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

PLO_ALLCOMMANDS = alias app bankswitch bitstream blob bootcm4 bootrom bridge call console \
copy devices dump echo erase go help jffs2 kernel kernelimg lspci map mem mpu otp phfs \
ptable reboot script stop test-dev test-ddr wait
ptable reboot script stop test-dev test-ddr wait vbe

PLO_COMMANDS ?= $(PLO_ALLCOMMANDS)
PLO_APPLETS = $(filter $(PLO_ALLCOMMANDS), $(PLO_COMMANDS))
Expand Down
315 changes: 315 additions & 0 deletions cmds/vbe.c
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
};
2 changes: 1 addition & 1 deletion hal/ia32/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ CFLAGS += -DVADDR_KERNEL_BASE=$(VADDR_KERNEL_BASE)
CFLAGS += -Ihal/ia32

PLO_COMMANDS ?= alias app blob call console copy devices dump echo go help kernel lspci map mem \
phfs script reboot stop syspage wait
phfs script reboot stop syspage wait vbe

PLO_ALLDEVICES := disk-bios tty-bios uart-16550

Expand Down
3 changes: 3 additions & 0 deletions hal/ia32/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ extern void _interrupts_bios(unsigned char intr, unsigned short ds, unsigned sho
#define PATH_KERNEL "phoenix-ia32-generic.elf"


/* Platform can set graphic modes (i.e. via VBE) */
#define HAS_GRAPHICS 1


/* Import platform specific definitions */
#include "ld/ia32-generic.ldt"
Expand Down
2 changes: 2 additions & 0 deletions hal/ia32/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ int hal_memoryGetNextEntry(addr_t start, addr_t end, mapent_t *entry)
{ .start = (addr_t)__stack_limit, .end = (addr_t)__stack_top, .type = hal_entryTemp },
{ .start = ADDR_GDT, .end = ADDR_GDT + SIZE_GDT, .type = hal_entryTemp },
{ .start = ADDR_IDT, .end = ADDR_IDT + SIZE_IDT, .type = hal_entryTemp },
{ .start = ADDR_VBE_INFO, .end = ADDR_VBE_INFO + SIZE_VBE_INFO, .type = hal_entryTemp },
{ .start = ADDR_VBE_MODE_INFO, .end = ADDR_VBE_MODE_INFO + SIZE_VBE_MODE_INFO, .type = hal_entryTemp },
/* TODO: this entry should be removed after changes in disk-bios */
{ .start = ADDR_RCACHE, .end = ADDR_RCACHE + SIZE_RCACHE + SIZE_WCACHE, .type = hal_entryTemp },
};
Expand Down
7 changes: 7 additions & 0 deletions ld/ia32-generic.ldt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
#define ADDR_PDIR 0x3000
#define ADDR_PTABLE 0x4000

/* VBE info/mode info structs */
#define ADDR_VBE_INFO 0x77c00
#define SIZE_VBE_INFO 0x200

#define ADDR_VBE_MODE_INFO 0x77e00
#define SIZE_VBE_MODE_INFO 0x100

/* Disk caches (below upper memory starting at 0x80000) */
#define ADDR_RCACHE 0x78000
#define SIZE_RCACHE 0x4000
Expand Down
12 changes: 12 additions & 0 deletions syspage.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,3 +625,15 @@ void syspage_progShow(void)
prog = prog->next;
} while (prog != syspage_common.syspage->progs);
}


#if HAS_GRAPHICS
void syspage_graphmodeSet(graphmode_t graphmode)
{
syspage_common.syspage->hs.graphmode.width = graphmode.width;

Check failure on line 633 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-pc)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'

Check failure on line 633 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-qemu)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'
syspage_common.syspage->hs.graphmode.height = graphmode.height;

Check failure on line 634 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-pc)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'

Check failure on line 634 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-qemu)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'
syspage_common.syspage->hs.graphmode.bpp = graphmode.bpp;

Check failure on line 635 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-pc)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'

Check failure on line 635 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-qemu)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'
syspage_common.syspage->hs.graphmode.pitch = graphmode.pitch;

Check failure on line 636 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-pc)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'

Check failure on line 636 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-qemu)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'
syspage_common.syspage->hs.graphmode.framebuffer = graphmode.framebuffer;

Check failure on line 637 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-pc)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'

Check failure on line 637 in syspage.c

View workflow job for this annotation

GitHub Actions / call-ci / build (ia32-generic-qemu)

'hal_syspage_t' {aka 'struct <anonymous>'} has no member named 'graphmode'
}
#endif
Loading

0 comments on commit d27d2e9

Please sign in to comment.