This code assumes that payload didn't change video mode which is improper assumption if you're not a payload. On 04.09.2014 12:25, Gerd Hoffmann wrote:
Signed-off-by: Gerd Hoffmann kraxel@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);