Skip to content

Commit

Permalink
driver: Google Memory Console
Browse files Browse the repository at this point in the history
This patch introduces the 'memconsole' driver.

Our firmware gives us access to an in-memory log of the firmware's
output.   This gives us visibility in a data-center of headless machines
as to what the firmware is doing.

The memory console is found by the driver by finding a header block in
the EBDA.  The buffer is then copied out, and is exported to userland in
the file /sys/firmware/log.

Signed-off-by: San Mehat <[email protected]>
Signed-off-by: Mike Waychison <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
waych authored and gregkh committed Apr 30, 2011
1 parent 74c5b31 commit e561bc4
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Documentation/ABI/testing/sysfs-firmware-log
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
What: /sys/firmware/log
Date: February 2011
Contact: Mike Waychison <[email protected]>
Description:
The /sys/firmware/log is a binary file that represents a
read-only copy of the firmware's log if one is
available.
8 changes: 8 additions & 0 deletions drivers/firmware/google/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ config GOOGLE_SMI
platforms. This provides an interface for writing to and
clearing the EFI event log and reading and writing NVRAM
variables.

config GOOGLE_MEMCONSOLE
tristate "Firmware Memory Console"
depends on DMI
help
This option enables the kernel to search for a firmware log in
the EBDA on Google servers. If found, this log is exported to
userland in the file /sys/firmware/log.
1 change: 1 addition & 0 deletions drivers/firmware/google/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
166 changes: 166 additions & 0 deletions drivers/firmware/google/memconsole.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* memconsole.c
*
* Infrastructure for importing the BIOS memory based console
* into the kernel log ringbuffer.
*
* Copyright 2010 Google Inc. All rights reserved.
*/

#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/dmi.h>
#include <asm/bios_ebda.h>

#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))

struct biosmemcon_ebda {
u32 signature;
union {
struct {
u8 enabled;
u32 buffer_addr;
u16 start;
u16 end;
u16 num_chars;
u8 wrapped;
} __packed v1;
struct {
u32 buffer_addr;
/* Misdocumented as number of pages! */
u16 num_bytes;
u16 start;
u16 end;
} __packed v2;
};
} __packed;

static char *memconsole_baseaddr;
static size_t memconsole_length;

static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count)
{
return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
memconsole_length);
}

static struct bin_attribute memconsole_bin_attr = {
.attr = {.name = "log", .mode = 0444},
.read = memconsole_read,
};


static void found_v1_header(struct biosmemcon_ebda *hdr)
{
printk(KERN_INFO "BIOS console v1 EBDA structure found at %p\n", hdr);
printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
"start = %d, end = %d, num = %d\n",
hdr->v1.buffer_addr, hdr->v1.start,
hdr->v1.end, hdr->v1.num_chars);

memconsole_length = hdr->v1.num_chars;
memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr);
}

static void found_v2_header(struct biosmemcon_ebda *hdr)
{
printk(KERN_INFO "BIOS console v2 EBDA structure found at %p\n", hdr);
printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
"start = %d, end = %d, num_bytes = %d\n",
hdr->v2.buffer_addr, hdr->v2.start,
hdr->v2.end, hdr->v2.num_bytes);

memconsole_length = hdr->v2.end - hdr->v2.start;
memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr
+ hdr->v2.start);
}

/*
* Search through the EBDA for the BIOS Memory Console, and
* set the global variables to point to it. Return true if found.
*/
static bool found_memconsole(void)
{
unsigned int address;
size_t length, cur;

address = get_bios_ebda();
if (!address) {
printk(KERN_INFO "BIOS EBDA non-existent.\n");
return false;
}

/* EBDA length is byte 0 of EBDA (in KB) */
length = *(u8 *)phys_to_virt(address);
length <<= 10; /* convert to bytes */

/*
* Search through EBDA for BIOS memory console structure
* note: signature is not necessarily dword-aligned
*/
for (cur = 0; cur < length; cur++) {
struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);

/* memconsole v1 */
if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
found_v1_header(hdr);
return true;
}

/* memconsole v2 */
if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
found_v2_header(hdr);
return true;
}
}

printk(KERN_INFO "BIOS console EBDA structure not found!\n");
return false;
}

static struct dmi_system_id memconsole_dmi_table[] __initdata = {
{
.ident = "Google Board",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
},
},
{}
};
MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);

static int __init memconsole_init(void)
{
int ret;

if (!dmi_check_system(memconsole_dmi_table))
return -ENODEV;

if (!found_memconsole())
return -ENODEV;

memconsole_bin_attr.size = memconsole_length;

ret = sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);

return ret;
}

static void __exit memconsole_exit(void)
{
sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
}

module_init(memconsole_init);
module_exit(memconsole_exit);

MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

0 comments on commit e561bc4

Please sign in to comment.