When bootloader sets a linear framebuffer mode and passes the required info to Hurd via multiboot info table, we can use this framebuffer as is. Otherwise, fall back to EGA text mode as before. --- console-client/Makefile | 3 +- console-client/fb.c | 601 +++++++++++++++++++++++++++++++++++ console-client/fb.h | 74 +++++ console-client/vga-hw.h | 7 + console-client/vga-support.c | 87 ++++- console-client/vga-support.h | 60 ++++ console-client/vga.c | 61 +++- 7 files changed, 862 insertions(+), 31 deletions(-) create mode 100644 console-client/fb.c create mode 100644 console-client/fb.h
diff --git a/console-client/Makefile b/console-client/Makefile index b991cd73..711258c7 100644 --- a/console-client/Makefile +++ b/console-client/Makefile @@ -22,7 +22,7 @@ makemode := utilities targets = console CONSOLE_SRCS = console.c timer.c driver.c trans.c -VGA_SO_SRCS = bdf.c vga-dynafont.c vga-dynacolor.c vga-support.c vga.c +VGA_SO_SRCS = bdf.c vga-dynafont.c vga-dynacolor.c vga-support.c vga.c fb.c PC_KBD_SO_SRCS = pc-kbd.c kbd-repeat.c PC_MOUSE_SO_SRCS = pc-mouse.c GENERIC_SPEAKER_SO_SRCS = generic-speaker.c @@ -60,6 +60,7 @@ console: $(CONSOLE_SRCS:.c=.o) \ modules = vga pc_kbd generic_speaker pc_mouse current_vcs vga-CPPFLAGS = -DDEFAULT_VGA_FONT_DIR=\"${datadir}/hurd/\" +fb-CPPFLAGS = -DDEFAULT_VGA_FONT_DIR=\"${datadir}/hurd/\" vga.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(VGA_SO_SRCS)) pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_KBD_SO_SRCS)) \ kdioctlServer_pic.o diff --git a/console-client/fb.c b/console-client/fb.c new file mode 100644 index 00000000..5bf8ac9b --- /dev/null +++ b/console-client/fb.c @@ -0,0 +1,601 @@ +/* + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <assert-backtrace.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <unistd.h> +#include <iconv.h> +#include <argp.h> +#include <string.h> +#include <stdint.h> + +#include <sys/io.h> +#include <sys/mman.h> +#include <sys/fcntl.h> +#include <pthread.h> +#include <hurd/console.h> + +#include "driver.h" + +#include "fb.h" +#include "vga-hw.h" +#include "vga-support.h" +#include "bdf.h" +#include "unicode.h" + +/* The font file. */ +#define DEFAULT_VGA_FONT DEFAULT_VGA_FONT_DIR "vga-system.bdf" +static char *fb_display_font; + +/* Is set to 1 if the cursor state should be hidden. */ +static int cursor_hidden; +static int cursor_state; +static int cursor_pos = 0; + +static int current_width; +static int current_height; + + + +static void +blit_glyph(bdf_font_t font, char *mem, int ch, uint32_t rgb, int width, int bpp) +{ + int w, h; + + struct bdf_glyph *gl; + + gl = bdf_find_glyph (font, ch, 0); + if (!gl) + gl = bdf_find_glyph (font, -1, ch); + if (!gl) + gl = bdf_find_glyph (font, '?', 0); + + assert_backtrace(gl != 0); + + for (h = 0; h < vga_hc; h++) + { + for (w = 0; w < vga_wc; w++) + { + char *pixel = mem + bpp/8 * (w + width * h); + uint32_t colour = (gl->bitmap[h] & (1 << (7-w))) ? rgb : 0; + pixel[0] = (colour >> 16) & 0xff; + pixel[1] = (colour >> 8) & 0xff; + pixel[2] = colour & 0xff; + } + } +} + +static void +blit_glyph_xor(bdf_font_t font, char *mem, int ch, uint32_t rgb, int width, int bpp) +{ + int w, h; + + struct bdf_glyph *gl; + gl = bdf_find_glyph (font, ch, 0); + if (!gl) + gl = bdf_find_glyph (font, -1, ch); + if (!gl) + gl = bdf_find_glyph (font, '?', 0); + + assert_backtrace(gl != 0); + + for (h = 0; h < vga_hc; h++) + { + for (w = 0; w < vga_wc; w++) + { + char *pixel = mem + bpp/8 * (w + width * h); + uint32_t colour = (gl->bitmap[h] & (1 << (7-w))) ? rgb : 0; + pixel[0] ^= (colour >> 16) & 0xff; + pixel[1] ^= (colour >> 8) & 0xff; + pixel[2] ^= colour & 0xff; + } + } +} + +static error_t +fb_init(void) +{ + error_t err; + int fd; + + fd = open ("/dev/mem", O_RDWR); + if (fd >= 0) + { + vga_videomem = mmap (0, vga_width * vga_height * vga_bpp/8, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, vga_fb); + err = errno; + close (fd); + if (vga_videomem == (void *) -1) + return err; + } + else + return errno; + + /* Clear screen */ + memset (vga_videomem, 0, vga_width * vga_height * vga_bpp/8); + return 0; +} + +static void +fb_fini(void) +{ + munmap (vga_videomem, vga_width * vga_height * vga_bpp/8); +} + +/* Start the driver. */ +error_t +fb_display_start (void *handle) +{ + error_t err; + struct fb_display *disp = handle; + FILE *font_file; + + err = fb_init (); + if (err) + return err; + +#define LOAD_FONT(x,y,z) \ + do { \ + font_file = fopen (fb_display_##x ?: DEFAULT_VGA_##y, "r"); \ + if (font_file) \ + { \ + bdf_error_t bdferr = bdf_read (font_file, &z, NULL); \ + if (bdferr) \ + { \ + z = NULL; \ + err = ENOSYS; \ + } \ + else \ + bdf_sort_glyphs (z); \ + fclose (font_file); \ + } \ + else \ + err = ENOSYS; \ + } while (0) + + LOAD_FONT (font, FONT, disp->font); + if (err) + { + fb_fini (); + free (disp); + return err; + } + + err = driver_add_display (&fb_display_ops, disp); + if (err) + { + fb_fini (); + free (disp); + } + return err; +} + +/* Destroy the display HANDLE. */ +error_t +fb_display_fini (void *handle, int force) +{ + struct fb_display *disp = handle; + + driver_remove_display (&fb_display_ops, disp); + bdf_destroy (disp->font); + free (disp); + fb_fini (); + free (fb_display_font); + + return 0; +} + +/* First 8 colours of EGA palette */ +uint32_t console_rgb[8] = { + 0x000000, /* black */ + 0x0000aa, /* blue */ + 0x00aa00, /* green */ + 0x00aaaa, /* cyan */ + 0xaa0000, /* red */ + 0xaa00aa, /* magenta */ + 0xffff55, /* yellow */ + 0xaaaaaa /* white */ +}; + + +static void +hide_mousecursor (struct fb_display *disp) +{ + char *oldpos = vga_videomem + vga_bpp/8 * ((int) disp->mousecursor.posy * vga_hc * disp->width + + (int) disp->mousecursor.posx * vga_wc); + + if (!disp->mousecursor.visible) + return; + + /* First remove the old cursor. */ + blit_glyph_xor (disp->font, oldpos, 'X', 0x00ff00, disp->width, vga_bpp); + disp->mousecursor.visible = 0; +} + + +static void +draw_mousecursor (struct fb_display *disp) +{ + char *newpos = vga_videomem + vga_bpp/8 * ((int) disp->mousecursor.posy * vga_hc * disp->width + + (int) disp->mousecursor.posx * vga_wc); + + if (disp->mousecursor.visible) + return; + + /* Draw the new cursor. */ + blit_glyph_xor (disp->font, newpos, 'X', 0x00ff00, disp->width, vga_bpp); + + disp->mousecursor.visible = 1; +} + + +static void +hide_cursor(struct fb_display *disp) +{ + int x, y; + char *curpos; + + if (cursor_hidden) + return; + + /* Remove old cursor */ + x = cursor_pos % (disp->width/vga_wc); + y = cursor_pos / (disp->width/vga_wc); + curpos = vga_videomem + vga_bpp/8 * (y * vga_hc * disp->width + x * vga_wc); + blit_glyph_xor (disp->font, curpos, '_', 0xffffff, disp->width, vga_bpp); + cursor_hidden = 1; +} + +static void +draw_cursor(struct fb_display *disp) +{ + int x, y; + char *curpos; + + if (!cursor_hidden) + return; + + /* Add new cursor */ + x = cursor_pos % (disp->width/vga_wc); + y = cursor_pos / (disp->width/vga_wc); + curpos = vga_videomem + vga_bpp/8 * (y * vga_hc * disp->width + x * vga_wc); + blit_glyph_xor (disp->font, curpos, '_', 0xffffff, disp->width, vga_bpp); + cursor_hidden = 0; +} + +/* Set the cursor's state to STATE on display HANDLE. */ +static error_t +fb_display_set_cursor_status (void *handle, uint32_t state) +{ + struct fb_display *disp = handle; + + cursor_state = state; + + if (!state) + hide_cursor (disp); + else + draw_cursor (disp); + + return 0; +} + + +/* Set the cursor's position on display HANDLE to column COL and row + ROW. */ +static error_t +fb_display_set_cursor_pos (void *handle, uint32_t col, uint32_t row) +{ + struct fb_display *disp = handle; + + /* If the cursor did not move from the character position, don't + bother about updating the cursor position. */ + if (cursor_state && (col == (cursor_pos % (disp->width/vga_wc))) + && (row == (cursor_pos / (disp->width/vga_wc)))) + return 0; + + hide_cursor (disp); + + cursor_pos = col + disp->width/vga_wc * row; + + if (cursor_state) + draw_cursor (disp); + + return 0; +} + +/* Deallocate any scarce resources occupied by the LENGTH characters + from column COL and row ROW. */ +static error_t +fb_display_clear (void *handle, size_t length, uint32_t col, uint32_t row) +{ + int len; + struct fb_display *disp = handle; + struct fbchr *refpos = &disp->refmatrix[row][0]; + char *pos = vga_videomem; + + /* The column can be outside the physical screen, in that case + adjust the position. */ + if (col >= disp->width/vga_wc) + { + len = col; + col = len % disp->width/vga_wc; + row += len / disp->width/vga_wc; + } + refpos += col; + pos += vga_bpp/8 * (row*vga_hc * disp->width + col*vga_wc); + + /* The first row is not in the physical screen, nothing has to be + done. */ + if (row >= disp->height/vga_hc) + return 0; + + /* The length cannot be used. Recalculate it to wrap the lines. */ + len = length / current_width; + length = (length % current_width) + len * disp->width/vga_wc; + + /* Make sure the end of length is still in the physical screen. */ + if (length > (disp->width/vga_wc * disp->height/vga_hc - (row * disp->width/vga_wc + col)) - col) + length = disp->width/vga_wc * disp->height/vga_hc - (row * disp->width/vga_wc + col) - col; + + while (length > 0) + { + refpos->used = 0; + refpos++; + length--; + /* wipe char */ + blit_glyph (disp->font, pos, ' ', 0, disp->width, vga_bpp); + if ((length % (disp->width/vga_wc)) == 0) + { + /* pos just wrapped by one line, bump it down more lines to match height of glyph */ + pos += vga_bpp/8 * (disp->width * (vga_hc-1)); + } + else + { + /* move pointer one glyph width ahead */ + pos += vga_wc * vga_bpp/8; + } + } + return 0; +} + +/* Scroll the display by the desired number of lines. The area that becomes + free will be filled in a subsequent write call. */ +static error_t +fb_display_scroll (void *handle, int delta) +{ + struct fb_display *disp = handle; + int pixels, chars, nextdelta = 0; + char *pos = vga_videomem; + uint32_t r; + + if (delta > disp->height/vga_hc) + { + nextdelta = delta - disp->height/vga_hc; + delta = disp->height/vga_hc; + } + else if (delta < -(disp->height/vga_hc)) + { + nextdelta = delta + disp->height/vga_hc; + delta = -(disp->height/vga_hc); + } + + pixels = abs(delta)*vga_hc * disp->width; + chars = abs(delta) * disp->width/vga_wc; + + hide_mousecursor (disp); + hide_cursor (disp); + + /* XXX: If the virtual console is bigger than the physical console it is + impossible to scroll because the data to scroll is not in memory. */ + if (current_height > disp->height/vga_hc) + return ENOTSUP; + + if (delta > 0) + { + memmove (vga_videomem, vga_videomem + vga_bpp/8 * pixels, + vga_bpp/8 * disp->width * (disp->height - delta*vga_hc)); + } + else + { + memmove (vga_videomem + vga_bpp/8 * pixels, vga_videomem, + vga_bpp/8 * disp->width * (disp->height + delta*vga_hc)); + } + + if (delta > 0) + { + r = disp->height/vga_hc - delta; + memmove (&disp->refmatrix[0][0], &disp->refmatrix[0][0] + chars, + sizeof (struct fbchr) * disp->width/vga_wc * r); + pos += vga_bpp/8 * (r*vga_hc * disp->width); + } + else + { + r = 0; + memmove (&disp->refmatrix[0][0] + chars, &disp->refmatrix[0][0], + sizeof (struct fbchr) * disp->width/vga_wc * (disp->height/vga_hc + delta)); + } + + fb_display_clear (disp, chars, 0, r); + + if (nextdelta) + fb_display_scroll (handle, nextdelta); + + return 0; +} + + + +/* Write the text STR with LENGTH characters to column COL and row + ROW. */ +static error_t +fb_display_write (void *handle, conchar_t *str, size_t length, + uint32_t col, uint32_t row) +{ + struct fb_display *disp = handle; + char *pos; + struct fbchr *refpos = &disp->refmatrix[row][col]; + char *mouse_cursor_pos; + + hide_mousecursor (disp); + hide_cursor (disp); + + /* The starting column is outside the physical screen. */ + if (disp->width/vga_wc < current_width && col >= disp->width/vga_wc) + { + size_t skip = current_width - disp->width/vga_wc; + str += skip; + length -= skip; + col = 0; + row += skip / current_width + 1; + } + + mouse_cursor_pos = vga_videomem + vga_bpp/8 + * ((int) disp->mousecursor.posy*vga_hc + * disp->width + (int) disp->mousecursor.posx*vga_wc); + + while (length--) + { + int charval = str->chr; + + /* The virtual console is smaller than the physical screen. */ + if (col >= current_width) + { + size_t skip = disp->width/vga_wc - current_width; + refpos += skip; + col = 0; + row += skip / disp->width/vga_wc + 1; + } + /* The virtual console is bigger than the physical console. */ + else if (disp->width/vga_wc < current_width && col == disp->width/vga_wc) + { + size_t skip = current_width - disp->width/vga_wc; + str += skip; + length -= skip; + col = skip % current_width; + row += skip / current_width; + } + + /* The screen is filled until the bottom of the screen. */ + if (row >= disp->height/vga_hc) + return 0; + + pos = vga_videomem + (col*vga_wc + (row*vga_hc * disp->width)) * vga_bpp/8; + + /* blit glyph to screen */ + blit_glyph(disp->font, pos, charval, console_rgb[str->attr.fgcol], disp->width, vga_bpp); + + if (pos == mouse_cursor_pos) + disp->mousecursor.visible = 0; + + /* Perform reference counting. */ + refpos->used = 1; + refpos->chr = charval; + refpos->fgcol = str->attr.fgcol; + refpos++; + col++; + + /* Go to next character. */ + str++; + } + return 0; +} + +static error_t +fb_set_dimension (void *handle, unsigned int width, unsigned int height) +{ + if (current_width && current_height) + fb_display_clear (handle, current_width * current_height, 0, 0); + + current_width = width; + current_height = height; + + return 0; +} + + +static error_t +fb_display_update (void *handle) +{ + struct fb_display *disp = handle; + + if (disp->mousecursor.enabled) + draw_mousecursor (disp); + + if (cursor_state) + draw_cursor (disp); + + return 0; +} + + +static error_t +fb_set_mousecursor_pos (void *handle, float x, float y) +{ + struct fb_display *disp = handle; + + /* If the mouse did not move from the character position, don't + bother about updating the cursor position. */ + if (disp->mousecursor.visible && x == (int) disp->mousecursor.posx + && y == (int) disp->mousecursor.posy) + return 0; + + hide_mousecursor (disp); + + disp->mousecursor.posx = x; + disp->mousecursor.posy = y; + + if (disp->mousecursor.enabled) + draw_mousecursor (disp); + + return 0; +} + + +static error_t +fb_set_mousecursor_status (void *handle, int status) +{ + struct fb_display *disp = handle; + + disp->mousecursor.enabled = status; + if (!status) + hide_mousecursor (disp); + else + draw_mousecursor (disp); + + return 0; +} + + + +struct display_ops fb_display_ops = + { + fb_display_set_cursor_pos, + fb_display_set_cursor_status, + fb_display_scroll, + fb_display_clear, + fb_display_write, + fb_display_update, + NULL, + NULL, + fb_set_dimension, + fb_set_mousecursor_pos, + fb_set_mousecursor_status + }; diff --git a/console-client/fb.h b/console-client/fb.h new file mode 100644 index 00000000..23e0a51a --- /dev/null +++ b/console-client/fb.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef _FB_H_ +#define _FB_H_ 1 + +#include "display.h" +#include "vga-dynafont.h" +#include "vga-dynacolor.h" +#include "vga-hw.h" + +extern struct display_ops fb_display_ops; + +error_t fb_display_start (void *handle); +error_t fb_display_fini (void *handle, int force); + +struct fbchr +{ + unsigned int used : 1; + unsigned int chr : 9; + unsigned int fgcol: 3; +}; + +typedef struct fb_mousecursor +{ + float posx; + float posy; + int visible; + int enabled; +} fb_mousecursor_t; + +struct fb_display +{ + /* The font for this display. */ + bdf_font_t font; + + int df_size; + int df_width; + + /* The color palette. */ + dynacolor_t dc; + + int width; + int height; + + /* The state of the mouse cursor. */ + fb_mousecursor_t mousecursor; + + /* The position of the cursor, x_pos + y_pos * width (in characters) */ + int cursor_pos; + + /* Remember for each cell on the display the glyph written to it and + the colours assigned. 0 means unassigned. */ + + struct fbchr refmatrix[VGA_VIDEO_MEM_MAX_H / VGA_PIXELS_H][VGA_VIDEO_MEM_MAX_W / VGA_PIXELS_W]; +}; + +#endif diff --git a/console-client/vga-hw.h b/console-client/vga-hw.h index b4212e62..71ffb721 100644 --- a/console-client/vga-hw.h +++ b/console-client/vga-hw.h @@ -24,6 +24,13 @@ #define VGA_VIDEO_MEM_BASE_ADDR 0x0b8000 #define VGA_VIDEO_MEM_LENGTH 0x004000 +#define VGA_VIDEO_MEM_MAX_W 1920 +#define VGA_VIDEO_MEM_MAX_H 1080 +#define VGA_VIDEO_MEM_MAX_BPP 32 + +#define VGA_PIXELS_W 8 +#define VGA_PIXELS_H 16 + #define VGA_FONT_BUFFER 8 #define VGA_FONT_SIZE 256 #define VGA_FONT_HEIGHT 32 diff --git a/console-client/vga-support.c b/console-client/vga-support.c index 32ede46d..0fa229d4 100644 --- a/console-client/vga-support.c +++ b/console-client/vga-support.c @@ -25,6 +25,9 @@ #include <sys/io.h> #include <sys/mman.h> #include <sys/types.h> +#include <device/device.h> +#include <hurd.h> +#include <mach.h> #include <string.h> #include <stdlib.h> @@ -32,8 +35,15 @@ #include "vga-support.h" -/* The base of the video memory mapping. */ +/* The parameters of the video memory mapping. */ char *vga_videomem; +size_t vga_fb; +int vga_pitch; +int vga_width; +int vga_height; +int vga_bpp; +int vga_wc; +int vga_hc; /* The saved state of the VGA card. */ struct vga_state @@ -56,14 +66,65 @@ struct vga_state unsigned char attr_mode; /* Alignment is required by some "hardware", and optimizes transfers. */ - char videomem[2 * 80 * 25] + char videomem[VGA_VIDEO_MEM_MAX_W * VGA_VIDEO_MEM_MAX_H * VGA_VIDEO_MEM_MAX_BPP / 8] __attribute__ ((aligned (__BIGGEST_ALIGNMENT__))); - unsigned char fontmem[2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT] + unsigned char fontmem[VGA_VIDEO_MEM_MAX_BPP / 8 * VGA_FONT_SIZE * VGA_FONT_HEIGHT] __attribute__ ((aligned (__BIGGEST_ALIGNMENT__))); }; static struct vga_state *vga_state; +error_t +vga_get_multiboot_params (void) +{ + error_t ret = 0; + mach_port_t master_device, mbinfo_dev; + struct multiboot_raw_info mbi; + char buf[sizeof(struct multiboot_raw_info)] = {0}; + char *bufptr = &buf[0]; + uint32_t bytes = sizeof(struct multiboot_raw_info); + uint32_t bytes_read = 0; + + ret = get_privileged_ports (NULL, &master_device); + if (ret) + goto fail; + + ret = device_open (master_device, D_READ, "mbinfo", &mbinfo_dev); + mach_port_deallocate (mach_task_self (), master_device); + if (ret) + goto fail; + + ret = device_read (mbinfo_dev, D_READ, 0, bytes, &bufptr, &bytes_read); + mach_port_deallocate (mach_task_self (), mbinfo_dev); + if (ret) + goto fail; + + if (bytes_read != bytes) + goto fail; + + memcpy((void *)&mbi, (void *)bufptr, sizeof(struct multiboot_raw_info)); + + vga_fb = mbi.fb_info.framebuffer_addr; + vga_pitch = mbi.fb_info.framebuffer_pitch; + vga_width = mbi.fb_info.framebuffer_width; + vga_height = mbi.fb_info.framebuffer_height; + vga_bpp = mbi.fb_info.framebuffer_bpp; + vga_wc = VGA_PIXELS_W; + vga_hc = VGA_PIXELS_H; + return ret; + +fail: + /* Fall back to EGA text mode */ + vga_fb = VGA_VIDEO_MEM_BASE_ADDR; + vga_pitch = 160; + vga_width = 80; + vga_height = 25; + vga_bpp = 16; + vga_wc = 1; + vga_hc = 1; + return ret; +} + error_t vga_init (void) @@ -87,8 +148,8 @@ vga_init (void) fd = open ("/dev/mem", O_RDWR); if (fd >= 0) { - vga_videomem = mmap (0, VGA_VIDEO_MEM_LENGTH, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, VGA_VIDEO_MEM_BASE_ADDR); + vga_videomem = mmap (0, vga_width * vga_height * vga_bpp, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, vga_fb); err = errno; close (fd); if (vga_videomem == (void *) -1) @@ -147,13 +208,13 @@ vga_init (void) outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG); outb (VGA_GFX_MODE_HOSTOE, VGA_GFX_DATA_REG); - memcpy (vga_state->videomem, vga_videomem, 2 * 80 * 25); + memcpy (vga_state->videomem, vga_videomem, vga_width * vga_height * vga_bpp / 8); vga_read_font_buffer (0, 0, vga_state->fontmem, - 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); + vga_bpp/8 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); - /* 80 cols, 25 rows, two bytes per cell and twice because with lower + /* X cols, Y rows, bpp/8 bytes per cell and twice because with lower max scan line we get more lines on the screen. */ - memset (vga_videomem, 0, 80 * 25 * 2 * 2); + memset (vga_videomem, 0, vga_width * vga_height * vga_bpp / 4); return 0; } @@ -166,8 +227,8 @@ vga_fini (void) { /* Recover the saved state. */ vga_write_font_buffer (0, 0, vga_state->fontmem, - 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); - memcpy (vga_videomem, vga_state->videomem, 2 * 80 * 25); + vga_bpp/8 * VGA_FONT_SIZE * VGA_FONT_HEIGHT); + memcpy (vga_videomem, vga_state->videomem, vga_width * vga_height * vga_bpp / 8); /* Restore the registers. */ outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG); @@ -207,7 +268,7 @@ vga_fini (void) outb (0x00, VGA_ATTR_ADDR_DATA_REG); ioperm (VGA_MIN_REG, VGA_MAX_REG - VGA_MIN_REG + 1, 0); - munmap (vga_videomem, VGA_VIDEO_MEM_LENGTH); + munmap (vga_videomem, vga_width * vga_height * vga_bpp); } @@ -225,7 +286,7 @@ vga_read_write_font_buffer (int write, int buffer, int index, char saved_gfx_misc; int offset = buffer * VGA_FONT_SIZE + index * VGA_FONT_HEIGHT; - assert_backtrace (offset >= 0 && offset + datalen <= VGA_VIDEO_MEM_LENGTH); + assert_backtrace (offset >= 0 && offset + datalen <= vga_width * vga_height * vga_bpp); /* Select plane 2 for sequential writing. You might think it is not necessary for reading, but it is. Likewise for read settings diff --git a/console-client/vga-support.h b/console-client/vga-support.h index 17c0b5fd..a3de5aea 100644 --- a/console-client/vga-support.h +++ b/console-client/vga-support.h @@ -23,6 +23,57 @@ #include <errno.h> #include <sys/types.h> +#include <stdint.h> + +struct multiboot_framebuffer_info { + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + uint8_t framebuffer_type; + union + { + struct + { + uint32_t framebuffer_palette_addr; + uint16_t framebuffer_palette_num_colors; + }; + struct + { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; +} __attribute__((packed)); + +/* + * Multiboot information structure as passed by the boot loader. + */ +struct multiboot_raw_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t unused0; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + uint32_t shdr_num; + uint32_t shdr_size; + uint32_t shdr_addr; + uint32_t shdr_strndx; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t unused1[9]; + struct multiboot_framebuffer_info fb_info; +} __attribute__((packed)); /* The VGA interface does not do locking on its own, for maximum @@ -32,6 +83,15 @@ /* The mapped video memory. */ extern char *vga_videomem; +extern size_t vga_fb; +extern int vga_width; +extern int vga_height; +extern int vga_bpp; +extern int vga_wc; +extern int vga_hc; + +/* Get the multiboot video parameters */ +error_t vga_get_multiboot_params (void); /* Initialize the VGA hardware and set up the permissions and memory mappings. */ diff --git a/console-client/vga.c b/console-client/vga.c index e954013d..67e7e3f2 100644 --- a/console-client/vga.c +++ b/console-client/vga.c @@ -37,6 +37,7 @@ #include "driver.h" #include "timer.h" +#include "fb.h" #include "vga-hw.h" #include "vga-support.h" #include "bdf.h" @@ -45,8 +46,6 @@ #include "unicode.h" -#define VGA_DISP_WIDTH 80 -#define VGA_DISP_HEIGHT 25 /* The font file. */ #define DEFAULT_VGA_FONT DEFAULT_VGA_FONT_DIR "vga-system.bdf" @@ -87,15 +86,19 @@ static int cursor_state; /* Is set to 1 if the cursor moved out of the physical screen and the cursor state should be hidden. */ static int cursor_hidden; + +/* Forward declaration */ +struct driver_ops driver_vga_ops; + + struct refchr { unsigned int used : 1; unsigned int chr : 9; - unsigned int attr : 8; + unsigned int attr: 8; }; - typedef struct vga_mousecursor { float posx; @@ -129,7 +132,7 @@ struct vga_display /* Remember for each cell on the display the glyph written to it and the colors (in the upper byte) assigned. 0 means unassigned. */ - struct refchr refmatrix[VGA_DISP_HEIGHT][VGA_DISP_WIDTH]; + struct refchr refmatrix[VGA_VIDEO_MEM_MAX_H / VGA_PIXELS_H][VGA_VIDEO_MEM_MAX_W / VGA_PIXELS_W]; }; @@ -279,9 +282,12 @@ vga_display_init (void **handle, int no_exit, int argc, char *argv[], int *next) { error_t err; - struct vga_display *disp; + struct vga_display *vgadisp; + struct fb_display *fbdisp; int pos = 1; + vga_get_multiboot_params(); + /* XXX Assert that we are called only once. */ pthread_mutex_init (&vga_display_lock, NULL); timer_clear (&vga_display_timer); @@ -294,22 +300,43 @@ vga_display_init (void **handle, int no_exit, int argc, char *argv[], if (err && err != EINVAL) return err; - /* Create and initialize the display structure as much as - possible. */ - disp = calloc (1, sizeof *disp); - if (!disp) - return ENOMEM; + if (vga_fb == VGA_VIDEO_MEM_BASE_ADDR) + { + /* EGA text mode */ + vgadisp = calloc (1, sizeof *vgadisp); + if (!vgadisp) + return ENOMEM; + + vgadisp->df_size = vga_display_max_glyphs ? 512 : 256; + vgadisp->df_width = vga_display_font_width; + vgadisp->width = vga_width; + vgadisp->height = vga_height; - disp->df_size = vga_display_max_glyphs ? 512 : 256; - disp->df_width = vga_display_font_width; - disp->width = VGA_DISP_WIDTH; - disp->height = VGA_DISP_HEIGHT; + *handle = vgadisp; + } + else + { + /* Linear framebuffer! */ + fbdisp = calloc (1, sizeof *fbdisp); + if (!fbdisp) + return ENOMEM; + + fbdisp->df_size = vga_display_max_glyphs ? 512 : 256; + fbdisp->df_width = vga_display_font_width; + fbdisp->width = vga_width; + fbdisp->height = vga_height; + + /* dynamically switch drivers */ + driver_vga_ops.start = fb_display_start; + driver_vga_ops.fini = fb_display_fini; + driver_vga_ops.restore_status = NULL; + + *handle = fbdisp; + } - *handle = disp; return 0; } - /* Start the driver. */ static error_t vga_display_start (void *handle) -- 2.45.2