add support to efifb for drawing a console on a coreboot framebuffer
authorjcs <jcs@openbsd.org>
Tue, 21 Jun 2016 15:24:55 +0000 (15:24 +0000)
committerjcs <jcs@openbsd.org>
Tue, 21 Jun 2016 15:24:55 +0000 (15:24 +0000)
if coreboot's memory table is found, it has a framebuffer entry, and
there is no previously attached efi, vga, or serial console.

useful on chromebooks that have no legacy vga device to get an early
console before inteldrm(4) attaches or, for newer chipsets, a full
console and X with wsfb(4).

ok kettenis

sys/arch/amd64/amd64/efifb.c
sys/arch/amd64/amd64/mainbus.c
sys/arch/amd64/amd64/wscons_machdep.c
sys/arch/amd64/include/efifbvar.h

index efc7334..2cd59e1 100644 (file)
@@ -1,7 +1,8 @@
-/*     $OpenBSD: efifb.c,v 1.8 2015/12/01 18:42:56 yasuoka Exp $       */
+/*     $OpenBSD: efifb.c,v 1.9 2016/06/21 15:24:55 jcs Exp $   */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
+ * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <machine/biosvar.h>
 #include <machine/efifbvar.h>
 
+/* coreboot tables */
+
+struct cb_header {
+       union {
+               uint8_t signature[4]; /* "LBIO" */
+               uint32_t signature32;
+       };
+       uint32_t        header_bytes;
+       uint32_t        header_checksum;
+       uint32_t        table_bytes;
+       uint32_t        table_checksum;
+       uint32_t        table_entries;
+};
+
+struct cb_framebuffer {
+       uint64_t        physical_address;
+       uint32_t        x_resolution;
+       uint32_t        y_resolution;
+       uint32_t        bytes_per_line;
+       uint8_t         bits_per_pixel;
+       uint8_t         red_mask_pos;
+       uint8_t         red_mask_size;
+       uint8_t         green_mask_pos;
+       uint8_t         green_mask_size;
+       uint8_t         blue_mask_pos;
+       uint8_t         blue_mask_size;
+       uint8_t         reserved_mask_pos;
+       uint8_t         reserved_mask_size;
+};
+
+struct cb_entry {
+       uint32_t        tag;
+#define CB_TAG_VERSION          0x0004
+#define CB_TAG_FORWARD          0x0011
+#define CB_TAG_FRAMEBUFFER      0x0012
+       uint32_t        size;
+       union {
+               char    string[0];
+               uint64_t forward;
+               struct cb_framebuffer fb;
+       } u;
+};
+
 struct efifb {
        struct rasops_info       rinfo;
        int                      depth;
        paddr_t                  paddr;
        psize_t                  psize;
+
+       struct cb_framebuffer    cb_table_fb;
 };
 
 struct efifb_softc {
@@ -45,6 +91,7 @@ struct efifb_softc {
 int     efifb_match(struct device *, void *, void *);
 void    efifb_attach(struct device *, struct device *, void *);
 void    efifb_rasops_preinit(struct efifb *);
+void    efifb_rasops_init(void);
 int     efifb_ioctl(void *, u_long, caddr_t, int, struct proc *);
 paddr_t         efifb_mmap(void *, off_t, int);
 int     efifb_alloc_screen(void *, const struct wsscreen_descr *, void **,
@@ -55,7 +102,9 @@ int   efifb_show_screen(void *, void *, int, void (*cb) (void *, int, int),
 int     efifb_list_font(void *, struct wsdisplay_font *);
 int     efifb_load_font(void *, void *, struct wsdisplay_font *);
 
-struct cfattach efifb_ca = {
+struct cb_framebuffer *cb_find_fb(paddr_t);
+
+const struct cfattach efifb_ca = {
        sizeof(struct efifb_softc), efifb_match, efifb_attach, NULL
 };
 
@@ -112,7 +161,8 @@ efifb_attach(struct device *parent, struct device *self, void *aux)
        bus_space_handle_t       ioh;
        long                     defattr;
 
-       printf("\n");
+       printf(": %dx%d, %dbpp\n", efifb_console.rinfo.ri_width,
+           efifb_console.rinfo.ri_height, efifb_console.rinfo.ri_depth);
 
        if (1) {        /* XXX console */
                aa.console = 1;
@@ -151,16 +201,29 @@ efifb_rasops_preinit(struct efifb *fb)
 #define bmpos(_x) (ffs(_x) - 1)
        struct rasops_info      *ri = &fb->rinfo;
 
-       ri->ri_width = bios_efiinfo->fb_width;
-       ri->ri_height = bios_efiinfo->fb_height;
-       ri->ri_depth = fb->depth;
-       ri->ri_stride = bios_efiinfo->fb_pixpsl * (fb->depth / 8);
-       ri->ri_rnum = bmnum(bios_efiinfo->fb_red_mask);
-       ri->ri_rpos = bmpos(bios_efiinfo->fb_red_mask);
-       ri->ri_gnum = bmnum(bios_efiinfo->fb_green_mask);
-       ri->ri_gpos = bmpos(bios_efiinfo->fb_green_mask);
-       ri->ri_bnum = bmnum(bios_efiinfo->fb_blue_mask);
-       ri->ri_bpos = bmpos(bios_efiinfo->fb_blue_mask);
+       if (efifb_console.cb_table_fb.x_resolution) {
+               ri->ri_width = efifb_console.cb_table_fb.x_resolution;
+               ri->ri_height = efifb_console.cb_table_fb.y_resolution;
+               ri->ri_depth = fb->depth;
+               ri->ri_stride = efifb_console.cb_table_fb.bytes_per_line;
+               ri->ri_rnum = efifb_console.cb_table_fb.red_mask_size;
+               ri->ri_rpos = efifb_console.cb_table_fb.red_mask_pos;
+               ri->ri_gnum = efifb_console.cb_table_fb.green_mask_size;
+               ri->ri_gpos = efifb_console.cb_table_fb.green_mask_pos;
+               ri->ri_bnum = efifb_console.cb_table_fb.blue_mask_size;
+               ri->ri_bpos = efifb_console.cb_table_fb.blue_mask_pos;
+       } else {
+               ri->ri_width = bios_efiinfo->fb_width;
+               ri->ri_height = bios_efiinfo->fb_height;
+               ri->ri_depth = fb->depth;
+               ri->ri_stride = bios_efiinfo->fb_pixpsl * (fb->depth / 8);
+               ri->ri_rnum = bmnum(bios_efiinfo->fb_red_mask);
+               ri->ri_rpos = bmpos(bios_efiinfo->fb_red_mask);
+               ri->ri_gnum = bmnum(bios_efiinfo->fb_green_mask);
+               ri->ri_gpos = bmpos(bios_efiinfo->fb_green_mask);
+               ri->ri_bnum = bmnum(bios_efiinfo->fb_blue_mask);
+               ri->ri_bpos = bmpos(bios_efiinfo->fb_blue_mask);
+       }
 }
 
 int
@@ -285,8 +348,6 @@ int
 efifb_cnattach(void)
 {
        struct efifb            *fb = &efifb_console;
-       struct rasops_info      *ri = &fb->rinfo;
-       long                     defattr = 0;
 
        if (bios_efiinfo == NULL || bios_efiinfo->fb_addr == 0)
                return (-1);
@@ -302,22 +363,7 @@ efifb_cnattach(void)
        fb->psize = bios_efiinfo->fb_height *
            bios_efiinfo->fb_pixpsl * (fb->depth / 8);
 
-       ri->ri_bits = (u_char *)PMAP_DIRECT_MAP(fb->paddr);
-
-       efifb_rasops_preinit(fb);
-
-       ri->ri_bs = efifb_bs;
-       ri->ri_flg = RI_CLEAR | RI_CENTER | RI_WRONLY;
-       rasops_init(ri, EFIFB_HEIGHT, EFIFB_WIDTH);
-       efifb_std_descr.ncols = ri->ri_cols;
-       efifb_std_descr.nrows = ri->ri_rows;
-       efifb_std_descr.textops = &ri->ri_ops;
-       efifb_std_descr.fontwidth = ri->ri_font->fontwidth;
-       efifb_std_descr.fontheight = ri->ri_font->fontheight;
-       efifb_std_descr.capabilities = ri->ri_caps;
-
-       ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
-       wsdisplay_cnattach(&efifb_std_descr, ri, 0, 0, defattr);
+       efifb_rasops_init();
 
        return (0);
 }
@@ -358,3 +404,110 @@ efifb_cndetach(void)
 {
        efifb_console.paddr = 0;
 }
+
+void
+efifb_rasops_init(void)
+{
+       struct efifb            *fb = &efifb_console;
+       struct rasops_info      *ri = &fb->rinfo;
+       long                     defattr = 0;
+
+       ri->ri_bits = (u_char *)PMAP_DIRECT_MAP(fb->paddr);
+
+       efifb_rasops_preinit(fb);
+
+       ri->ri_bs = efifb_bs;
+       ri->ri_flg = RI_CLEAR | RI_CENTER | RI_WRONLY;
+       rasops_init(ri, EFIFB_HEIGHT, EFIFB_WIDTH);
+       efifb_std_descr.ncols = ri->ri_cols;
+       efifb_std_descr.nrows = ri->ri_rows;
+       efifb_std_descr.textops = &ri->ri_ops;
+       efifb_std_descr.fontwidth = ri->ri_font->fontwidth;
+       efifb_std_descr.fontheight = ri->ri_font->fontheight;
+       efifb_std_descr.capabilities = ri->ri_caps;
+
+       ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
+       wsdisplay_cnattach(&efifb_std_descr, ri, 0, 0, defattr);
+}
+
+int
+efifb_cb_cnattach(void)
+{
+       struct cb_framebuffer *cb_fb = cb_find_fb((paddr_t)0x0);
+
+       if (cb_fb == NULL || !cb_fb->x_resolution)
+               return (-1);
+
+       memset(&efifb_console, 0, sizeof(efifb_console));
+       memcpy(&efifb_console.cb_table_fb, cb_fb,
+           sizeof(struct cb_framebuffer));
+
+       efifb_console.paddr = cb_fb->physical_address;
+       efifb_console.depth = cb_fb->bits_per_pixel;
+       efifb_console.psize = cb_fb->y_resolution * cb_fb->bytes_per_line;
+
+       efifb_rasops_init();
+
+       return (0);
+}
+
+int
+efifb_cb_found(void)
+{
+       return (efifb_console.paddr && efifb_console.cb_table_fb.x_resolution);
+}
+
+static uint16_t
+cb_checksum(const void *addr, unsigned size)
+{
+       const uint16_t *p = addr;
+       unsigned i, n = size / 2;
+       uint32_t sum = 0;
+
+       for (i = 0; i < n; i++)
+               sum += p[i];
+
+       sum = (sum >> 16) + (sum & 0xffff);
+       sum += (sum >> 16);
+       sum = ~sum & 0xffff;
+
+       return (uint16_t)sum;
+}
+
+struct cb_framebuffer *
+cb_find_fb(paddr_t addr)
+{
+       int i, j;
+
+       for (i = 0; i < (4 * 1024); i += 16) {
+               struct cb_header *cbh;
+               struct cb_entry *cbe;
+               paddr_t cbtable;
+
+               cbh = (struct cb_header *)(PMAP_DIRECT_MAP(addr + i));
+               if (memcmp(cbh->signature, "LBIO", 4) != 0)
+                       continue;
+
+               if (!cbh->header_bytes)
+                       continue;
+
+               if (cb_checksum(cbh, sizeof(*cbh)) != 0)
+                       return NULL;
+
+               cbtable = PMAP_DIRECT_MAP(addr + i + cbh->header_bytes);
+
+               for (j = 0; j < cbh->table_bytes; j += cbe->size) {
+                       cbe = (struct cb_entry *)((char *)cbtable + j);
+
+                       switch (cbe->tag) {
+                       case CB_TAG_FORWARD:
+                               return cb_find_fb(cbe->u.forward);
+
+                       case CB_TAG_FRAMEBUFFER:
+                               return &cbe->u.fb;
+                       }
+               }
+       }
+
+       return NULL;
+}
index 5018938..4afbae1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mainbus.c,v 1.36 2015/12/12 12:33:49 reyk Exp $       */
+/*     $OpenBSD: mainbus.c,v 1.37 2016/06/21 15:24:55 jcs Exp $        */
 /*     $NetBSD: mainbus.c,v 1.1 2003/04/26 18:39:29 fvdl Exp $ */
 
 /*
@@ -253,7 +253,7 @@ mainbus_attach(struct device *parent, struct device *self, void *aux)
 #endif /* NVMM > 0 */
 
 #if NEFIFB > 0
-       if (bios_efiinfo != NULL) {
+       if (bios_efiinfo != NULL || efifb_cb_found()) {
                mba.mba_eaa.eaa_name = "efifb";
                config_found(self, &mba, mainbus_print);
        }
index 9f1f8a6..461441c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: wscons_machdep.c,v 1.12 2016/03/06 22:41:24 naddy Exp $ */
+/*     $OpenBSD: wscons_machdep.c,v 1.13 2016/06/21 15:24:55 jcs Exp $ */
 
 /*
  * Copyright (c) 2001 Aaron Campbell
@@ -147,6 +147,10 @@ wscn_video_init(void)
        if (vga_cnattach(X86_BUS_SPACE_IO, X86_BUS_SPACE_MEM, -1, 1) == 0)
                return (0);
 #endif
+#if (NEFIFB > 0)
+       if (efifb_cb_cnattach() == 0)
+               return (0);
+#endif
 #if (NPCDISPLAY > 0)
        if (pcdisplay_cnattach(X86_BUS_SPACE_IO, X86_BUS_SPACE_MEM) == 0)
                return (0);
index 67bfdb3..f5e2bb2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: efifbvar.h,v 1.3 2015/10/30 11:21:01 kettenis Exp $   */
+/*     $OpenBSD: efifbvar.h,v 1.4 2016/06/21 15:24:55 jcs Exp $        */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -29,4 +29,7 @@ int efifb_cnattach(void);
 int efifb_is_console(struct pci_attach_args *);
 void efifb_cndetach(void);
 
+int efifb_cb_found(void);
+int efifb_cb_cnattach(void);
+
 #endif /* _MACHINE_EFIFB_H_ */