blob: 276c69420e00ba8ac759e30c0a36147ddb3bb3e1 [file] [log] [blame]
/*
* Copyright (c) 2014 - 2015, 2020, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/qcom_scm.h>
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/of.h>
#define QFPROM_MAX_VERSION_EXCEEDED 0x10
#define QFPROM_IS_AUTHENTICATE_CMD_RSP_SIZE 0x2
#define SW_TYPE_DEFAULT 0xFF
#define SW_TYPE_SBL 0x0
#define SW_TYPE_TZ 0x7
#define SW_TYPE_APPSBL 0x9
#define SW_TYPE_HLOS 0x17
#define SW_TYPE_RPM 0xA
#define SW_TYPE_DEVCFG 0x5
#define SW_TYPE_APDP 0x200
static int gl_version_enable;
static int fuse_blow_size_required;
static ssize_t
qfprom_show_authenticate(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
ret = qti_qfprom_show_authenticate();
if (ret == -1)
return ret;
/* show needs a string response */
if (ret == 1)
buf[0] = '1';
else
buf[0] = '0';
buf[1] = '\0';
return QFPROM_IS_AUTHENTICATE_CMD_RSP_SIZE;
}
int write_version(struct device *dev, uint32_t type, uint32_t version)
{
int ret;
uint32_t qfprom_ret_ptr;
uint32_t *qfprom_api_status = kzalloc(sizeof(uint32_t), GFP_KERNEL);
uint32_t ret_size = sizeof(*qfprom_api_status);
if (!qfprom_api_status)
return -ENOMEM;
qfprom_ret_ptr = dma_map_single(dev, qfprom_api_status,
sizeof(*qfprom_api_status), DMA_FROM_DEVICE);
ret = dma_mapping_error(dev, qfprom_ret_ptr);
if (ret) {
pr_err("DMA Mapping Error(api_status)\n");
goto err_write;
}
ret = qti_qfprom_write_version(type, version, qfprom_ret_ptr,
ret_size);
dma_unmap_single(dev, qfprom_ret_ptr,
sizeof(*qfprom_api_status), DMA_FROM_DEVICE);
if(ret)
pr_err("%s: Error in QFPROM write (%d, %d)\n",
__func__, ret, *qfprom_api_status);
if (*qfprom_api_status == QFPROM_MAX_VERSION_EXCEEDED)
pr_err("Version %u exceeds maximum limit. All fuses blown.\n",
version);
err_write:
kfree(qfprom_api_status);
return ret;
}
int read_version(struct device *dev, int type, uint32_t **version_ptr)
{
int ret, ret1, ret2;
struct qfprom_read {
uint32_t sw_type;
uint32_t value;
uint32_t qfprom_ret_ptr;
} rdip;
uint32_t *qfprom_api_status = kzalloc(sizeof(uint32_t), GFP_KERNEL);
if (!qfprom_api_status)
return -ENOMEM;
rdip.sw_type = type;
rdip.value = dma_map_single(dev, *version_ptr,
sizeof(uint32_t), DMA_FROM_DEVICE);
rdip.qfprom_ret_ptr = dma_map_single(dev, qfprom_api_status,
sizeof(*qfprom_api_status), DMA_FROM_DEVICE);
ret1 = dma_mapping_error(dev, rdip.value);
ret2 = dma_mapping_error(dev, rdip.qfprom_ret_ptr);
if (ret1 == 0 && ret2 == 0) {
ret = qti_qfprom_read_version(type, rdip.value,
rdip.qfprom_ret_ptr);
}
if (ret1 == 0) {
dma_unmap_single(dev, rdip.value,
sizeof(uint32_t), DMA_FROM_DEVICE);
}
if (ret2 == 0) {
dma_unmap_single(dev, rdip.qfprom_ret_ptr,
sizeof(*qfprom_api_status), DMA_FROM_DEVICE);
}
if (ret1 || ret2) {
pr_err("DMA Mapping Error version ret %d api_status ret %d\n",
ret1, ret2);
ret = ret1 ? ret1 : ret2;
goto err_read;
}
if (ret || *qfprom_api_status) {
pr_err("%s: Error in QFPROM read (%d, %d)\n",
__func__, ret, *qfprom_api_status);
}
err_read:
kfree(qfprom_api_status);
return ret;
}
static ssize_t generic_version(struct device *dev, const char *buf,
uint32_t sw_type, int op, size_t count)
{
int ret = 0;
uint32_t *version = kzalloc(sizeof(uint32_t), GFP_KERNEL);
if (!version)
return -ENOMEM;
/*
* Operation Type: Read: 1 and Write: 2
*/
switch (op) {
case 1:
ret = read_version(dev, sw_type, &version);
if (ret) {
pr_err("Error in reading version: %d\n", ret);
goto err_generic;
}
ret = snprintf((char *)buf, 10, "%d\n", *version);
break;
case 2:
/* Input validation handled here */
ret = kstrtouint(buf, 0, version);
if (ret)
goto err_generic;
/* Commit version is true if user input is greater than 0 */
if (*version <= 0) {
ret = -EINVAL;
goto err_generic;
}
ret = write_version(dev, sw_type, *version);
if (ret) {
pr_err("Error in writing version: %d\n", ret);
goto err_generic;
}
ret = count;
break;
default:
ret = -EINVAL;
}
err_generic:
kfree(version);
return ret;
}
static ssize_t
show_sbl_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_SBL, 1, 0);
}
static ssize_t
show_tz_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_TZ, 1, 0);
}
static ssize_t
show_appsbl_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_APPSBL, 1, 0);
}
static ssize_t
show_hlos_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_HLOS, 1, 0);
}
static ssize_t
show_rpm_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_RPM, 1, 0);
}
static ssize_t
show_devcfg_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_DEVCFG, 1, 0);
}
static ssize_t
show_apdp_version(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return generic_version(dev, buf, SW_TYPE_APDP, 1, 0);
}
static ssize_t
store_version_commit(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return generic_version(dev, buf, 0, 2, count);
}
static ssize_t
store_sec_auth(struct device *dev,
struct device_attribute *sec_attr,
const char *buf, size_t count)
{
int ret;
long size, sw_type;
unsigned int img_addr, img_size;
char *sw, *file_name, *sec_auth_str;
static void __iomem *file_buf;
struct device_node *np;
struct file *file;
struct kstat st;
u32 scm_cmd_id;
file_name = kzalloc(count+1, GFP_KERNEL);
if (file_name == NULL)
return -ENOMEM;
sec_auth_str = file_name;
strlcpy(file_name, buf, count+1);
sw = strsep(&file_name, " ");
ret = kstrtol(sw, 0, &sw_type);
if (ret) {
pr_err("sw_type str to long conversion failed\n");
goto free_mem;
}
file = filp_open(file_name, O_RDONLY, 0);
if (IS_ERR(file)) {
pr_err("%s File open failed\n", file_name);
ret = -EBADF;
goto free_mem;
}
ret = vfs_getattr(&file->f_path, &st, STATX_SIZE, AT_STATX_SYNC_AS_STAT);
if (ret) {
pr_err("get file attributes failed\n");
goto file_close;
}
size = (long)st.size;
np = of_find_node_by_name(NULL, "qfprom");
if (!np) {
pr_err("Unable to find qfprom node\n");
goto file_close;
}
ret = of_property_read_u32(np, "img-size", &img_size);
if (ret) {
pr_err("Read of property:img-size from node failed\n");
goto put_node;
}
if (size > img_size) {
pr_err("File size exceeds allocated memory region\n");
goto put_node;
}
ret = of_property_read_u32(np, "img-addr", &img_addr);
if (ret) {
pr_err("Read of property:img-addr from node failed\n");
goto put_node;
}
ret = of_property_read_u32(np, "scm-cmd-id", &scm_cmd_id);
if (ret)
scm_cmd_id = QTI_KERNEL_AUTH_CMD;
file_buf = ioremap_nocache(img_addr, img_size);
if (file_buf == NULL) {
ret = -ENOMEM;
goto put_node;
}
memset_io(file_buf, 0x0, img_size);
ret = kernel_read(file, file_buf, size, 0);
if (ret != size) {
pr_err("%s file read failed\n", file_name);
goto un_map;
}
ret = qti_sec_upgrade_auth(scm_cmd_id, sw_type, size, img_addr);
if (ret) {
pr_err("sec_upgrade_auth failed with return=%d\n", ret);
goto un_map;
}
ret = count;
un_map:
iounmap(file_buf);
put_node:
of_node_put(np);
file_close:
filp_close(file, NULL);
free_mem:
kfree(sec_auth_str);
return ret;
}
static struct device_attribute sec_attr =
__ATTR(sec_auth, 0644, NULL, store_sec_auth);
struct kobject *sec_kobj;
static ssize_t
store_sec_dat(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = count;
loff_t size;
unsigned long fuse_status = 0;
struct file *fptr = NULL;
struct kstat st;
void *ptr = NULL;
struct fuse_blow fuse_blow;
dma_addr_t dma_req_addr = 0;
size_t req_order = 0;
struct page *req_page = NULL;
int rc = 0;
u64 dma_size;
fptr = filp_open(buf, O_RDONLY, 0);
if (IS_ERR(fptr)) {
pr_err("%s File open failed\n", buf);
ret = -EBADF;
goto out;
}
ret = vfs_getattr(&fptr->f_path, &st, STATX_SIZE, AT_STATX_SYNC_AS_STAT);
if (ret) {
pr_err("Getting file attributes failed\n");
goto file_close;
}
size = st.size;
/* determine the allocation order of a memory size */
req_order = get_order(size);
/* allocate pages from the kernel page pool */
req_page = alloc_pages(GFP_KERNEL, req_order);
if (!req_page) {
ret = -ENOMEM;
goto file_close;
} else {
/* get the mapped virtual address of the page */
ptr = page_address(req_page);
}
memset(ptr, 0, size);
ret = kernel_read(fptr, ptr, size, 0);
if (ret != size) {
pr_err("File read failed\n");
goto free_page;
}
arch_setup_dma_ops(dev, 0, 0, NULL, 0);
dev->coherent_dma_mask = DMA_BIT_MASK(32);
dev->dma_pfn_offset = 0;
INIT_LIST_HEAD(&dev->dma_pools);
dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
dma_size = dev->coherent_dma_mask + 1;
/* map the memory region */
dma_req_addr = dma_map_single(dev, ptr, size, DMA_TO_DEVICE);
rc = dma_mapping_error(dev, dma_req_addr);
if (rc) {
pr_err("DMA Mapping Error\n");
dma_unmap_single(dev, dma_req_addr, size, DMA_TO_DEVICE);
free_pages((unsigned long)page_address(req_page), req_order);
goto file_close;
}
fuse_blow.address = dma_req_addr;
if (fuse_blow_size_required)
fuse_blow.size = size;
else
fuse_blow.size = 0;
fuse_blow.status = &fuse_status;
ret = qti_fuseipq_scm_call(dev, QTI_SCM_SVC_FUSE,
TZ_BLOW_FUSE_SECDAT, &fuse_blow,
sizeof(fuse_blow));
if (ret) {
pr_err("Error in QFPROM write (%d %lu)\n", ret, fuse_status);
goto free_mem;
}
if (fuse_status == FUSEPROV_SECDAT_LOCK_BLOWN)
pr_info("Fuse already blown\n");
else if (fuse_status == FUSEPROV_INVALID_HASH)
pr_info("Invalid sec.dat\n");
else if (fuse_status != FUSEPROV_SUCCESS)
pr_info("Failed to Blow fuses\n");
else
pr_info("Fuse Blow Success\n");
ret = count;
free_mem:
dma_unmap_single(dev, dma_req_addr, size, DMA_TO_DEVICE);
free_page:
free_pages((unsigned long)page_address(req_page), req_order);
file_close:
filp_close(fptr, NULL);
out:
return ret;
}
static struct device_attribute sec_dat_attr =
__ATTR(sec_dat, 0200, NULL, store_sec_dat);
/*
* Do not change the order of attributes.
* New types should be added at the end
*/
static struct device_attribute qfprom_attrs[] = {
__ATTR(authenticate, 0444, qfprom_show_authenticate, NULL),
__ATTR(sbl_version, 0444, show_sbl_version, NULL),
__ATTR(tz_version, 0444, show_tz_version, NULL),
__ATTR(appsbl_version, 0444, show_appsbl_version, NULL),
__ATTR(hlos_version, 0444, show_hlos_version, NULL),
__ATTR(rpm_version, 0444, show_rpm_version, NULL),
__ATTR(devcfg_version, 0444, show_devcfg_version, NULL),
__ATTR(apdp_version, 0444, show_apdp_version, NULL),
__ATTR(version_commit, 0200, NULL, store_version_commit),
};
static struct bus_type qfprom_subsys = {
.name = "qfprom",
.dev_name = "qfprom",
};
static struct device device_qfprom = {
.id = 0,
.bus = &qfprom_subsys,
};
static int __init qfprom_create_files(int size, int16_t sw_bitmap)
{
int i;
int err;
int sw_bit;
/* authenticate sysfs entry is mandatory */
err = device_create_file(&device_qfprom, &qfprom_attrs[0]);
if (err) {
pr_err("%s: device_create_file(%s)=%d\n",
__func__, qfprom_attrs[0].attr.name, err);
return err;
}
if (gl_version_enable != 1)
return 0;
for (i = 1; i < size; i++) {
if(strncmp(qfprom_attrs[i].attr.name, "version_commit",
strlen(qfprom_attrs[i].attr.name))) {
/*
* Following is the BitMap adapted:
* SBL:0 TZ:1 APPSBL:2 HLOS:3 RPM:4. New types should
* be added at the end of "qfprom_attrs" variable.
*/
sw_bit = i - 1;
if (!(sw_bitmap & (1 << sw_bit)))
break;
}
err = device_create_file(&device_qfprom, &qfprom_attrs[i]);
if (err) {
pr_err("%s: device_create_file(%s)=%d\n",
__func__, qfprom_attrs[i].attr.name, err);
return err;
}
}
/* setup the DMA framework for the device 'qfprom' */
device_qfprom.coherent_dma_mask = DMA_BIT_MASK(32);
device_qfprom.dma_pfn_offset = 0;
INIT_LIST_HEAD(&device_qfprom.dma_pools);
dma_coerce_mask_and_coherent(&device_qfprom, DMA_BIT_MASK(32));
arch_setup_dma_ops(&device_qfprom, 0, 0, NULL, 0);
return 0;
}
int is_version_rlbk_enabled(struct device *dev, int16_t *sw_bitmap)
{
int ret;
uint32_t *version_enable = kzalloc(sizeof(uint32_t), GFP_KERNEL);
if (!version_enable)
return -ENOMEM;
ret = read_version(dev, SW_TYPE_DEFAULT, &version_enable);
if (ret) {
pr_err("\n Version Read Failed with error %d", ret);
goto err_ver;
}
*sw_bitmap = ((*version_enable & 0xFFFF0000) >> 16);
ret = (*version_enable & 0x1);
err_ver:
kfree(version_enable);
return ret;
}
static int qfprom_probe(struct platform_device *pdev)
{
int err, ret;
int16_t sw_bitmap = 0;
struct device_node *np = pdev->dev.of_node;
u32 scm_cmd_id;
if (!qcom_scm_is_available()) {
pr_info("SCM call is not initialized, defering probe\n");
return -EPROBE_DEFER;
}
gl_version_enable = is_version_rlbk_enabled(&pdev->dev, &sw_bitmap);
if (gl_version_enable == 0)
pr_info("\nVersion Rollback Feature Disabled\n");
/*
* Registering under "/sys/devices/system"
*/
err = subsys_system_register(&qfprom_subsys, NULL);
if (err) {
pr_err("%s: subsys_system_register fail (%d)\n",
__func__, err);
return err;
}
device_register(&device_qfprom);
/*
* Registering sec_auth under "/sys/sec_authenticate"
only if board is secured
*/
ret = qti_qfprom_show_authenticate();
if (ret == -1)
return ret;
if (ret == 1) {
err = of_property_read_u32(np, "scm-cmd-id", &scm_cmd_id);
if (err)
scm_cmd_id = QTI_KERNEL_AUTH_CMD;
/*
* Checking if secure sysupgrade scm_call is supported
*/
if (!qti_scm_sec_auth_available(scm_cmd_id)) {
pr_info("qcom_scm_sec_auth_available is not supported\n");
} else {
sec_kobj = kobject_create_and_add("sec_upgrade", NULL);
if (!sec_kobj) {
pr_info("Failed to register sec_upgrade sysfs\n");
return -ENOMEM;
}
err = sysfs_create_file(sec_kobj, &sec_attr.attr);
if (err) {
pr_info("Failed to register sec_auth sysfs\n");
kobject_put(sec_kobj);
sec_kobj = NULL;
}
}
}
err = of_property_read_u32(np, "fuse-blow-size-required",
&fuse_blow_size_required);
/* sysfs entry for fusing QFPROM */
err = device_create_file(&device_qfprom, &sec_dat_attr);
if (err) {
pr_err("%s: device_create_file(%s)=%d\n",
__func__, sec_dat_attr.attr.name, err);
}
return qfprom_create_files(ARRAY_SIZE(qfprom_attrs), sw_bitmap);
}
static const struct of_device_id qcom_qfprom_dt_match[] = {
{ .compatible = "qcom,qfprom-sec",},
{}
};
static struct platform_driver qcom_qfprom_driver = {
.driver = {
.name = "qcom_qfprom",
.of_match_table = qcom_qfprom_dt_match,
},
.probe = qfprom_probe,
};
module_platform_driver(qcom_qfprom_driver);