mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
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:
commit
af82455f7d
166 changed files with 8064 additions and 3259 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
88
drivers/firmware/google/coreboot_table-acpi.c
Normal file
88
drivers/firmware/google/coreboot_table-acpi.c
Normal 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");
|
||||
82
drivers/firmware/google/coreboot_table-of.c
Normal file
82
drivers/firmware/google/coreboot_table-of.c
Normal 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");
|
||||
94
drivers/firmware/google/coreboot_table.c
Normal file
94
drivers/firmware/google/coreboot_table.c
Normal 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");
|
||||
50
drivers/firmware/google/coreboot_table.h
Normal file
50
drivers/firmware/google/coreboot_table.h
Normal 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 */
|
||||
109
drivers/firmware/google/memconsole-coreboot.c
Normal file
109
drivers/firmware/google/memconsole-coreboot.c
Normal 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");
|
||||
153
drivers/firmware/google/memconsole-x86-legacy.c
Normal file
153
drivers/firmware/google/memconsole-x86-legacy.c
Normal 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");
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
43
drivers/firmware/google/memconsole.h
Normal file
43
drivers/firmware/google/memconsole.h
Normal 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 */
|
||||
332
drivers/firmware/google/vpd.c
Normal file
332
drivers/firmware/google/vpd.c
Normal 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");
|
||||
99
drivers/firmware/google/vpd_decode.c
Normal file
99
drivers/firmware/google/vpd_decode.c
Normal 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;
|
||||
}
|
||||
58
drivers/firmware/google/vpd_decode.h
Normal file
58
drivers/firmware/google/vpd_decode.h
Normal 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 */
|
||||
Loading…
Reference in a new issue