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 <kra...@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);
> 


Attachment: signature.asc
Description: OpenPGP digital signature

-- 
coreboot mailing list: coreboot@coreboot.org
http://www.coreboot.org/mailman/listinfo/coreboot

Reply via email to