-/* $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 {
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 **,
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
};
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;
#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
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);
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);
}
{
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;
+}