This implements a new framebuffer console (based on efifb) for amd64 devices running Coreboot, which do not have otherwise working EFI, VGA text, inteldrm, or serial console. So mostly just Skylake Chromebooks.
This is required for my HP Chromebook 13 (and would have previously been required for the Broadwell Chromebook Pixel 2015) since SeaBIOS only handles printing text on Coreboot's framebuffer while at the bootloader. Once the kernel loads and tries to find a vga device, it would fail and print nothing on the screen. With this driver I get a fully working console and working X11 via the wsfb driver. I've tested this on a couple non-Chromebook machines, including my APU which is also using Coreboot, but since that has a serial console, this driver does not try to attach. Index: dev/wscons/wsconsio.h =================================================================== RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v retrieving revision 1.74 diff -u -p -u -p -r1.74 wsconsio.h --- dev/wscons/wsconsio.h 30 Mar 2016 23:34:12 -0000 1.74 +++ dev/wscons/wsconsio.h 10 Jun 2016 17:35:38 -0000 @@ -348,6 +348,7 @@ struct wsmouse_calibcoords { #define WSDISPLAY_TYPE_INTELDRM 69 /* Intel KMS framebuffer */ #define WSDISPLAY_TYPE_RADEONDRM 70 /* ATI Radeon KMS framebuffer */ #define WSDISPLAY_TYPE_EFIFB 71 /* EFI framebuffer */ +#define WSDISPLAY_TYPE_CBFB 72 /* Coreboot framebuffer */ /* Basic display information. Not applicable to all display types. */ struct wsdisplay_fbinfo { Index: arch/amd64/amd64/cbfb.c =================================================================== RCS file: arch/amd64/amd64/cbfb.c diff -N arch/amd64/amd64/cbfb.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/amd64/amd64/cbfb.c 10 Jun 2016 17:35:38 -0000 @@ -0,0 +1,447 @@ +/* $OpenBSD$ */ + +/* + * Coreboot framebuffer console driver + * + * Copyright (c) 2016 joshua stein <j...@openbsd.org> + * Copyright (c) 2015 YASUOKA Masahiko <yasu...@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <uvm/uvm_extern.h> +#include <machine/bus.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsdisplayvar.h> +#include <dev/rasops/rasops.h> +#include <dev/pci/pcivar.h> + +#include <machine/cbfbvar.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 cbfb { + struct rasops_info rinfo; + int depth; + paddr_t paddr; + psize_t psize; + + struct cb_framebuffer table_cbfb; +}; + +struct cbfb_softc { + struct device sc_dev; + struct cbfb *sc_fb; +}; + +int cbfb_match(struct device *, void *, void *); +void cbfb_attach(struct device *, struct device *, void *); +void cbfb_rasops_preinit(struct cbfb *); +int cbfb_ioctl(void *, u_long, caddr_t, int, struct proc *); +paddr_t cbfb_mmap(void *, off_t, int); +int cbfb_alloc_screen(void *, const struct wsscreen_descr *, void **, + int *, int *, long *); +void cbfb_free_screen(void *, void *); +int cbfb_show_screen(void *, void *, int, void (*cb) (void *, int, int), + void *); +int cbfb_list_font(void *, struct wsdisplay_font *); +int cbfb_load_font(void *, void *, struct wsdisplay_font *); + +int cb_parse_table(paddr_t); + +struct cfattach cbfb_ca = { + sizeof(struct cbfb_softc), cbfb_match, cbfb_attach, NULL +}; + +#define CBFB_WIDTH 100 +#define CBFB_HEIGHT 31 + +struct wsdisplay_charcell cbfb_bs[CBFB_HEIGHT * CBFB_WIDTH]; + +struct wsscreen_descr cbfb_std_descr = { "std" }; + +const struct wsscreen_descr *cbfb_descrs[] = { + &cbfb_std_descr +}; + +const struct wsscreen_list cbfb_screen_list = { + nitems(cbfb_descrs), cbfb_descrs +}; + +struct wsdisplay_accessops cbfb_accessops = { + .ioctl = cbfb_ioctl, + .mmap = cbfb_mmap, + .alloc_screen = cbfb_alloc_screen, + .free_screen = cbfb_free_screen, + .show_screen = cbfb_show_screen, + .load_font = cbfb_load_font, + .list_font = cbfb_list_font +}; + +struct cfdriver cbfb_cd = { + NULL, "cbfb", DV_DULL +}; + +struct cbfb cbfb_console; + +int +cbfb_match(struct device *parent, void *cf, void *aux) +{ + struct cbfb_attach_args *cbaa = aux; + + if (strcmp(cbaa->cbfbaa_name, cbfb_cd.cd_name) == 0 && cbfb_found()) + return (1); + + return (0); +} + +int +cbfb_found(void) +{ + return !!(cbfb_console.paddr); +} + +void +cbfb_attach(struct device *parent, struct device *self, void *aux) +{ + struct cbfb *fb; + struct cbfb_softc *sc = (struct cbfb_softc *)self; + struct wsemuldisplaydev_attach_args aa; + struct rasops_info *ri; + int ccol = 0, crow = 0; + bus_space_tag_t iot = X86_BUS_SPACE_MEM; + bus_space_handle_t ioh; + long defattr; + + printf(": %dx%d, %d bpp\n", + cbfb_console.table_cbfb.x_resolution, + cbfb_console.table_cbfb.y_resolution, + cbfb_console.table_cbfb.bits_per_pixel); + + aa.console = 1; + fb = sc->sc_fb = &cbfb_console; + ri = &fb->rinfo; + ccol = ri->ri_ccol; + crow = ri->ri_crow; + + if (bus_space_map(iot, fb->paddr, fb->psize, + BUS_SPACE_MAP_PREFETCHABLE | BUS_SPACE_MAP_LINEAR, &ioh) == 0) + ri->ri_origbits = bus_space_vaddr(iot, ioh); + + cbfb_rasops_preinit(fb); + ri->ri_flg &= ~RI_CLEAR; + ri->ri_flg |= RI_VCONS | RI_WRONLY; + + rasops_init(ri, cbfb_std_descr.nrows, cbfb_std_descr.ncols); + + aa.scrdata = &cbfb_screen_list; + aa.accessops = &cbfb_accessops; + aa.accesscookie = sc; + aa.defaultscreens = 0; + + ri->ri_ops.alloc_attr(ri->ri_active, 0, 0, 0, &defattr); + wsdisplay_cnattach(&cbfb_std_descr, ri->ri_active, ccol, crow, defattr); + + config_found(self, &aa, wsemuldisplaydevprint); +} + +void +cbfb_rasops_preinit(struct cbfb *cbfb) +{ +#define bmnum(_x) (fls(_x) - ffs(_x) + 1) +#define bmpos(_x) (ffs(_x) - 1) + struct rasops_info *ri = &cbfb->rinfo; + + ri->ri_width = cbfb->table_cbfb.x_resolution; + ri->ri_height = cbfb->table_cbfb.y_resolution; + ri->ri_depth = cbfb->depth; + ri->ri_stride = cbfb->table_cbfb.bytes_per_line; + ri->ri_rnum = cbfb->table_cbfb.red_mask_size; + ri->ri_rpos = cbfb->table_cbfb.red_mask_pos; + ri->ri_gnum = cbfb->table_cbfb.green_mask_size; + ri->ri_gpos = cbfb->table_cbfb.green_mask_pos; + ri->ri_bnum = cbfb->table_cbfb.blue_mask_size; + ri->ri_bpos = cbfb->table_cbfb.blue_mask_pos; +} + +int +cbfb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct cbfb_softc *sc = v; + struct cbfb *fb = sc->sc_fb; + struct rasops_info *ri = &fb->rinfo; + struct wsdisplay_fbinfo *wdf; + + switch (cmd) { + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_CBFB; + break; + case WSDISPLAYIO_GINFO: + wdf = (struct wsdisplay_fbinfo *)data; + wdf->width = ri->ri_width; + wdf->height = ri->ri_height; + wdf->depth = ri->ri_depth; + wdf->cmsize = 0; /* color map is unavailable */ + break; + case WSDISPLAYIO_LINEBYTES: + *(u_int *)data = ri->ri_stride; + break; + case WSDISPLAYIO_SMODE: + break; + case WSDISPLAYIO_GETSUPPORTEDDEPTH: + switch (ri->ri_depth) { + case 32: + *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; + break; + case 24: + *(u_int *)data = WSDISPLAYIO_DEPTH_24_24; + break; + case 16: + *(u_int *)data = WSDISPLAYIO_DEPTH_16; + break; + case 15: + *(u_int *)data = WSDISPLAYIO_DEPTH_15; + break; + case 8: + *(u_int *)data = WSDISPLAYIO_DEPTH_8; + break; + case 4: + *(u_int *)data = WSDISPLAYIO_DEPTH_4; + break; + case 1: + *(u_int *)data = WSDISPLAYIO_DEPTH_1; + break; + default: + return (-1); + } + break; + default: + return (-1); + } + + return (0); +} + +paddr_t +cbfb_mmap(void *v, off_t off, int prot) +{ + struct cbfb_softc *sc = v; + + if (off < 0 || off >= sc->sc_fb->psize) + return (-1); + + return ((sc->sc_fb->paddr + off) | PMAP_WC); +} + +int +cbfb_alloc_screen(void *v, const struct wsscreen_descr *descr, + void **cookiep, int *curxp, int *curyp, long *attrp) +{ + struct cbfb_softc *sc = v; + struct rasops_info *ri = &sc->sc_fb->rinfo; + + return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp); +} + +void +cbfb_free_screen(void *v, void *cookie) +{ + struct cbfb_softc *sc = v; + struct rasops_info *ri = &sc->sc_fb->rinfo; + + rasops_free_screen(ri, cookie); +} + +int +cbfb_show_screen(void *v, void *cookie, int waitok, + void (*cb) (void *, int, int), void *cb_arg) +{ + struct cbfb_softc *sc = v; + struct rasops_info *ri = &sc->sc_fb->rinfo; + + return rasops_show_screen(ri, cookie, waitok, cb, cb_arg); +} + +int +cbfb_load_font(void *v, void *cookie, struct wsdisplay_font *font) +{ + struct cbfb_softc *sc = v; + struct rasops_info *ri = &sc->sc_fb->rinfo; + + return (rasops_load_font(ri, cookie, font)); +} + +int +cbfb_list_font(void *v, struct wsdisplay_font *font) +{ + struct cbfb_softc *sc = v; + struct rasops_info *ri = &sc->sc_fb->rinfo; + + return (rasops_list_font(ri, font)); +} + +int +cbfb_cnattach(void) +{ + if (cb_parse_table((paddr_t)0x0) || !cbfb_console.paddr) + return (-1); + + return (0); +} + +int +cbfb_is_console(struct pci_attach_args *pa) +{ + return !!(cbfb_console.paddr); +} + +void +cbfb_cndetach(void) +{ + cbfb_console.paddr = 0; +} + +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; +} + +static void +cb_fb_init(struct cb_framebuffer *table_cbfb) +{ + struct cbfb *cbfb; + struct rasops_info *ri = &cbfb->rinfo; + long defattr = 0; + + memset(&cbfb_console, 0, sizeof(cbfb_console)); + memcpy(&cbfb_console.table_cbfb, table_cbfb, + sizeof(struct cb_framebuffer)); + + cbfb = &cbfb_console; + cbfb->paddr = table_cbfb->physical_address; + cbfb->depth = table_cbfb->bits_per_pixel; + cbfb->psize = table_cbfb->y_resolution * table_cbfb->bytes_per_line; + + ri->ri_bits = (u_char *)PMAP_DIRECT_MAP(cbfb->paddr); + + cbfb_rasops_preinit(cbfb); + + ri->ri_bs = cbfb_bs; + ri->ri_flg = RI_CLEAR | RI_CENTER | RI_WRONLY; + rasops_init(ri, CBFB_HEIGHT, CBFB_WIDTH); + cbfb_std_descr.ncols = ri->ri_cols; + cbfb_std_descr.nrows = ri->ri_rows; + cbfb_std_descr.textops = &ri->ri_ops; + cbfb_std_descr.fontwidth = ri->ri_font->fontwidth; + cbfb_std_descr.fontheight = ri->ri_font->fontheight; + cbfb_std_descr.capabilities = ri->ri_caps; + + ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr); + wsdisplay_cnattach(&cbfb_std_descr, ri, 0, 0, defattr); +} + +int +cb_parse_table(paddr_t addr) +{ + int i, j; + + for (i = 0; i < (4 * 1024); i += 16) { + struct cb_header *cbh; + struct cb_entry *cbe; + void *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 (-1); + + cbtable = (void *)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_parse_table(cbe->u.forward); + + case CB_TAG_FRAMEBUFFER: + cb_fb_init(&cbe->u.fb); + break; + } + } + } + + return (0); +} Index: arch/amd64/amd64/mainbus.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/mainbus.c,v retrieving revision 1.36 diff -u -p -u -p -r1.36 mainbus.c --- arch/amd64/amd64/mainbus.c 12 Dec 2015 12:33:49 -0000 1.36 +++ arch/amd64/amd64/mainbus.c 10 Jun 2016 17:35:38 -0000 @@ -52,6 +52,7 @@ #include "vmm.h" #include "pvbus.h" #include "efifb.h" +#include "cbfb.h" #include <machine/cpuvar.h> #include <machine/i82093var.h> @@ -74,6 +75,10 @@ #include <machine/efifbvar.h> #endif +#if NCBFB > 0 +#include <machine/cbfbvar.h> +#endif + int mainbus_match(struct device *, void *, void *); void mainbus_attach(struct device *, struct device *, void *); @@ -105,6 +110,9 @@ union mainbus_attach_args { #if NEFIFB > 0 struct efifb_attach_args mba_eaa; #endif +#if NCBFB > 0 + struct cbfb_attach_args mba_cbfbaa; +#endif }; /* @@ -255,6 +263,13 @@ mainbus_attach(struct device *parent, st #if NEFIFB > 0 if (bios_efiinfo != NULL) { mba.mba_eaa.eaa_name = "efifb"; + config_found(self, &mba, mainbus_print); + } +#endif + +#if NCBFB > 0 + if (cbfb_found()) { + mba.mba_cbfbaa.cbfbaa_name = "cbfb"; config_found(self, &mba, mainbus_print); } #endif Index: arch/amd64/amd64/wscons_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/wscons_machdep.c,v retrieving revision 1.12 diff -u -p -u -p -r1.12 wscons_machdep.c --- arch/amd64/amd64/wscons_machdep.c 6 Mar 2016 22:41:24 -0000 1.12 +++ arch/amd64/amd64/wscons_machdep.c 10 Jun 2016 17:35:38 -0000 @@ -75,6 +75,10 @@ #if NEFIFB > 0 #include <machine/efifbvar.h> #endif +#include "cbfb.h" +#if NCBFB > 0 +#include <machine/cbfbvar.h> +#endif int wscn_video_init(void); void wscn_input_init(int); @@ -145,6 +149,10 @@ wscn_video_init(void) #endif #if (NVGA > 0) if (vga_cnattach(X86_BUS_SPACE_IO, X86_BUS_SPACE_MEM, -1, 1) == 0) + return (0); +#endif +#if (NCBFB > 0) + if (cbfb_cnattach() == 0) return (0); #endif #if (NPCDISPLAY > 0) Index: arch/amd64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.419 diff -u -p -u -p -r1.419 GENERIC --- arch/amd64/conf/GENERIC 1 Jun 2016 09:48:20 -0000 1.419 +++ arch/amd64/conf/GENERIC 10 Jun 2016 17:35:38 -0000 @@ -358,6 +358,9 @@ lpt* at puc? efifb0 at mainbus? # EFI Framebuffer wsdisplay0 at efifb? console 1 +cbfb0 at mainbus? # Coreboot Framebuffer +wsdisplay0 at cbfb? console 1 + #bha* at pci? # BusLogic [57]4X SCSI controllers ahc* at pci? # Adaptec 2940 SCSI controllers jmb* at pci? # JMicron JMB36x controllers Index: arch/amd64/conf/files.amd64 =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/files.amd64,v retrieving revision 1.85 diff -u -p -u -p -r1.85 files.amd64 --- arch/amd64/conf/files.amd64 8 Jan 2016 15:54:13 -0000 1.85 +++ arch/amd64/conf/files.amd64 10 Jun 2016 17:35:38 -0000 @@ -114,6 +114,13 @@ attach efifb at mainbus file arch/amd64/amd64/efifb.c efifb needs-flag # +# Coreboot Framebuffer +# +device cbfb: wsemuldisplaydev, rasops32, rasops16, rasops8, rasops4 +attach cbfb at mainbus +file arch/amd64/amd64/cbfb.c cbfb needs-flag + +# # PCI drivers # Index: arch/amd64/include/cbfbvar.h =================================================================== RCS file: arch/amd64/include/cbfbvar.h diff -N arch/amd64/include/cbfbvar.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/amd64/include/cbfbvar.h 10 Jun 2016 17:35:38 -0000 @@ -0,0 +1,34 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2016 joshua stein <j...@openbsd.org> + * Copyright (c) 2015 YASUOKA Masahiko <yasu...@yasuoka.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_CBFB_H_ +#define _MACHINE_CBFB_H_ + +struct cbfb_attach_args { + const char *cbfbaa_name; +}; + +struct pci_attach_args; + +int cbfb_found(void); +int cbfb_cnattach(void); +int cbfb_is_console(struct pci_attach_args *); +void cbfb_cndetach(void); + +#endif /* _MACHINE_CBFB_H_ */