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