diff options
author | Joshua Stein <jcs@cvs.openbsd.org> | 2016-06-21 15:24:56 +0000 |
---|---|---|
committer | Joshua Stein <jcs@cvs.openbsd.org> | 2016-06-21 15:24:56 +0000 |
commit | 7e69dbfca45ff61a2865b4132a3ba615e1291693 (patch) | |
tree | 10294d88c5d035c46acffffa0117f63115f4089d /sys/arch/amd64 | |
parent | f289116dee91e153bf3291145614fa0bda8ee9b1 (diff) |
add support to efifb for drawing a console on a coreboot framebuffer
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
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r-- | sys/arch/amd64/amd64/efifb.c | 215 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/mainbus.c | 4 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/wscons_machdep.c | 6 | ||||
-rw-r--r-- | sys/arch/amd64/include/efifbvar.h | 5 |
4 files changed, 195 insertions, 35 deletions
diff --git a/sys/arch/amd64/amd64/efifb.c b/sys/arch/amd64/amd64/efifb.c index efc7334936a..2cd59e122f2 100644 --- a/sys/arch/amd64/amd64/efifb.c +++ b/sys/arch/amd64/amd64/efifb.c @@ -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 @@ -30,11 +31,56 @@ #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; +} diff --git a/sys/arch/amd64/amd64/mainbus.c b/sys/arch/amd64/amd64/mainbus.c index 5018938ff8c..4afbae1b39f 100644 --- a/sys/arch/amd64/amd64/mainbus.c +++ b/sys/arch/amd64/amd64/mainbus.c @@ -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); } diff --git a/sys/arch/amd64/amd64/wscons_machdep.c b/sys/arch/amd64/amd64/wscons_machdep.c index 9f1f8a6a041..461441c4d43 100644 --- a/sys/arch/amd64/amd64/wscons_machdep.c +++ b/sys/arch/amd64/amd64/wscons_machdep.c @@ -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); diff --git a/sys/arch/amd64/include/efifbvar.h b/sys/arch/amd64/include/efifbvar.h index 67bfdb33ecd..f5e2bb26cae 100644 --- a/sys/arch/amd64/include/efifbvar.h +++ b/sys/arch/amd64/include/efifbvar.h @@ -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_ */ |