[coreboot] [PATCH] x86: add coreboot framebuffer support

Gerd Hoffmann kraxel at redhat.com
Thu Sep 4 12:25:07 CEST 2014


Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
 arch/x86/Kconfig           |  12 +++
 arch/x86/kernel/Makefile   |   1 +
 arch/x86/kernel/coreboot.c | 232 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+)
 create mode 100644 arch/x86/kernel/coreboot.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 778178f..3aeb038 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2372,6 +2372,18 @@ config X86_SYSFB
 
 	  If unsure, say Y.
 
+config COREBOOT
+	bool "coreboot"
+	depends on X86_SYSFB
+	depends on FB_SIMPLE
+	help
+	  Add coreboot framebuffer support to the linux kernel.	 Say Y here
+	  if you want the linux kernel find and use the firmware framebuffer
+	  initialized by coreboot.  Useful when using the linux kernel as
+	  coreboot payload.
+
+	  If unsure, or if you don't know what coreboot is, say N.
+
 endmenu
 
 
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index ada2e2d..4b30b0e 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -103,6 +103,7 @@ obj-$(CONFIG_UPROBES)			+= uprobes.o
 obj-y					+= sysfb.o
 obj-$(CONFIG_X86_SYSFB)			+= sysfb_simplefb.o
 obj-$(CONFIG_EFI)			+= sysfb_efi.o
+obj-$(CONFIG_COREBOOT)			+= coreboot.o
 
 obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
