Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags   |
// SPDX-License-Identifier: GPL-2.0-only
/*
* vpd.c
*
* Driver for exporting VPD content to sysfs.
*
* Copyright 2017 Google Inc.
*/
#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[];
};
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, u32 key_len,
<------><------><------><------> const u8 *value, u32 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);
<------>if (!info)
<------><------>return -ENOMEM;
<------>info->key = kstrndup(key, key_len, GFP_KERNEL);
<------>if (!info->key) {
<------><------>ret = -ENOMEM;
<------><------>goto free_info;
<------>}
<------>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);
<------>ret = sysfs_create_bin_file(sec->kobj, &info->bin_attr);
<------>if (ret)
<------><------>goto free_info_key;
<------>list_add_tail(&info->list, &sec->attribs);
<------>return 0;
free_info_key:
<------>kfree(info->key);
free_info:
<------>kfree(info);
<------>return ret;
}
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) {
<------><------>sysfs_remove_bin_file(sec->kobj, &info->bin_attr);
<------><------>kfree(info->key);
<------><------>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 err;
<------>sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
<------>if (!sec->baseaddr)
<------><------>return -ENOMEM;
<------>sec->name = name;
<------>/* We want to export the raw partition with name ${name}_raw */
<------>sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
<------>if (!sec->raw_name) {
<------><------>err = -ENOMEM;
<------><------>goto err_memunmap;
<------>}
<------>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;
<------>err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
<------>if (err)
<------><------>goto err_free_raw_name;
<------>sec->kobj = kobject_create_and_add(name, vpd_kobj);
<------>if (!sec->kobj) {
<------><------>err = -EINVAL;
<------><------>goto err_sysfs_remove;
<------>}
<------>INIT_LIST_HEAD(&sec->attribs);
<------>vpd_section_create_attribs(sec);
<------>sec->enabled = true;
<------>return 0;
err_sysfs_remove:
<------>sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
err_free_raw_name:
<------>kfree(sec->raw_name);
err_memunmap:
<------>memunmap(sec->baseaddr);
<------>return err;
}
static int vpd_section_destroy(struct vpd_section *sec)
{
<------>if (sec->enabled) {
<------><------>vpd_section_attrib_destroy(sec);
<------><------>kobject_put(sec->kobj);
<------><------>sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
<------><------>kfree(sec->raw_name);
<------><------>memunmap(sec->baseaddr);
<------><------>sec->enabled = false;
<------>}
<------>return 0;
}
static int vpd_sections_init(phys_addr_t physaddr)
{
<------>struct vpd_cbmem *temp;
<------>struct vpd_cbmem header;
<------>int ret = 0;
<------>temp = memremap(physaddr, sizeof(struct vpd_cbmem), MEMREMAP_WB);
<------>if (!temp)
<------><------>return -ENOMEM;
<------>memcpy(&header, temp, sizeof(struct vpd_cbmem));
<------>memunmap(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) {
<------><------><------>vpd_section_destroy(&ro_vpd);
<------><------><------>return ret;
<------><------>}
<------>}
<------>return 0;
}
static int vpd_probe(struct coreboot_device *dev)
{
<------>int ret;
<------>vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
<------>if (!vpd_kobj)
<------><------>return -ENOMEM;
<------>ret = vpd_sections_init(dev->cbmem_ref.cbmem_addr);
<------>if (ret) {
<------><------>kobject_put(vpd_kobj);
<------><------>return ret;
<------>}
<------>return 0;
}
static int vpd_remove(struct coreboot_device *dev)
{
<------>vpd_section_destroy(&ro_vpd);
<------>vpd_section_destroy(&rw_vpd);
<------>kobject_put(vpd_kobj);
<------>return 0;
}
static struct coreboot_driver vpd_driver = {
<------>.probe = vpd_probe,
<------>.remove = vpd_remove,
<------>.drv = {
<------><------>.name = "vpd",
<------>},
<------>.tag = CB_TAG_VPD,
};
module_coreboot_driver(vpd_driver);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");