213 lines
4.7 KiB
C
Executable File
213 lines
4.7 KiB
C
Executable File
/*
|
|
* Sysfs for OASES framework
|
|
*
|
|
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
|
|
*
|
|
* You should have received a copy of license along with this program;
|
|
* if not, ask for it from Baidu, Inc.
|
|
*
|
|
*/
|
|
#include <linux/types.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/kmsg_dump.h>
|
|
#include <linux/module.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
#include "pmem.h"
|
|
#include "util.h"
|
|
|
|
int oases_boot_reason = -1;
|
|
|
|
struct oases_pmem_data {
|
|
phys_addr_t phys_addr;
|
|
unsigned phys_size;
|
|
void *virt_addr;
|
|
};
|
|
|
|
static struct oases_pmem *g_oases_pmem = NULL;
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
|
|
static void oases_dump(struct kmsg_dumper *dump, enum kmsg_dump_reason reason,
|
|
const char *s1, unsigned long l1, const char *s2, unsigned long l2)
|
|
#else
|
|
static void oases_dump(struct kmsg_dumper *dump,
|
|
enum kmsg_dump_reason reason)
|
|
#endif
|
|
{
|
|
if (reason != KMSG_DUMP_PANIC)
|
|
return;
|
|
if (g_oases_pmem) {
|
|
struct oases_pmem tmp;
|
|
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memcpy(tmp.magic, OASES_PMEM_MAGIC, 16);
|
|
tmp.boot |= OASES_KDUMP_PANIC;
|
|
memcpy_toio(g_oases_pmem, &tmp, sizeof(tmp));
|
|
}
|
|
}
|
|
|
|
static struct kmsg_dumper oases_kmsg_dump = {
|
|
.dump = oases_dump,
|
|
};
|
|
|
|
static int pmem_load_dt(struct device *dev, struct device_node *of)
|
|
{
|
|
int rc;
|
|
struct oases_pmem_data *pdata;
|
|
u32 val;
|
|
|
|
if (!of) {
|
|
oases_debug("OASES DT not configured\n");
|
|
return -EINVAL;
|
|
}
|
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (pdata == NULL) {
|
|
oases_debug("devm_kzalloc failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
rc = of_property_read_u32(of, "oases,pmem_phys", &val);
|
|
if (rc) {
|
|
oases_debug("oases,pmem_phys not configured\n");
|
|
goto fail_of;
|
|
}
|
|
pdata->phys_addr = val;
|
|
rc = of_property_read_u32(of, "oases,pmem_size", &val);
|
|
if (rc) {
|
|
oases_debug("oases,pmem_size not configured\n");
|
|
goto fail_of;
|
|
}
|
|
pdata->phys_size = val;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
|
|
rc = dev_set_drvdata(dev, pdata);
|
|
if (rc) {
|
|
oases_debug("dev_set_drvdata failed\n");
|
|
goto fail_drvdata;
|
|
}
|
|
#else
|
|
dev_set_drvdata(dev, pdata);
|
|
#endif
|
|
return 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
|
|
fail_drvdata:
|
|
#endif
|
|
fail_of:
|
|
if (pdata)
|
|
kfree(pdata);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int pmem_config(struct oases_pmem_data *pdata)
|
|
{
|
|
struct oases_pmem tmp, *p;
|
|
|
|
pdata->virt_addr = ioremap(pdata->phys_addr, pdata->phys_size);
|
|
if (!pdata->virt_addr) {
|
|
oases_debug("pmem ioremap failed\n");
|
|
return -EINVAL;
|
|
}
|
|
p = pdata->virt_addr;
|
|
memcpy_fromio(&tmp, p, sizeof(*p));
|
|
if (!memcmp(tmp.magic, OASES_PMEM_MAGIC, 16)) {
|
|
oases_debug("pmem magic match\n");
|
|
oases_boot_reason = tmp.boot;
|
|
} else {
|
|
/* treat cold boot as a normal *hot* reboot */
|
|
oases_boot_reason = 0;
|
|
}
|
|
/* reset pmem */
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memcpy(tmp.magic, OASES_PMEM_MAGIC, 16);
|
|
memcpy_toio(p, &tmp, sizeof(*p));
|
|
return 0;
|
|
}
|
|
|
|
static int oases_pmem_probe(struct platform_device *pdev)
|
|
{
|
|
int rc;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *of = pdev->dev.of_node;
|
|
struct oases_pmem_data *pdata;
|
|
|
|
rc = pmem_load_dt(dev, of);
|
|
if (rc) {
|
|
oases_error("pmem_load_dt() failed\n");
|
|
goto fail_load_dt;
|
|
}
|
|
pdata = dev_get_drvdata(dev);
|
|
rc = pmem_config(pdata);
|
|
if (rc) {
|
|
oases_error("pmem_config() failed\n");
|
|
goto fail_config;
|
|
}
|
|
/* for oases_dump() */
|
|
g_oases_pmem = pdata->virt_addr;
|
|
|
|
rc = kmsg_dump_register(&oases_kmsg_dump);
|
|
if (rc) {
|
|
oases_error("kmsg_dump_register failed\n");
|
|
goto fail_reg;
|
|
}
|
|
oases_info("oases_pmem_probe success\n");
|
|
return 0;
|
|
fail_reg:
|
|
iounmap(pdata->virt_addr);
|
|
fail_config:
|
|
dev_set_drvdata(dev, NULL);
|
|
kfree(pdata);
|
|
fail_load_dt:
|
|
return rc;
|
|
}
|
|
|
|
static int oases_pmem_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct oases_pmem_data *pdata;
|
|
|
|
oases_info("oases_pmem_remove\n");
|
|
g_oases_pmem = NULL;
|
|
kmsg_dump_unregister(&oases_kmsg_dump);
|
|
pdata = dev_get_drvdata(dev);
|
|
dev_set_drvdata(dev, NULL);
|
|
iounmap(pdata->virt_addr);
|
|
kfree(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id oases_pmem_dt[] = {
|
|
{ .compatible = "oases_pmem" },
|
|
{ },
|
|
};
|
|
|
|
static struct platform_driver oases_pmem_driver = {
|
|
.probe = oases_pmem_probe,
|
|
.remove = oases_pmem_remove,
|
|
.driver = {
|
|
.name = "oases_pmem",
|
|
.of_match_table = oases_pmem_dt,
|
|
},
|
|
};
|
|
|
|
static int __init oases_pmem_init(void)
|
|
{
|
|
return platform_driver_register(&oases_pmem_driver);
|
|
}
|
|
|
|
static void __exit oases_pmem_exit(void)
|
|
{
|
|
platform_driver_unregister(&oases_pmem_driver);
|
|
}
|
|
|
|
module_init(oases_pmem_init);
|
|
module_exit(oases_pmem_exit);
|
|
|
|
MODULE_AUTHOR("Baidu, Inc.");
|
|
MODULE_DESCRIPTION("OASES - Open Adaptive Security Extensions");
|