diff --git a/arch/x86/kernel/coreboot.c b/arch/x86/kernel/coreboot.c
new file mode 100644
index 0000000..44ed3fa
--- /dev/null
+++ b/arch/x86/kernel/coreboot.c
@@ -0,0 +1,232 @@
+/*
+ * Find and parse coreboot tables.  Register framebuffer if present.
+ * See src/include/boot/coreboot_tables.h in coreboot source tree.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/screen_info.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/simplefb.h>
+#include <video/vga.h>
+
+#include <asm/checksum.h>
+#include <asm/io.h>
+#include <asm/sysfb.h>
+
+struct cb_header {
+	u32     signature;
+	u32     header_bytes;
+	u32     header_checksum;
+	u32     table_bytes;
+	u32     table_checksum;
+	u32     table_entries;
+};
+
+#define CB_TAG_MAINBOARD        0x0003
+#define CB_TAG_VERSION          0x0004
+#define CB_TAG_FORWARD          0x0011
+#define CB_TAG_FRAMEBUFFER      0x0012
+
+struct cb_mainboard {
+	u8      vendor_idx;
+	u8      part_idx;
+	char    strings[0];
+};
+
+struct cb_framebuffer {
+	u64     physical_address;
+	u32     x_resolution;
+	u32     y_resolution;
+	u32     bytes_per_line;
+	u8      bits_per_pixel;
+	u8      red_mask_pos;
+	u8      red_mask_size;
+	u8      green_mask_pos;
+	u8      green_mask_size;
+	u8      blue_mask_pos;
+	u8      blue_mask_size;
+	u8      reserved_mask_pos;
+	u8      reserved_mask_size;
+};
+
+struct cb_entry {
+	u32     tag;
+	u32     size;
+	union {
+		char    string[0];
+		u64     forward;
+		struct cb_mainboard   mb;
+		struct cb_framebuffer fb;
+	} u;
+};
+
+#define CB_SIGNATURE 0x4f49424C  /* "LBIO" */
+
+/* Try to locate the coreboot header in a given address range. */
+static __init struct cb_header *coreboot_find_header(u32 addr, int len)
+{
+	struct cb_header *cbh, *copy;
+	int offset;
+	void *map;
+
+	map = ioremap(addr, len);
+	for (offset = 0; offset < len; offset += 16) {
+		cbh = map + offset;
+		if (!cbh)
+			continue;
+		if (cbh->signature != CB_SIGNATURE)
+			continue;
+		if (!cbh->table_bytes)
+			continue;
+		if (ip_compute_csum(cbh, sizeof(*cbh)) != 0)
+			continue;
+		if (ip_compute_csum(cbh + 1, cbh->table_bytes)
+		    != cbh->table_checksum)
+			continue;
+		pr_debug("coreboot: header found at 0x%x\n",
+			 addr + offset);
+		copy = kzalloc(sizeof(*cbh) + cbh->table_bytes, GFP_KERNEL);
+		memcpy(copy, cbh, sizeof(*cbh) + cbh->table_bytes);
+		iounmap(map);
+		return copy;
+	}
+	iounmap(map);
+	return NULL;
+}
+
+static __init bool check_vga_text_mode(void)
+{
+	uint8_t reg;
+
+	if (screen_info.orig_video_isVGA != 1)
+		return false;
+
+	reg = inb(VGA_GFX_I);
+	if (reg == 0xff)
+		/* no vga present */
+		return false;
+
+	outb(VGA_GFX_MISC, VGA_GFX_I);
+	reg = inb(VGA_GFX_D);
+	if (reg & 0x01)
+		/* vga is in gfx mode */
+		return false;
+
+	return true;
+}
+
+static __init void coreboot_fb_init(struct cb_framebuffer *fb)
+{
+	const char *reason = "";
+	struct simplefb_platform_data mode;
+	struct screen_info si;
+	struct resource res;
+
+	pr_info("coreboot: framebuffer: %dx%d, %d bpp, at %llx\n",
+		fb->x_resolution,
+		fb->y_resolution,
+		fb->bits_per_pixel,
+		fb->physical_address);
+
+	if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB) {
+		reason = "have vesa lfb";
+		goto skip;
+	}
+	if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
+		reason = "have efi gop";
+		goto skip;
+	}
+	if (check_vga_text_mode()) {
+		reason = "vga is in text mode";
+		goto skip;
+	}
+
+	memset(&si, 0, sizeof(si));
+	si.orig_video_isVGA = VIDEO_TYPE_VLFB;
+	si.lfb_width        = fb->x_resolution;
+	si.lfb_height       = fb->y_resolution;
+	si.lfb_depth        = fb->bits_per_pixel;
+	si.lfb_base         = fb->physical_address;
+	si.lfb_linelength   = fb->bytes_per_line;
+	si.lfb_size         =
+		(fb->y_resolution * fb->bytes_per_line + 65535) / 65536;
+	si.pages            = 1;
+	si.red_size         = fb->red_mask_size;
+	si.red_pos          = fb->red_mask_pos;
+	si.green_size       = fb->green_mask_size;
+	si.green_pos        = fb->green_mask_pos;
+	si.blue_size        = fb->blue_mask_size;
+	si.blue_pos         = fb->blue_mask_pos;
+	si.rsvd_size        = fb->reserved_mask_size;
+	si.rsvd_pos         = fb->reserved_mask_pos;
+	if (!parse_mode(&si, &mode)) {
+		reason = "mode not compatible";
+		goto skip;
+	}
+
+	memset(&res, 0, sizeof(res));
+	res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	res.name = "coreboot-fb";
+	res.start = si.lfb_base;
+	res.end = si.lfb_base + si.lfb_size * 65536 - 1;
+
+	pr_info("coreboot: setting up simplefb\n");
+	platform_device_register_resndata(NULL, "simple-framebuffer", 0,
+					  &res, 1, &mode, sizeof(mode));
+	return;
+
+skip:
+	pr_info("coreboot: skipping framebuffer setup (%s)\n", reason);
+	return;
+}
+
+static __init int coreboot_detect(void)
+{
+	struct cb_header *cbh;
+	struct cb_entry *cbe;
+	u64 addr = 0;
+	void *next;
+	int i;
+
+newheader:
+	cbh = coreboot_find_header(addr, 0x1000);
+	if (!cbh)
+		return 0;
+
+	next = cbh + 1;
+	for (i = 0; i < cbh->table_entries; i++) {
+		cbe = next;
+		next += cbe->size;
+		switch (cbe->tag) {
+		case CB_TAG_MAINBOARD:
+			pr_info("coreboot: mainboard: %s / %s\n",
+				cbe->u.mb.strings + cbe->u.mb.vendor_idx,
+				cbe->u.mb.strings + cbe->u.mb.part_idx);
+			break;
+		case CB_TAG_VERSION:
+			pr_info("coreboot: version: %s\n",
+				cbe->u.string);
+			break;
+		case CB_TAG_FORWARD:
+			pr_debug("coreboot: forward to 0x%llx\n",
+				 cbe->u.forward);
+			addr = cbe->u.forward;
+			kfree(cbh);
+			goto newheader;
+		case CB_TAG_FRAMEBUFFER:
+			coreboot_fb_init(&cbe->u.fb);
+			break;
+		default:
+			pr_debug("%s: unhandled tag 0x%x (size %d)\n",
+				 __func__, cbe->tag, cbe->size);
+			break;
+		}
+	}
+
+	kfree(cbh);
+	return 0;
+}
+
+device_initcall(coreboot_detect);
-- 
1.8.3.1




More information about the coreboot mailing list