linux/drivers/firmware/google/memconsole.c
<<
>>
Prefs
   1/*
   2 * memconsole.c
   3 *
   4 * Infrastructure for importing the BIOS memory based console
   5 * into the kernel log ringbuffer.
   6 *
   7 * Copyright 2010 Google Inc. All rights reserved.
   8 */
   9
  10#include <linux/ctype.h>
  11#include <linux/init.h>
  12#include <linux/kernel.h>
  13#include <linux/string.h>
  14#include <linux/sysfs.h>
  15#include <linux/kobject.h>
  16#include <linux/module.h>
  17#include <linux/dmi.h>
  18#include <linux/io.h>
  19#include <asm/bios_ebda.h>
  20
  21#define BIOS_MEMCONSOLE_V1_MAGIC        0xDEADBABE
  22#define BIOS_MEMCONSOLE_V2_MAGIC        (('M')|('C'<<8)|('O'<<16)|('N'<<24))
  23
  24struct biosmemcon_ebda {
  25        u32 signature;
  26        union {
  27                struct {
  28                        u8  enabled;
  29                        u32 buffer_addr;
  30                        u16 start;
  31                        u16 end;
  32                        u16 num_chars;
  33                        u8  wrapped;
  34                } __packed v1;
  35                struct {
  36                        u32 buffer_addr;
  37                        /* Misdocumented as number of pages! */
  38                        u16 num_bytes;
  39                        u16 start;
  40                        u16 end;
  41                } __packed v2;
  42        };
  43} __packed;
  44
  45static u32 memconsole_baseaddr;
  46static size_t memconsole_length;
  47
  48static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
  49                               struct bin_attribute *bin_attr, char *buf,
  50                               loff_t pos, size_t count)
  51{
  52        char *memconsole;
  53        ssize_t ret;
  54
  55        memconsole = ioremap_cache(memconsole_baseaddr, memconsole_length);
  56        if (!memconsole) {
  57                pr_err("memconsole: ioremap_cache failed\n");
  58                return -ENOMEM;
  59        }
  60        ret = memory_read_from_buffer(buf, count, &pos, memconsole,
  61                                      memconsole_length);
  62        iounmap(memconsole);
  63        return ret;
  64}
  65
  66static struct bin_attribute memconsole_bin_attr = {
  67        .attr = {.name = "log", .mode = 0444},
  68        .read = memconsole_read,
  69};
  70
  71
  72static void __init found_v1_header(struct biosmemcon_ebda *hdr)
  73{
  74        pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
  75        pr_info("BIOS console buffer at 0x%.8x, "
  76               "start = %d, end = %d, num = %d\n",
  77               hdr->v1.buffer_addr, hdr->v1.start,
  78               hdr->v1.end, hdr->v1.num_chars);
  79
  80        memconsole_length = hdr->v1.num_chars;
  81        memconsole_baseaddr = hdr->v1.buffer_addr;
  82}
  83
  84static void __init found_v2_header(struct biosmemcon_ebda *hdr)
  85{
  86        pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
  87        pr_info("BIOS console buffer at 0x%.8x, "
  88               "start = %d, end = %d, num_bytes = %d\n",
  89               hdr->v2.buffer_addr, hdr->v2.start,
  90               hdr->v2.end, hdr->v2.num_bytes);
  91
  92        memconsole_length = hdr->v2.end - hdr->v2.start;
  93        memconsole_baseaddr = hdr->v2.buffer_addr + hdr->v2.start;
  94}
  95
  96/*
  97 * Search through the EBDA for the BIOS Memory Console, and
  98 * set the global variables to point to it.  Return true if found.
  99 */
 100static bool __init found_memconsole(void)
 101{
 102        unsigned int address;
 103        size_t length, cur;
 104
 105        address = get_bios_ebda();
 106        if (!address) {
 107                pr_info("BIOS EBDA non-existent.\n");
 108                return false;
 109        }
 110
 111        /* EBDA length is byte 0 of EBDA (in KB) */
 112        length = *(u8 *)phys_to_virt(address);
 113        length <<= 10; /* convert to bytes */
 114
 115        /*
 116         * Search through EBDA for BIOS memory console structure
 117         * note: signature is not necessarily dword-aligned
 118         */
 119        for (cur = 0; cur < length; cur++) {
 120                struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
 121
 122                /* memconsole v1 */
 123                if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
 124                        found_v1_header(hdr);
 125                        return true;
 126                }
 127
 128                /* memconsole v2 */
 129                if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
 130                        found_v2_header(hdr);
 131                        return true;
 132                }
 133        }
 134
 135        pr_info("BIOS console EBDA structure not found!\n");
 136        return false;
 137}
 138
 139static struct dmi_system_id memconsole_dmi_table[] __initdata = {
 140        {
 141                .ident = "Google Board",
 142                .matches = {
 143                        DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
 144                },
 145        },
 146        {}
 147};
 148MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
 149
 150static int __init memconsole_init(void)
 151{
 152        if (!dmi_check_system(memconsole_dmi_table))
 153                return -ENODEV;
 154
 155        if (!found_memconsole())
 156                return -ENODEV;
 157
 158        memconsole_bin_attr.size = memconsole_length;
 159        return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
 160}
 161
 162static void __exit memconsole_exit(void)
 163{
 164        sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
 165}
 166
 167module_init(memconsole_init);
 168module_exit(memconsole_exit);
 169
 170MODULE_AUTHOR("Google, Inc.");
 171MODULE_LICENSE("GPL");
 172