char/misc patches for 4.12-rc1

Here is the big set of new char/misc driver drivers and features for
 4.12-rc1.
 
 There's lots of new drivers added this time around, new firmware drivers
 from Google, more auxdisplay drivers, extcon drivers, fpga drivers, and
 a bunch of other driver updates.  Nothing major, except if you happen to
 have the hardware for these drivers, and then you will be happy :)
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWQvAgg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yknsACgzkAeyz16Z97J3UTaeejbR7nKUCAAoKY4WEHY
 8O9f9pr9gj8GMBwxeZQa
 =OIfB
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the big set of new char/misc driver drivers and features for
  4.12-rc1.

  There's lots of new drivers added this time around, new firmware
  drivers from Google, more auxdisplay drivers, extcon drivers, fpga
  drivers, and a bunch of other driver updates. Nothing major, except if
  you happen to have the hardware for these drivers, and then you will
  be happy :)

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (136 commits)
  firmware: google memconsole: Fix return value check in platform_memconsole_init()
  firmware: Google VPD: Fix return value check in vpd_platform_init()
  goldfish_pipe: fix build warning about using too much stack.
  goldfish_pipe: An implementation of more parallel pipe
  fpga fr br: update supported version numbers
  fpga: region: release FPGA region reference in error path
  fpga altera-hps2fpga: disable/unprepare clock on error in alt_fpga_bridge_probe()
  mei: drop the TODO from samples
  firmware: Google VPD sysfs driver
  firmware: Google VPD: import lib_vpd source files
  misc: lkdtm: Add volatile to intentional NULL pointer reference
  eeprom: idt_89hpesx: Add OF device ID table
  misc: ds1682: Add OF device ID table
  misc: tsl2550: Add OF device ID table
  w1: Remove unneeded use of assert() and remove w1_log.h
  w1: Use kernel common min() implementation
  uio_mf624: Align memory regions to page size and set correct offsets
  uio_mf624: Refactor memory info initialization
  uio: Allow handling of non page-aligned memory regions
  hangcheck-timer: Fix typo in comment
  ...
This commit is contained in:
Linus Torvalds 2017-05-04 19:07:10 -07:00
commit af82455f7d
166 changed files with 8064 additions and 3259 deletions

View file

@ -1,18 +1,16 @@
config GOOGLE_FIRMWARE
menuconfig GOOGLE_FIRMWARE
bool "Google Firmware Drivers"
depends on X86
default n
help
These firmware drivers are used by Google's servers. They are
only useful if you are working directly on one of their
proprietary servers. If in doubt, say "N".
menu "Google Firmware Drivers"
depends on GOOGLE_FIRMWARE
if GOOGLE_FIRMWARE
config GOOGLE_SMI
tristate "SMI interface for Google platforms"
depends on ACPI && DMI && EFI
depends on X86 && ACPI && DMI && EFI
select EFI_VARS
help
Say Y here if you want to enable SMI callbacks for Google
@ -20,12 +18,57 @@ config GOOGLE_SMI
clearing the EFI event log and reading and writing NVRAM
variables.
config GOOGLE_COREBOOT_TABLE
tristate
depends on GOOGLE_COREBOOT_TABLE_ACPI || GOOGLE_COREBOOT_TABLE_OF
config GOOGLE_COREBOOT_TABLE_ACPI
tristate "Coreboot Table Access - ACPI"
depends on ACPI
select GOOGLE_COREBOOT_TABLE
help
This option enables the coreboot_table module, which provides other
firmware modules to access to the coreboot table. The coreboot table
pointer is accessed through the ACPI "GOOGCB00" object.
If unsure say N.
config GOOGLE_COREBOOT_TABLE_OF
tristate "Coreboot Table Access - Device Tree"
depends on OF
select GOOGLE_COREBOOT_TABLE
help
This option enable the coreboot_table module, which provide other
firmware modules to access coreboot table. The coreboot table pointer
is accessed through the device tree node /firmware/coreboot.
If unsure say N.
config GOOGLE_MEMCONSOLE
tristate "Firmware Memory Console"
depends on DMI
tristate
depends on GOOGLE_MEMCONSOLE_X86_LEGACY || GOOGLE_MEMCONSOLE_COREBOOT
config GOOGLE_MEMCONSOLE_X86_LEGACY
tristate "Firmware Memory Console - X86 Legacy support"
depends on X86 && ACPI && DMI
select GOOGLE_MEMCONSOLE
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.
endmenu
config GOOGLE_MEMCONSOLE_COREBOOT
tristate "Firmware Memory Console"
depends on GOOGLE_COREBOOT_TABLE
select GOOGLE_MEMCONSOLE
help
This option enables the kernel to search for a firmware log in
the coreboot table. If found, this log is exported to userland
in the file /sys/firmware/log.
config GOOGLE_VPD
tristate "Vital Product Data"
depends on GOOGLE_COREBOOT_TABLE
help
This option enables the kernel to expose the content of Google VPD
under /sys/firmware/vpd.
endif # GOOGLE_FIRMWARE

