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



Reply via email to