This is a resend of two previous patches that improve the text emulation of the "coreboot native vga" SeaVGABIOS support. The main difference in this version is that the support requires both a compile time define (CONFIG_VGA_EMULATE_TEXT) as well as runtime enablement. The net effect of this is that the code wont try to blink the cursor if a bootloader explicitly switches into graphics mode.
This is also available at: https://github.com/KevinOConnor/seabios/tree/testing
-Kevin
Kevin O'Connor (2): vgabios: Support emulating text mode attributes while in graphics mode vgabios: Add software cursor capability
vgasrc/Kconfig | 6 ++++ vgasrc/cbvga.c | 10 +++++-- vgasrc/vbe.c | 5 ++-- vgasrc/vgabios.c | 13 ++++++-- vgasrc/vgabios.h | 14 ++++++++- vgasrc/vgaentry.S | 30 +++++++++++++++++++ vgasrc/vgafb.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- vgasrc/vgainit.c | 34 +++++++++++++++++++++ 8 files changed, 188 insertions(+), 13 deletions(-)
Add support for simple text mode attribute emulation while in graphics mode. This improves text highlighting and background color on some boot-loaders. Enable it only for CBVGA vgabios and only when a text mode is requested.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- vgasrc/Kconfig | 6 ++++++ vgasrc/cbvga.c | 10 +++++++--- vgasrc/vbe.c | 5 ++--- vgasrc/vgabios.h | 10 +++++++++- vgasrc/vgafb.c | 26 +++++++++++++++++++++++--- 5 files changed, 47 insertions(+), 10 deletions(-)
diff --git a/vgasrc/Kconfig b/vgasrc/Kconfig index 951240c..400e8da 100644 --- a/vgasrc/Kconfig +++ b/vgasrc/Kconfig @@ -50,6 +50,7 @@ menu "VGA ROM" config VGA_COREBOOT depends on COREBOOT bool "coreboot linear framebuffer" + select VGA_EMULATE_TEXT help Build support for a vgabios wrapper around video devices initialized using coreboot native vga init. @@ -83,6 +84,11 @@ menu "VGA ROM"
config VGA_STDVGA_PORTS bool + config VGA_EMULATE_TEXT + bool + help + Support emulating text mode features when only a + framebuffer is available.
config VGA_ALLOCATE_EXTRA_STACK depends on BUILD_VGABIOS diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index a9c6d3a..3acc839 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -14,12 +14,15 @@
static int CBmode VAR16; static struct vgamode_s CBmodeinfo VAR16; +static struct vgamode_s CBemulinfo VAR16; static u32 CBlinelength VAR16;
struct vgamode_s *cbvga_find_mode(int mode) { if (mode == GET_GLOBAL(CBmode)) return &CBmodeinfo; + if (mode == 0x03) + return &CBemulinfo; return NULL; }
@@ -92,6 +95,8 @@ cbvga_save_restore(int cmd, u16 seg, void *data) int cbvga_set_mode(struct vgamode_s *vmode_g, int flags) { + MASK_BDA_EXT(flags, BF_EMULATE_TEXT + , (vmode_g == &CBemulinfo) ? BF_EMULATE_TEXT : 0); if (!(flags & MF_NOCLEARMEM)) { if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) { memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2); @@ -181,9 +186,8 @@ cbvga_setup(void) SET_VGA(CBmodeinfo.depth, bpp); SET_VGA(CBmodeinfo.cwidth, 8); SET_VGA(CBmodeinfo.cheight, 16); - - // Setup BDA and clear screen. - vga_set_mode(GET_GLOBAL(CBmode), 0); + memcpy_far(get_global_seg(), &CBemulinfo + , get_global_seg(), &CBmodeinfo, sizeof(CBemulinfo));
return 0; } diff --git a/vgasrc/vbe.c b/vgasrc/vbe.c index e2aeced..af3d0cc 100644 --- a/vgasrc/vbe.c +++ b/vgasrc/vbe.c @@ -381,9 +381,8 @@ vbe_104f10(struct bregs *regs) case 0x00: regs->bx = 0x0f30; break; - case 0x01: ; - u8 flags = GET_BDA_EXT(flags); - SET_BDA_EXT(flags, (flags & ~BF_PM_MASK) | (regs->bh & BF_PM_MASK)); + case 0x01: + MASK_BDA_EXT(flags, BF_PM_MASK, regs->bh & BF_PM_MASK); break; case 0x02: regs->bh = GET_BDA_EXT(flags) & BF_PM_MASK; diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index d06ebb4..344b3d9 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -1,6 +1,7 @@ #ifndef __VGABIOS_H #define __VGABIOS_H
+#include "config.h" // CONFIG_VGA_EMULATE_TEXT #include "types.h" // u8 #include "farptr.h" // struct segoff_s #include "std/vga.h" // struct video_param_s @@ -70,12 +71,19 @@ struct vga_bda_s { u16 vgamode_offset; } PACKED;
-#define BF_PM_MASK 0x0f +#define BF_PM_MASK 0x0f +#define BF_EMULATE_TEXT 0x10
#define GET_BDA_EXT(var) \ GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) #define SET_BDA_EXT(var, val) \ SET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var, (val)) +#define MASK_BDA_EXT(var, off, on) \ + SET_BDA_EXT(var, (GET_BDA_EXT(var) & ~(off)) | (on)) + +static inline int vga_emulate_text(void) { + return CONFIG_VGA_EMULATE_TEXT && GET_BDA_EXT(flags) & BF_EMULATE_TEXT; +}
// Debug settings #define DEBUG_VGA_POST 1 diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index bb27660..e6a4a4c 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -380,6 +380,8 @@ gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest op.y = dest.y * cheight; op.ylen = clearsize.y * cheight; op.pixels[0] = ca.attr; + if (vga_emulate_text()) + op.pixels[0] = ca.attr >> 4; op.op = GO_MEMSET; handle_gfx_op(&op); } @@ -414,7 +416,25 @@ gfx_write_char(struct vgamode_s *vmode_g op.x = cp.x * 8; int cheight = GET_BDA(char_height); op.y = cp.y * cheight; - int usexor = ca.attr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8; + u8 fgattr = ca.attr, bgattr = 0x00; + int usexor = 0; + if (vga_emulate_text()) { + if (ca.use_attr) { + bgattr = fgattr >> 4; + fgattr = fgattr & 0x0f; + } else { + // Read bottom right pixel of the cell to guess bg color + op.op = GO_READ8; + op.y += cheight-1; + handle_gfx_op(&op); + op.y -= cheight-1; + bgattr = op.pixels[7]; + fgattr = bgattr ^ 0x7; + } + } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) { + usexor = 1; + fgattr &= 0x7f; + } int i; for (i = 0; i < cheight; i++, op.y++) { u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i)); @@ -423,11 +443,11 @@ gfx_write_char(struct vgamode_s *vmode_g handle_gfx_op(&op); int j; for (j = 0; j < 8; j++) - op.pixels[j] ^= (fontline & (0x80>>j)) ? (ca.attr & 0x7f) : 0x00; + op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00; } else { int j; for (j = 0; j < 8; j++) - op.pixels[j] = (fontline & (0x80>>j)) ? ca.attr : 0x00; + op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr; } op.op = GO_WRITE8; handle_gfx_op(&op);
Add mechanism for drawing a cursor to the framebuffer to implement a cursor in software. The timer interrupt is "hooked" so that the cursor can blink. This can be useful for "coreboot native vga".
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- vgasrc/cbvga.c | 4 ++-- vgasrc/vgabios.c | 13 +++++++++--- vgasrc/vgabios.h | 4 ++++ vgasrc/vgaentry.S | 30 ++++++++++++++++++++++++++ vgasrc/vgafb.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vgasrc/vgainit.c | 34 ++++++++++++++++++++++++++++++ 6 files changed, 143 insertions(+), 5 deletions(-)
diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c index 3acc839..1cfb9d3 100644 --- a/vgasrc/cbvga.c +++ b/vgasrc/cbvga.c @@ -95,8 +95,8 @@ cbvga_save_restore(int cmd, u16 seg, void *data) int cbvga_set_mode(struct vgamode_s *vmode_g, int flags) { - MASK_BDA_EXT(flags, BF_EMULATE_TEXT - , (vmode_g == &CBemulinfo) ? BF_EMULATE_TEXT : 0); + u8 emul = vmode_g == &CBemulinfo || GET_GLOBAL(CBmode) == 0x03; + MASK_BDA_EXT(flags, BF_EMULATE_TEXT, emul ? BF_EMULATE_TEXT : 0); if (!(flags & MF_NOCLEARMEM)) { if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) { memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2); diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c index 858f415..f5abda6 100644 --- a/vgasrc/vgabios.c +++ b/vgasrc/vgabios.c @@ -51,7 +51,7 @@ calc_page_size(u8 memmodel, u16 width, u16 height) }
// Determine cursor shape (taking into account possible cursor scaling) -static u16 +u16 get_cursor_shape(void) { u16 cursor_type = GET_BDA(cursor_type); @@ -74,6 +74,7 @@ get_cursor_shape(void) static void set_cursor_shape(u16 cursor_type) { + vgafb_set_swcursor(0); SET_BDA(cursor_type, cursor_type); if (CONFIG_VGA_STDVGA_PORTS) stdvga_set_cursor_shape(get_cursor_shape()); @@ -88,6 +89,8 @@ set_cursor_pos(struct cursorpos cp) if (page > 7) return;
+ vgafb_set_swcursor(0); + // Bios cursor pos SET_BDA(cursor_pos[page], (y << 8) | x);
@@ -103,7 +106,7 @@ set_cursor_pos(struct cursorpos cp) stdvga_set_cursor_pos((int)text_address(cp)); }
-static struct cursorpos +struct cursorpos get_cursor_pos(u8 page) { if (page == 0xff) @@ -129,6 +132,8 @@ set_active_page(u8 page) if (!vmode_g) return;
+ vgafb_set_swcursor(0); + // Calculate memory address of start of page struct cursorpos cp = {0, 0, page}; int address = (int)text_address(cp); @@ -282,6 +287,8 @@ vga_set_mode(int mode, int flags) if (!vmode_g) return VBE_RETURN_STATUS_FAILED;
+ vgafb_set_swcursor(0); + int ret = vgahw_set_mode(vmode_g, flags); if (ret) return ret; @@ -305,7 +312,7 @@ vga_set_mode(int mode, int flags) int cwidth = GET_GLOBAL(vmode_g->cwidth); SET_BDA(video_cols, width / cwidth); SET_BDA(video_rows, (height / cheight) - 1); - SET_BDA(cursor_type, 0x0000); + SET_BDA(cursor_type, vga_emulate_text() ? 0x0607 : 0x0000); } SET_BDA(video_pagesize, calc_page_size(memmodel, width, height)); SET_BDA(crtc_address, CONFIG_VGA_STDVGA_PORTS ? stdvga_get_crtc() : 0); diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index 344b3d9..fd796f2 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -73,6 +73,7 @@ struct vga_bda_s {
#define BF_PM_MASK 0x0f #define BF_EMULATE_TEXT 0x10 +#define BF_SWCURSOR 0x20
#define GET_BDA_EXT(var) \ GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) @@ -112,6 +113,8 @@ struct cursorpos { }; int vga_bpp(struct vgamode_s *vmode_g); u16 calc_page_size(u8 memmodel, u16 width, u16 height); +u16 get_cursor_shape(void); +struct cursorpos get_cursor_pos(u8 page); int bda_save_restore(int cmd, u16 seg, void *data); struct vgamode_s *get_current_mode(void); int vga_set_mode(int mode, int flags); @@ -129,6 +132,7 @@ void vgafb_write_char(struct cursorpos cp, struct carattr ca); struct carattr vgafb_read_char(struct cursorpos cp); void vgafb_write_pixel(u8 color, u16 x, u16 y); u8 vgafb_read_pixel(u16 x, u16 y); +void vgafb_set_swcursor(int enable);
// vbe.c extern u32 VBE_total_memory; diff --git a/vgasrc/vgaentry.S b/vgasrc/vgaentry.S index 5d45380..c05502d 100644 --- a/vgasrc/vgaentry.S +++ b/vgasrc/vgaentry.S @@ -133,3 +133,33 @@ entry_10_extrastack: pushl BREGS_code(%eax) RESTOREBREGS_DSEAX iretw + + // Timer irq handling + DECLFUNC entry_timer_hook +entry_timer_hook: + ENTRY handle_timer_hook + ljmpw *%cs:Timer_Hook_Resume + + // Timer irq handling on extra stack + DECLFUNC entry_timer_hook_extrastack +entry_timer_hook_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movw %cs:ExtraStackSeg, %ds + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-BREGS_size-8), %eax + SAVEBREGS_POP_DSEAX + movl %esp, BREGS_size(%eax) + movw %ss, BREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll handle_timer_hook + + movl %esp, %eax // Restore registers and return + movw BREGS_size+4(%eax), %ss + movl BREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + ljmpw *%cs:Timer_Hook_Resume diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index e6a4a4c..59ddc56 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -454,6 +454,30 @@ gfx_write_char(struct vgamode_s *vmode_g } }
+// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell +void +gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp) +{ + u16 cursor_type = get_cursor_shape(); + u8 start = cursor_type >> 8, end = cursor_type & 0xff; + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = cp.x * 8; + int cheight = GET_BDA(char_height); + op.y = cp.y * cheight + start; + + int i; + for (i = start; i < cheight && i <= end; i++, op.y++) { + op.op = GO_READ8; + handle_gfx_op(&op); + int j; + for (j = 0; j < 8; j++) + op.pixels[j] ^= 0x07; + op.op = GO_WRITE8; + handle_gfx_op(&op); + } +} + // Set the pixel at the given position. void vgafb_write_pixel(u8 color, u16 x, u16 y) @@ -461,6 +485,7 @@ vgafb_write_pixel(u8 color, u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -485,6 +510,7 @@ vgafb_read_pixel(u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return 0; + vgafb_set_swcursor(0);
struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -518,6 +544,7 @@ vgafb_move_chars(struct cursorpos dest struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_move_chars(vmode_g, dest, src, movesize); @@ -538,6 +565,7 @@ vgafb_clear_chars(struct cursorpos dest struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_clear_chars(vmode_g, dest, ca, clearsize); @@ -557,6 +585,7 @@ vgafb_write_char(struct cursorpos cp, struct carattr ca) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_write_char(vmode_g, cp, ca); @@ -579,6 +608,7 @@ vgafb_read_char(struct cursorpos cp) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) goto fail; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { // FIXME gfx mode @@ -595,3 +625,36 @@ fail: ; struct carattr ca2 = {0, 0, 0}; return ca2; } + +// Draw/undraw a cursor on the screen +void +vgafb_set_swcursor(int enable) +{ + if (!vga_emulate_text()) + return; + u8 flags = GET_BDA_EXT(flags); + if (!!(flags & BF_SWCURSOR) == enable) + // Already in requested mode. + return; + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + struct cursorpos cp = get_cursor_pos(0xff); + if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows) + || GET_BDA(cursor_type) >= 0x2000) + // Cursor not visible + return; + + SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0)); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_set_swcursor(vmode_g, enable, cp); + return; + } + + // In text mode, swap foreground and background attributes for cursor + void *dest_far = text_address(cp) + 1; + u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far); + attr = (attr >> 4) | (attr << 4); + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr); +} diff --git a/vgasrc/vgainit.c b/vgasrc/vgainit.c index 6ef19e1..8d12261 100644 --- a/vgasrc/vgainit.c +++ b/vgasrc/vgainit.c @@ -88,6 +88,38 @@ allocate_extra_stack(void)
/**************************************************************** + * Timer hook + ****************************************************************/ + +struct segoff_s Timer_Hook_Resume VAR16 VISIBLE16; + +void VISIBLE16 +handle_timer_hook(void) +{ + if (!vga_emulate_text()) + return; + vgafb_set_swcursor(GET_BDA(timer_counter) % 18 < 9); +} + +static void +hook_timer_irq(void) +{ + if (!CONFIG_VGA_EMULATE_TEXT) + return; + extern void entry_timer_hook(void); + extern void entry_timer_hook_extrastack(void); + struct segoff_s oldirq = GET_IVT(0x08); + struct segoff_s newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook); + if (CONFIG_VGA_ALLOCATE_EXTRA_STACK && GET_GLOBAL(ExtraStackSeg)) + newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook_extrastack); + dprintf(1, "Hooking hardware timer irq (old=%x new=%x)\n" + , oldirq.segoff, newirq.segoff); + SET_VGA(Timer_Hook_Resume, oldirq); + SET_IVT(0x08, newirq); +} + + +/**************************************************************** * VGA post ****************************************************************/
@@ -150,6 +182,8 @@ vga_post(struct bregs *regs)
allocate_extra_stack();
+ hook_timer_irq(); + SET_VGA(HaveRunInit, 1);
// Fixup checksum
On Fri, Oct 31, 2014 at 02:57:59PM -0400, Kevin O'Connor wrote:
This is a resend of two previous patches that improve the text emulation of the "coreboot native vga" SeaVGABIOS support. The main difference in this version is that the support requires both a compile time define (CONFIG_VGA_EMULATE_TEXT) as well as runtime enablement. The net effect of this is that the code wont try to blink the cursor if a bootloader explicitly switches into graphics mode.
FYI, I have pushed this series to the main seabios repo.
-Kevin