View file

@ -1,3 +1,11 @@
obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
vpd-sysfs-y := vpd.o vpd_decode.o
obj-$(CONFIG_GOOGLE_VPD) += vpd-sysfs.o

View file

@ -0,0 +1,88 @@
/*
* coreboot_table-acpi.c
*
* Using ACPI to locate Coreboot table and provide coreboot table access.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "coreboot_table.h"
static int coreboot_table_acpi_probe(struct platform_device *pdev)
{
phys_addr_t phyaddr;
resource_size_t len;
struct coreboot_table_header __iomem *header = NULL;
struct resource *res;
void __iomem *ptr = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
len = resource_size(res);
if (!res->start || !len)
return -EINVAL;
phyaddr = res->start;
header = ioremap_cache(phyaddr, sizeof(*header));
if (header == NULL)
return -ENOMEM;
ptr = ioremap_cache(phyaddr,
header->header_bytes + header->table_bytes);
iounmap(header);
if (!ptr)
return -ENOMEM;
return coreboot_table_init(ptr);
}
static int coreboot_table_acpi_remove(struct platform_device *pdev)
{
return coreboot_table_exit();
}
static const struct acpi_device_id cros_coreboot_acpi_match[] = {
{ "GOOGCB00", 0 },
{ "BOOT0000", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
static struct platform_driver coreboot_table_acpi_driver = {
.probe = coreboot_table_acpi_probe,
.remove = coreboot_table_acpi_remove,
.driver = {
.name = "coreboot_table_acpi",
.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
},
};
static int __init coreboot_table_acpi_init(void)
{
return platform_driver_register(&coreboot_table_acpi_driver);
}
module_init(coreboot_table_acpi_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,82 @@
/*
* coreboot_table-of.c
*
* Coreboot table access through open firmware.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "coreboot_table.h"
static int coreboot_table_of_probe(struct platform_device *pdev)
{
struct device_node *fw_dn = pdev->dev.of_node;
void __iomem *ptr;
ptr = of_iomap(fw_dn, 0);
of_node_put(fw_dn);
if (!ptr)
return -ENOMEM;
return coreboot_table_init(ptr);
}
static int coreboot_table_of_remove(struct platform_device *pdev)
{
return coreboot_table_exit();
}
static const struct of_device_id coreboot_of_match[] = {
{ .compatible = "coreboot" },
{},
};
static struct platform_driver coreboot_table_of_driver = {
.probe = coreboot_table_of_probe,
.remove = coreboot_table_of_remove,
.driver = {
.name = "coreboot_table_of",
.of_match_table = coreboot_of_match,
},
};
static int __init platform_coreboot_table_of_init(void)
{
struct platform_device *pdev;
struct device_node *of_node;
/* Limit device creation to the presence of /firmware/coreboot node */
of_node = of_find_node_by_path("/firmware/coreboot");
if (!of_node)
return -ENODEV;
if (!of_match_node(coreboot_of_match, of_node))
return -ENODEV;
pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL);
if (!pdev)
return -ENODEV;
return platform_driver_register(&coreboot_table_of_driver);
}
module_init(platform_coreboot_table_of_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,94 @@
/*
* coreboot_table.c
*
* Module providing coreboot table access.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "coreboot_table.h"
struct coreboot_table_entry {
u32 tag;
u32 size;
};
static struct coreboot_table_header __iomem *ptr_header;
/*
* This function parses the coreboot table for an entry that contains the base
* address of the given entry tag. The coreboot table consists of a header
* directly followed by a number of small, variable-sized entries, which each
* contain an identifying tag and their length as the first two fields.
*/
int coreboot_table_find(int tag, void *data, size_t data_size)
{
struct coreboot_table_header header;
struct coreboot_table_entry entry;
void *ptr_entry;
int i;
if (!ptr_header)
return -EPROBE_DEFER;
memcpy_fromio(&header, ptr_header, sizeof(header));
if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
pr_warn("coreboot_table: coreboot table missing or corrupt!\n");
return -ENODEV;
}
ptr_entry = (void *)ptr_header + header.header_bytes;
for (i = 0; i < header.table_entries; i++) {
memcpy_fromio(&entry, ptr_entry, sizeof(entry));
if (entry.tag == tag) {
if (data_size < entry.size)
return -EINVAL;
memcpy_fromio(data, ptr_entry, entry.size);
return 0;
}
ptr_entry += entry.size;
}
return -ENOENT;
}
EXPORT_SYMBOL(coreboot_table_find);
int coreboot_table_init(void __iomem *ptr)
{
ptr_header = ptr;
return 0;
}
EXPORT_SYMBOL(coreboot_table_init);
int coreboot_table_exit(void)
{
if (ptr_header)
iounmap(ptr_header);
return 0;
}
EXPORT_SYMBOL(coreboot_table_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,50 @@
/*
* coreboot_table.h
*
* Internal header for coreboot table access.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __COREBOOT_TABLE_H
#define __COREBOOT_TABLE_H
#include <linux/io.h>
/* List of coreboot entry structures that is used */
struct lb_cbmem_ref {
uint32_t tag;
uint32_t size;
uint64_t cbmem_addr;
};
/* Coreboot table header structure */
struct coreboot_table_header {
char signature[4];
u32 header_bytes;
u32 header_checksum;
u32 table_bytes;
u32 table_checksum;
u32 table_entries;
};
/* Retrieve coreboot table entry with tag *tag* and copy it to data */
int coreboot_table_find(int tag, void *data, size_t data_size);
/* Initialize coreboot table module given a pointer to iomem */
int coreboot_table_init(void __iomem *ptr);
/* Cleanup coreboot table module */
int coreboot_table_exit(void);
#endif /* __COREBOOT_TABLE_H */

View file

@ -0,0 +1,109 @@
/*
* memconsole-coreboot.c
*
* Memory based BIOS console accessed through coreboot table.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "memconsole.h"
#include "coreboot_table.h"
#define CB_TAG_CBMEM_CONSOLE 0x17
/* CBMEM firmware console log descriptor. */
struct cbmem_cons {
u32 buffer_size;
u32 buffer_cursor;
u8 buffer_body[0];
} __packed;
static struct cbmem_cons __iomem *cbmem_console;
static int memconsole_coreboot_init(phys_addr_t physaddr)
{
struct cbmem_cons __iomem *tmp_cbmc;
tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB);
if (!tmp_cbmc)
return -ENOMEM;
cbmem_console = memremap(physaddr,
tmp_cbmc->buffer_size + sizeof(*cbmem_console),
MEMREMAP_WB);
memunmap(tmp_cbmc);
if (!cbmem_console)
return -ENOMEM;
memconsole_setup(cbmem_console->buffer_body,
min(cbmem_console->buffer_cursor, cbmem_console->buffer_size));
return 0;
}
static int memconsole_probe(struct platform_device *pdev)
{
int ret;
struct lb_cbmem_ref entry;
ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry));
if (ret)
return ret;
ret = memconsole_coreboot_init(entry.cbmem_addr);
if (ret)
return ret;
return memconsole_sysfs_init();
}
static int memconsole_remove(struct platform_device *pdev)
{
memconsole_exit();
if (cbmem_console)
memunmap(cbmem_console);
return 0;
}
static struct platform_driver memconsole_driver = {
.probe = memconsole_probe,
.remove = memconsole_remove,
.driver = {
.name = "memconsole",
},
};
static int __init platform_memconsole_init(void)
{
struct platform_device *pdev;
pdev = platform_device_register_simple("memconsole", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
platform_driver_register(&memconsole_driver);
return 0;
}
module_init(platform_memconsole_init);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,153 @@
/*
* memconsole-x86-legacy.c
*
* EBDA specific parts of the memory based BIOS console.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/mm.h>
#include <asm/bios_ebda.h>
#include <linux/acpi.h>
#include "memconsole.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 void found_v1_header(struct biosmemcon_ebda *hdr)
{
pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n",
hdr);
pr_info("memconsole: 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_setup(phys_to_virt(hdr->v1.buffer_addr), hdr->v1.num_chars);
}
static void found_v2_header(struct biosmemcon_ebda *hdr)
{
pr_info("memconsole: BIOS console v2 EBDA structure found at %p\n",
hdr);
pr_info("memconsole: 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_setup(phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start),
hdr->v2.end - 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 memconsole_ebda_init(void)
{
unsigned int address;
size_t length, cur;
address = get_bios_ebda();
if (!address) {
pr_info("memconsole: 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;
}
}
pr_info("memconsole: 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 bool __init memconsole_find(void)
{
if (!dmi_check_system(memconsole_dmi_table))
return false;
return memconsole_ebda_init();
}
static int __init memconsole_x86_init(void)
{
if (!memconsole_find())
return -ENODEV;
return memconsole_sysfs_init();
}
static void __exit memconsole_x86_exit(void)
{
memconsole_exit();
}
module_init(memconsole_x86_init);
module_exit(memconsole_x86_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -1,66 +1,36 @@
/*
* memconsole.c
*
* Infrastructure for importing the BIOS memory based console
* into the kernel log ringbuffer.
* Architecture-independent parts of the memory based BIOS console.
*
* Copyright 2010 Google Inc. All rights reserved.
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#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 <linux/io.h>
#include <asm/bios_ebda.h>
#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))
#include "memconsole.h"
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 u32 memconsole_baseaddr;
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)
{
char *memconsole;
ssize_t ret;
memconsole = ioremap_cache(memconsole_baseaddr, memconsole_length);
if (!memconsole) {
pr_err("memconsole: ioremap_cache failed\n");
return -ENOMEM;
}
ret = memory_read_from_buffer(buf, count, &pos, memconsole,
memconsole_length);
iounmap(memconsole);
return ret;
return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
memconsole_length);
}
static struct bin_attribute memconsole_bin_attr = {
@ -68,104 +38,25 @@ static struct bin_attribute memconsole_bin_attr = {
.read = memconsole_read,
};
static void __init found_v1_header(struct biosmemcon_ebda *hdr)
void memconsole_setup(void *baseaddr, size_t length)
{
pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
pr_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 = hdr->v1.buffer_addr;
memconsole_baseaddr = baseaddr;
memconsole_length = length;
}
EXPORT_SYMBOL(memconsole_setup);
static void __init found_v2_header(struct biosmemcon_ebda *hdr)
int memconsole_sysfs_init(void)
{
pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
pr_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 = 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 __init found_memconsole(void)
{
unsigned int address;
size_t length, cur;
address = get_bios_ebda();
if (!address) {
pr_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;
}
}
pr_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)
{
if (!dmi_check_system(memconsole_dmi_table))
return -ENODEV;
if (!found_memconsole())
return -ENODEV;
memconsole_bin_attr.size = memconsole_length;
return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
}
EXPORT_SYMBOL(memconsole_sysfs_init);
static void __exit memconsole_exit(void)
void memconsole_exit(void)
{
sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
}
module_init(memconsole_init);
module_exit(memconsole_exit);
EXPORT_SYMBOL(memconsole_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,43 @@
/*
* memconsole.h
*
* Internal headers of the memory based BIOS console.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H
#define __FIRMWARE_GOOGLE_MEMCONSOLE_H
/*
* memconsole_setup
*
* Initialize the memory console from raw (virtual) base
* address and length.
*/
void memconsole_setup(void *baseaddr, size_t length);
/*
* memconsole_sysfs_init
*
* Update memory console length and create binary file
* for firmware object.
*/
int memconsole_sysfs_init(void);
/* memconsole_exit
*
* Unmap the console buffer.
*/
void memconsole_exit(void);
#endif /* __FIRMWARE_GOOGLE_MEMCONSOLE_H */

View file

@ -0,0 +1,332 @@
/*
* vpd.c
*
* Driver for exporting VPD content to sysfs.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "coreboot_table.h"
#include "vpd_decode.h"
#define CB_TAG_VPD 0x2c
#define VPD_CBMEM_MAGIC 0x43524f53
static struct kobject *vpd_kobj;
struct vpd_cbmem {
u32 magic;
u32 version;
u32 ro_size;
u32 rw_size;
u8 blob[0];
};
struct vpd_section {
bool enabled;
const char *name;
char *raw_name; /* the string name_raw */
struct kobject *kobj; /* vpd/name directory */
char *baseaddr;
struct bin_attribute bin_attr; /* vpd/name_raw bin_attribute */
struct list_head attribs; /* key/value in vpd_attrib_info list */
};
struct vpd_attrib_info {
char *key;
const char *value;
struct bin_attribute bin_attr;
struct list_head list;
};
static struct vpd_section ro_vpd;
static struct vpd_section rw_vpd;
static ssize_t vpd_attrib_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count)
{
struct vpd_attrib_info *info = bin_attr->private;
return memory_read_from_buffer(buf, count, &pos, info->value,
info->bin_attr.size);
}
/*
* vpd_section_check_key_name()
*
* The VPD specification supports only [a-zA-Z0-9_]+ characters in key names but
* old firmware versions may have entries like "S/N" which are problematic when
* exporting them as sysfs attributes. These keys present in old firmwares are
* ignored.
*
* Returns VPD_OK for a valid key name, VPD_FAIL otherwise.
*
* @key: The key name to check
* @key_len: key name length
*/
static int vpd_section_check_key_name(const u8 *key, s32 key_len)
{
int c;
while (key_len-- > 0) {
c = *key++;
if (!isalnum(c) && c != '_')
return VPD_FAIL;
}
return VPD_OK;
}
static int vpd_section_attrib_add(const u8 *key, s32 key_len,
const u8 *value, s32 value_len,
void *arg)
{
int ret;
struct vpd_section *sec = arg;
struct vpd_attrib_info *info;
/*
* Return VPD_OK immediately to decode next entry if the current key
* name contains invalid characters.
*/
if (vpd_section_check_key_name(key, key_len) != VPD_OK)
return VPD_OK;
info = kzalloc(sizeof(*info), GFP_KERNEL);
info->key = kzalloc(key_len + 1, GFP_KERNEL);
if (!info->key)
return -ENOMEM;
memcpy(info->key, key, key_len);
sysfs_bin_attr_init(&info->bin_attr);
info->bin_attr.attr.name = info->key;
info->bin_attr.attr.mode = 0444;
info->bin_attr.size = value_len;
info->bin_attr.read = vpd_attrib_read;
info->bin_attr.private = info;
info->value = value;
INIT_LIST_HEAD(&info->list);
list_add_tail(&info->list, &sec->attribs);
ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
if (ret) {
kfree(info->key);
return ret;
}
return 0;
}
static void vpd_section_attrib_destroy(struct vpd_section *sec)
{
struct vpd_attrib_info *info;
struct vpd_attrib_info *temp;
list_for_each_entry_safe(info, temp, &sec->attribs, list) {
kfree(info->key);
sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
kfree(info);
}
}
static ssize_t vpd_section_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count)
{
struct vpd_section *sec = bin_attr->private;
return memory_read_from_buffer(buf, count, &pos, sec->baseaddr,
sec->bin_attr.size);
}
static int vpd_section_create_attribs(struct vpd_section *sec)
{
s32 consumed;
int ret;
consumed = 0;
do {
ret = vpd_decode_string(sec->bin_attr.size, sec->baseaddr,
&consumed, vpd_section_attrib_add, sec);
} while (ret == VPD_OK);
return 0;
}
static int vpd_section_init(const char *name, struct vpd_section *sec,
phys_addr_t physaddr, size_t size)
{
int ret;
int raw_len;
sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
if (!sec->baseaddr)
return -ENOMEM;
sec->name = name;
/* We want to export the raw partion with name ${name}_raw */
raw_len = strlen(name) + 5;
sec->raw_name = kzalloc(raw_len, GFP_KERNEL);
strncpy(sec->raw_name, name, raw_len);
strncat(sec->raw_name, "_raw", raw_len);
sysfs_bin_attr_init(&sec->bin_attr);
sec->bin_attr.attr.name = sec->raw_name;
sec->bin_attr.attr.mode = 0444;
sec->bin_attr.size = size;
sec->bin_attr.read = vpd_section_read;
sec->bin_attr.private = sec;
ret = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
if (ret)
goto free_sec;
sec->kobj = kobject_create_and_add(name, vpd_kobj);
if (!sec->kobj) {
ret = -EINVAL;
goto sysfs_remove;
}
INIT_LIST_HEAD(&sec->attribs);
vpd_section_create_attribs(sec);
sec->enabled = true;
return 0;
sysfs_remove:
sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
free_sec:
kfree(sec->raw_name);
iounmap(sec->baseaddr);
return ret;
}
static int vpd_section_destroy(struct vpd_section *sec)
{
if (sec->enabled) {
vpd_section_attrib_destroy(sec);
kobject_del(sec->kobj);
sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
kfree(sec->raw_name);
iounmap(sec->baseaddr);
}
return 0;
}
static int vpd_sections_init(phys_addr_t physaddr)
{
struct vpd_cbmem __iomem *temp;
struct vpd_cbmem header;
int ret = 0;
temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
if (!temp)
return -ENOMEM;
memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem));
iounmap(temp);
if (header.magic != VPD_CBMEM_MAGIC)
return -ENODEV;
if (header.ro_size) {
ret = vpd_section_init("ro", &ro_vpd,
physaddr + sizeof(struct vpd_cbmem),
header.ro_size);
if (ret)
return ret;
}
if (header.rw_size) {
ret = vpd_section_init("rw", &rw_vpd,
physaddr + sizeof(struct vpd_cbmem) +
header.ro_size, header.rw_size);
if (ret)
return ret;
}
return 0;
}
static int vpd_probe(struct platform_device *pdev)
{
int ret;
struct lb_cbmem_ref entry;
ret = coreboot_table_find(CB_TAG_VPD, &entry, sizeof(entry));
if (ret)
return ret;
return vpd_sections_init(entry.cbmem_addr);
}
static struct platform_driver vpd_driver = {
.probe = vpd_probe,
.driver = {
.name = "vpd",
},
};
static int __init vpd_platform_init(void)
{
struct platform_device *pdev;
pdev = platform_device_register_simple("vpd", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
if (!vpd_kobj)
return -ENOMEM;
memset(&ro_vpd, 0, sizeof(ro_vpd));
memset(&rw_vpd, 0, sizeof(rw_vpd));
platform_driver_register(&vpd_driver);
return 0;
}
static void __exit vpd_platform_exit(void)
{
vpd_section_destroy(&ro_vpd);
vpd_section_destroy(&rw_vpd);
kobject_del(vpd_kobj);
}
module_init(vpd_platform_init);
module_exit(vpd_platform_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,99 @@
/*
* vpd_decode.c
*
* Google VPD decoding routines.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/export.h>
#include "vpd_decode.h"
static int vpd_decode_len(const s32 max_len, const u8 *in,
s32 *length, s32 *decoded_len)
{
u8 more;
int i = 0;
if (!length || !decoded_len)
return VPD_FAIL;
*length = 0;
do {
if (i >= max_len)
return VPD_FAIL;
more = in[i] & 0x80;
*length <<= 7;
*length |= in[i] & 0x7f;
++i;
} while (more);
*decoded_len = i;
return VPD_OK;
}
int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
vpd_decode_callback callback, void *callback_arg)
{
int type;
int res;
s32 key_len;
s32 value_len;
s32 decoded_len;
const u8 *key;
const u8 *value;
/* type */
if (*consumed >= max_len)
return VPD_FAIL;
type = input_buf[*consumed];
switch (type) {
case VPD_TYPE_INFO:
case VPD_TYPE_STRING:
(*consumed)++;
/* key */
res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
&key_len, &decoded_len);
if (res != VPD_OK || *consumed + decoded_len >= max_len)
return VPD_FAIL;
*consumed += decoded_len;
key = &input_buf[*consumed];
*consumed += key_len;
/* value */
res = vpd_decode_len(max_len - *consumed, &input_buf[*consumed],
&value_len, &decoded_len);
if (res != VPD_OK || *consumed + decoded_len > max_len)
return VPD_FAIL;
*consumed += decoded_len;
value = &input_buf[*consumed];
*consumed += value_len;
if (type == VPD_TYPE_STRING)
return callback(key, key_len, value, value_len,
callback_arg);
break;
default:
return VPD_FAIL;
}
return VPD_OK;
}

