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);
Am 04.09.2014 um 12:25 schrieb Gerd Hoffmann:
Add coreboot framebuffer support to the linux kernel. Say Y here
Alternatively, we teach the linux payload trampoline to add those to the setup block (it's a TODO in the trampoline code)
Patrick
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);
On Do, 2014-09-04 at 22:09 +0200, Vladimir 'φ-coder/phcoder' Serbinenko wrote:
This code assumes that payload didn't change video mode which is improper assumption if you're not a payload.
The code tries to detect that case. If either the vga is in text mode or it finds vesafb / efifb informations it assumes being booted indirectly (via seabios for example), with possibly changed video mode, and doesn't register the framebuffer.
Suggestions how to improve that?
cheers, Gerd
On 09/04/2014 03:25 AM, 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
This should NOT be named CONFIG_COREBOOT. CONFIG_FB_COREBOOT perhaps.
+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" */
This stuff belongs in a header file.
I'm not a fan of Coreboot having invented its own nonstandard hacks, but I guess it is pretty much unavoidable.
-hpa
On Fri, Sep 5, 2014 at 1:05 PM, H. Peter Anvin hpa@zytor.com wrote:
I'm not a fan of Coreboot having invented its own nonstandard hacks, but I guess it is pretty much unavoidable.
I suspect this should not be architecture-dependent, since coreboot tables work across 3 coreboot architectures at present with more on the way.
Sometimes, something that spans more than an x86 can look like a non-standard hack. At other times, it might look portable. Depends on ones perspective I guess.
Gerd, I'll get back to you offline to show you what we did in Akaros.
ron
I'm not a fan of Coreboot having invented its own nonstandard hacks, but I guess it is pretty much unavoidable.
It's completely avoidable. The stub can copy this information to standard framebuffer info structure. The only missing thing is to apply patch by cjwatson or mjg59 (I'm not sure now who wrote it) for having an ID for linear framebuffer which implies no specific hardware (it can be any) or firmware (coreboot doesn't provide nay additional info or callback).
Please don't apply this patch it will break SeaBIOS booting with VGABIOS.
Vladimir can you point me to that patch? This sounds interesting.
ron
On 09/05/2014 02:09 PM, Vladimir 'φ-coder/phcoder' Serbinenko wrote:
I'm not a fan of Coreboot having invented its own nonstandard hacks, but I guess it is pretty much unavoidable.
It's completely avoidable. The stub can copy this information to standard framebuffer info structure. The only missing thing is to apply patch by cjwatson or mjg59 (I'm not sure now who wrote it) for having an ID for linear framebuffer which implies no specific hardware (it can be any) or firmware (coreboot doesn't provide nay additional info or callback).
Please don't apply this patch it will break SeaBIOS booting with VGABIOS.
Thanks for the warning. Yes, this is the Right Thing.
-hpa