/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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");