View file

@ -0,0 +1,58 @@
/*
* vpd_decode.h
*
* Google VPD decoding routines.
*
* Copyright 2017 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __VPD_DECODE_H
#define __VPD_DECODE_H
#include <linux/types.h>
enum {
VPD_OK = 0,
VPD_FAIL,
};
enum {
VPD_TYPE_TERMINATOR = 0,
VPD_TYPE_STRING,
VPD_TYPE_INFO = 0xfe,
VPD_TYPE_IMPLICIT_TERMINATOR = 0xff,
};
/* Callback for vpd_decode_string to invoke. */
typedef int vpd_decode_callback(const u8 *key, s32 key_len,
const u8 *value, s32 value_len,
void *arg);
/*
* vpd_decode_string
*
* Given the encoded string, this function invokes callback with extracted
* (key, value). The *consumed will be plused the number of bytes consumed in
* this function.
*
* The input_buf points to the first byte of the input buffer.
*
* The *consumed starts from 0, which is actually the next byte to be decoded.
* It can be non-zero to be used in multiple calls.
*
* If one entry is successfully decoded, sends it to callback and returns the
* result.
*/
int vpd_decode_string(const s32 max_len, const u8 *input_buf, s32 *consumed,
vpd_decode_callback callback, void *callback_arg);
#endif /* __VPD_DECODE_H */