lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1485827915-9620-4-git-send-email-nobuhiro.iwamatsu.kw@hitachi.com>
Date:   Tue, 31 Jan 2017 10:58:34 +0900
From:   Nobuhiro Iwamatsu <nobuhiro.iwamatsu.kw@...achi.com>
To:     Anton Vorontsov <anton@...msg.org>,
        Colin Cross <ccross@...roid.com>,
        Kees Cook <keescook@...omium.org>,
        Tony Luck <tony.luck@...el.com>
Cc:     linux-kernel@...r.kernel.org,
        Nobuhiro Iwamatsu <nobuhiro.iwamatsu.kw@...achi.com>,
        Hiraku Toyooka <hiraku.toyooka.gu@...achi.com>,
        Jonathan Corbet <corbet@....net>,
        Mark Salyzyn <salyzyn@...roid.com>,
        Seiji Aguchi <seiji.aguchi.tr@...achi.com>
Subject: [PATCH v4 2nd 3/4] ramoops: support multiple pmsg instances

This enables ramoops to deal with multiple pmsg instances.
A User can configure the size of each buffers by its module parameter
as follows.

  pmsg_size=0x1000,0x2000,...

Then, the ramoops allocates multiple buffers and tells the number
of buffers to pstore to create multiple /dev/pmsg[ID].

Signed-off-by: Hiraku Toyooka <hiraku.toyooka.gu@...achi.com>
Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.kw@...achi.com>
Cc: Jonathan Corbet <corbet@....net>
Cc: Anton Vorontsov <anton@...msg.org>
Cc: Colin Cross <ccross@...roid.com>
Cc: Kees Cook <keescook@...omium.org>
Cc: Mark Salyzyn <salyzyn@...roid.com>
Cc: Seiji Aguchi <seiji.aguchi.tr@...achi.com>
Cc: Tony Luck <tony.luck@...el.com>

V4:
  - Rebase.

V3:
  - Rebase.
  - Update documentiation of DT binding.
  - Update parsing function of ramoops_pmsg_size, add NULL-terminated.
  - Update module parameters for pmsg_size list.
---
 Documentation/admin-guide/ramoops.rst              |  22 ++
 .../bindings/reserved-memory/ramoops.txt           |   6 +-
 fs/pstore/ram.c                                    | 223 ++++++++++++++++++---
 include/linux/pstore_ram.h                         |   8 +-
 4 files changed, 231 insertions(+), 28 deletions(-)

diff --git a/Documentation/admin-guide/ramoops.rst b/Documentation/admin-guide/ramoops.rst
index 4efd7ce77565..611ee1d7d9ba 100644
--- a/Documentation/admin-guide/ramoops.rst
+++ b/Documentation/admin-guide/ramoops.rst
@@ -154,3 +154,25 @@ file. Here is an example of usage::
  0 ffffffff811d9c54  ffffffff8101a7a0  __const_udelay <- native_machine_emergency_restart+0x110/0x1e0
  0 ffffffff811d9c34  ffffffff811d9c80  __delay <- __const_udelay+0x30/0x40
  0 ffffffff811d9d14  ffffffff811d9c3f  delay_tsc <- __delay+0xf/0x20
+
+6. Pmsg support
+
+Ramoops supports pmsg - logging userspace mesages in persistent store. By
+default, one pmsg area becomes available in ramoops. You can write data into
+/dev/pmsg0, e.g.:
+
+ # echo foo > /dev/pmsg0
+
+After reboot, the stored data can be read from pmsg-ramoops-0 on the pstore
+filesystem.
+
+You can specify size of the pmsg area by additional kernel command line, e.g.:
+
+ "ramoops.pmsg_size=0x1000"
+
+You can also use multiple pmsg areas, e.g.:
+
+ "ramoops.pmsg_size=0x1000,0x2000,..."
+
+Then, pmsg0, pmsg1, ... will appear on /dev. This is useful to control
+individual content aging or priority.
diff --git a/Documentation/devicetree/bindings/reserved-memory/ramoops.txt b/Documentation/devicetree/bindings/reserved-memory/ramoops.txt
index 0eba562fe5c6..e270221b98df 100644
--- a/Documentation/devicetree/bindings/reserved-memory/ramoops.txt
+++ b/Documentation/devicetree/bindings/reserved-memory/ramoops.txt
@@ -39,7 +39,11 @@ Optional properties:
 - ftrace-size: size in bytes of log buffer reserved for function tracing and
   profiling (defaults to 0: disabled)
 
-- pmsg-size: size in bytes of log buffer reserved for userspace messages
+- pmsg-size: array of value that are used to size in bytes of log buffer
+  reserved for userspace messages.
+
+	pmsg-size = <0x10000 0x10000>;
+
   (defaults to 0: disabled)
 
 - unbuffered: if present, use unbuffered mappings to map the reserved region
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 72072e8123e3..50bc78aff6b8 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -53,8 +53,8 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
 module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
 MODULE_PARM_DESC(ftrace_size, "size of ftrace log");
 
-static ulong ramoops_pmsg_size = MIN_MEM_SIZE;
-module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400);
+static char *ramoops_pmsg_size_str;
+module_param_named(pmsg_size, ramoops_pmsg_size_str, charp, 0400);
 MODULE_PARM_DESC(pmsg_size, "size of user space message log");
 
 static unsigned long long mem_address;
@@ -88,14 +88,14 @@ struct ramoops_context {
 	struct persistent_ram_zone **dprzs;	/* Oops dump zones */
 	struct persistent_ram_zone *cprz;	/* Console zone */
 	struct persistent_ram_zone **fprzs;	/* Ftrace zones */
-	struct persistent_ram_zone *mprz;	/* PMSG zone */
+	struct persistent_ram_zone **mprzs;	/* PMSG zone */
 	phys_addr_t phys_addr;
 	unsigned long size;
 	unsigned int memtype;
 	size_t record_size;
 	size_t console_size;
 	size_t ftrace_size;
-	size_t pmsg_size;
+	size_t *pmsg_size;
 	int dump_oops;
 	u32 flags;
 	struct persistent_ram_ecc_info ecc_info;
@@ -275,9 +275,11 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 		prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
 					   1, id, type, PSTORE_TYPE_CONSOLE, 0);
 
-	if (!prz_ok(prz))
-		prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
-					   1, id, type, PSTORE_TYPE_PMSG, 0);
+	while (cxt->pmsg_read_cnt < cxt->pstore.num_pmsg && !prz_ok(prz))
+		/* get pmsg prz */
+		prz = ramoops_get_next_prz(cxt->mprzs, &cxt->pmsg_read_cnt,
+					   cxt->pstore.num_pmsg, id, type,
+					   PSTORE_TYPE_PMSG, 0);
 
 	/* ftrace is last since it may want to dynamically allocate memory. */
 	if (!prz_ok(prz)) {
@@ -455,9 +457,9 @@ static int notrace ramoops_pstore_write_buf_user(enum pstore_type_id type,
 	if (type == PSTORE_TYPE_PMSG) {
 		struct ramoops_context *cxt = psi->data;
 
-		if (!cxt->mprz)
+		if (!cxt->mprzs)
 			return -ENOMEM;
-		return persistent_ram_write_user(cxt->mprz, buf, size);
+		return persistent_ram_write_user(cxt->mprzs[part], buf, size);
 	}
 
 	return -EINVAL;
@@ -484,7 +486,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
 		prz = cxt->fprzs[id];
 		break;
 	case PSTORE_TYPE_PMSG:
-		prz = cxt->mprz;
+		if (id >= cxt->pstore.num_pmsg)
+			return -EINVAL;
+		prz = cxt->mprzs[id];
 		break;
 	default:
 		return -EINVAL;
@@ -634,6 +638,40 @@ static int ramoops_init_przs(const char *name,
 	return err;
 }
 
+/* int function for pmsg przs */
+static int ramoops_init_mprzs(const char *name,
+			      struct device *dev, struct ramoops_context *cxt,
+			      phys_addr_t *paddr, unsigned long total)
+{
+	int err = -ENOMEM;
+	int i;
+
+	if (!total)
+		return 0;
+
+	if (*paddr + total - cxt->phys_addr > cxt->size) {
+		dev_err(dev, "no room for pmsg\n");
+		return -ENOMEM;
+	}
+
+	cxt->mprzs = kcalloc(cxt->pstore.num_pmsg, sizeof(*cxt->mprzs),
+			     GFP_KERNEL);
+	if (!cxt->mprzs)
+		return -ENOMEM;
+
+	for (i = 0; i < cxt->pstore.num_pmsg; i++) {
+		err = __ramoops_init_prz(name, dev, cxt, &cxt->mprzs[i], paddr,
+					 cxt->pmsg_size[i], 0, 0, true);
+		if (err)
+			goto fail_mprz;
+	}
+
+	return 0;
+fail_mprz:
+	ramoops_free_przs(cxt->mprzs);
+	return err;
+}
+
 static int ramoops_init_prz(const char *name,
 			    struct device *dev, struct ramoops_context *cxt,
 			    struct persistent_ram_zone **prz,
@@ -670,7 +708,8 @@ static int ramoops_parse_dt(struct platform_device *pdev,
 	struct device_node *of_node = pdev->dev.of_node;
 	struct resource *res;
 	u32 value;
-	int ret;
+	int ret, i;
+	unsigned int num_pmsg;
 
 	dev_dbg(&pdev->dev, "using Device Tree\n");
 
@@ -696,12 +735,71 @@ static int ramoops_parse_dt(struct platform_device *pdev,
 	parse_size("record-size", pdata->record_size);
 	parse_size("console-size", pdata->console_size);
 	parse_size("ftrace-size", pdata->ftrace_size);
-	parse_size("pmsg-size", pdata->pmsg_size);
 	parse_size("ecc-size", pdata->ecc_info.ecc_size);
 	parse_size("flags", pdata->flags);
 
 #undef parse_size
 
+	/* Parse pmesg size */
+	if (!of_find_property(of_node, "pmsg-size", &num_pmsg))
+		return 0; /* no data */
+
+	num_pmsg = num_pmsg / sizeof(u32);
+
+	pdata->pmsg_size =
+		devm_kzalloc(&pdev->dev, sizeof(size_t) * (num_pmsg + 1),
+			     GFP_KERNEL);
+	if (!pdata->pmsg_size)
+		return -ENOMEM;
+
+	for (i = 0; i < num_pmsg; i++) {
+		ret = of_property_read_u32_index(of_node, "pmsg-size",
+						 i, &value);
+		if (ret) {
+			dev_warn(&pdev->dev,
+				"%s: failed to read pmsg-size[%d]: %d\n",
+				 __func__, i, ret);
+			return -EINVAL;
+		}
+
+		/* CHECK INT_MAX */
+		if (value > INT_MAX) {
+			dev_err(&pdev->dev, "value %x > INT_MAX\n", value);
+			return -EOVERFLOW;
+		}
+		pdata->pmsg_size[i] = value;
+	}
+
+	return 0;
+}
+
+#define ADDR_STRING_SIZE	10 /* "0x00000000" */
+static int update_pmsg_size_mod_param(size_t *pmsg_size, int num)
+{
+	int i, len;
+
+	/* string size + commas count + NULL-terminated */
+	len = ADDR_STRING_SIZE * num + (num - 1) + 1;
+
+	/* using commandline or already allocation buffer.*/
+	if (ramoops_pmsg_size_str)
+		goto out;
+
+	ramoops_pmsg_size_str = kzalloc(len, GFP_KERNEL);
+	if (!ramoops_pmsg_size_str)
+		return -ENOMEM;
+
+	for (i = 0 ; i < num; i++) {
+		char str[ADDR_STRING_SIZE + 1];
+
+		memset(str, 0, sizeof(str));
+		if (i == num - 1)
+			snprintf(str, sizeof(str), "0x%x", pmsg_size[i]);
+		else
+			snprintf(str, sizeof(str), "0x%x,", pmsg_size[i]);
+		strcat(ramoops_pmsg_size_str, str);
+	}
+out:
 	return 0;
 }
 
@@ -713,6 +811,8 @@ static int ramoops_probe(struct platform_device *pdev)
 	size_t dump_mem_sz;
 	phys_addr_t paddr;
 	int err = -EINVAL;
+	unsigned long pmsg_size_total = 0;
+	unsigned int num_pmsg = 0;
 
 	if (dev_of_node(dev) && !pdata) {
 		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
@@ -755,8 +855,20 @@ static int ramoops_probe(struct platform_device *pdev)
 		pdata->console_size = rounddown_pow_of_two(pdata->console_size);
 	if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
 		pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
-	if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size))
-		pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size);
+
+	if (pdata->pmsg_size) {
+		for (;; num_pmsg++) {
+			unsigned long size = pdata->pmsg_size[num_pmsg];
+
+			if (!size)
+				break;
+
+			if (!is_power_of_2(size))
+				pdata->pmsg_size[num_pmsg]
+					= rounddown_pow_of_two(size);
+			pmsg_size_total += size;
+		}
+	}
 
 	cxt->size = pdata->mem_size;
 	cxt->phys_addr = pdata->mem_address;
@@ -764,20 +876,29 @@ static int ramoops_probe(struct platform_device *pdev)
 	cxt->record_size = pdata->record_size;
 	cxt->console_size = pdata->console_size;
 	cxt->ftrace_size = pdata->ftrace_size;
-	cxt->pmsg_size = pdata->pmsg_size;
 	cxt->dump_oops = pdata->dump_oops;
 	cxt->flags = pdata->flags;
 	cxt->ecc_info = pdata->ecc_info;
+	cxt->pstore.num_pmsg = num_pmsg;
 
-	paddr = cxt->phys_addr;
+	if (num_pmsg) {
+		cxt->pmsg_size = kcalloc(num_pmsg, sizeof(size_t), GFP_KERNEL);
+		if (!cxt->pmsg_size) {
+			err = -ENOMEM;
+			goto fail_out;
+		}
+		memcpy(cxt->pmsg_size, pdata->pmsg_size,
+		       sizeof(size_t) * num_pmsg);
+	}
 
+	paddr = cxt->phys_addr;
 	dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
-			- cxt->pmsg_size;
+			- pmsg_size_total;
 	err = ramoops_init_przs("dump", dev, cxt, &cxt->dprzs, &paddr,
 				dump_mem_sz, cxt->record_size,
 				&cxt->max_dump_cnt, 0, 0);
 	if (err)
-		goto fail_out;
+		goto fail_init_dprzs;
 
 	err = ramoops_init_prz("console", dev, cxt, &cxt->cprz, &paddr,
 			       cxt->console_size, 0);
@@ -795,10 +916,9 @@ static int ramoops_probe(struct platform_device *pdev)
 	if (err)
 		goto fail_init_fprz;
 
-	err = ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr,
-				cxt->pmsg_size, 0);
+	err = ramoops_init_mprzs("pmsg", dev, cxt, &paddr, pmsg_size_total);
 	if (err)
-		goto fail_init_mprz;
+		goto fail_init_mprzs;
 
 	cxt->pstore.data = cxt;
 	/*
@@ -841,8 +961,8 @@ static int ramoops_probe(struct platform_device *pdev)
 	record_size = pdata->record_size;
 	dump_oops = pdata->dump_oops;
 	ramoops_console_size = pdata->console_size;
-	ramoops_pmsg_size = pdata->pmsg_size;
 	ramoops_ftrace_size = pdata->ftrace_size;
+	update_pmsg_size_mod_param(cxt->pmsg_size, cxt->pstore.num_pmsg);
 
 	pr_info("attached 0x%lx@...llx, ecc: %d/%d\n",
 		cxt->size, (unsigned long long)cxt->phys_addr,
@@ -854,8 +974,8 @@ static int ramoops_probe(struct platform_device *pdev)
 	kfree(cxt->pstore.buf);
 fail_clear:
 	cxt->pstore.bufsize = 0;
-	persistent_ram_free(cxt->mprz);
-fail_init_mprz:
+	ramoops_free_przs(cxt->mprzs);
+fail_init_mprzs:
 	cxt->max_ftrace_cnt = 0;
 	ramoops_free_przs(cxt->fprzs);
 fail_init_fprz:
@@ -863,6 +983,8 @@ static int ramoops_probe(struct platform_device *pdev)
 fail_init_cprz:
 	cxt->max_dump_cnt = 0;
 	ramoops_free_przs(cxt->dprzs);
+fail_init_dprzs:
+	kfree(cxt->pmsg_size);
 fail_out:
 	return err;
 }
@@ -875,8 +997,10 @@ static int ramoops_remove(struct platform_device *pdev)
 
 	kfree(cxt->pstore.buf);
 	cxt->pstore.bufsize = 0;
+	kfree(cxt->pmsg_size);
 
-	persistent_ram_free(cxt->mprz);
+	ramoops_free_przs(cxt->mprzs);
+	cxt->pstore.num_pmsg = 0;
 	persistent_ram_free(cxt->cprz);
 
 	/* Free ftrace PRZs */
@@ -903,6 +1027,43 @@ static struct platform_driver ramoops_driver = {
 	},
 };
 
+static unsigned long *parse_size_str(char *size_str)
+{
+	int i, ret;
+	unsigned long *size_array, count = 1;
+
+	if (size_str) {
+		char *s = size_str;
+
+		/* Necessary array size is the number of commas + 1 */
+		for (; (s = strchr(s, ',')); s++)
+			count++;
+	}
+
+	/* Add NULL-terminated */
+	count++;
+
+	size_array = kcalloc(count, sizeof(unsigned long), GFP_KERNEL);
+	if (!size_array)
+		goto out;
+
+	if (size_str) {
+		for (i = 0; i < count; i++) {
+			ret = get_option(&size_str, (int *)&size_array[i]);
+			if (ret == 1)
+				break;
+			else if (ret != 2) {
+				size_array[i] = 0;
+				break;
+			}
+		}
+	} else
+		size_array[0] = MIN_MEM_SIZE;
+
+out:
+	return size_array;
+}
+
 static void ramoops_register_dummy(void)
 {
 	if (!mem_size)
@@ -922,7 +1083,10 @@ static void ramoops_register_dummy(void)
 	dummy_data->record_size = record_size;
 	dummy_data->console_size = ramoops_console_size;
 	dummy_data->ftrace_size = ramoops_ftrace_size;
-	dummy_data->pmsg_size = ramoops_pmsg_size;
+	dummy_data->pmsg_size = parse_size_str(ramoops_pmsg_size_str);
+
+	if (!dummy_data->pmsg_size)
+		goto fail_pmsg_size;
 	dummy_data->dump_oops = dump_oops;
 	dummy_data->flags = RAMOOPS_FLAG_FTRACE_PER_CPU;
 
@@ -937,7 +1101,13 @@ static void ramoops_register_dummy(void)
 	if (IS_ERR(dummy)) {
 		pr_info("could not create platform device: %ld\n",
 			PTR_ERR(dummy));
+		goto fail_pdev;
 	}
+	return;
+fail_pdev:
+	kfree(dummy_data->pmsg_size);
+fail_pmsg_size:
+	kfree(dummy_data);
 }
 
 static int __init ramoops_init(void)
@@ -951,6 +1121,7 @@ static void __exit ramoops_exit(void)
 {
 	platform_driver_unregister(&ramoops_driver);
 	platform_device_unregister(dummy);
+	kfree(dummy_data->pmsg_size);
 	kfree(dummy_data);
 }
 module_exit(ramoops_exit);
diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h
index 9395f06e8372..030fa45af2bf 100644
--- a/include/linux/pstore_ram.h
+++ b/include/linux/pstore_ram.h
@@ -84,6 +84,12 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
  * Ramoops platform data
  * @mem_size	memory size for ramoops
  * @mem_address	physical memory address to contain ramoops
+ * @pmsg_size	array containing size of each pmsg area. 0 value in the array
+ *		indicates the end of the content. For example, if the following
+ *		array is given,
+ *		    .pmsg_size = { 0x1000, 0x2000, 0x0, 0x3000, };
+ *		then, pmsg areas are allocated for the first two size values
+ *		and '0x3000' is just ignored.
  */
 
 #define RAMOOPS_FLAG_FTRACE_PER_CPU	BIT(0)
@@ -95,7 +101,7 @@ struct ramoops_platform_data {
 	unsigned long	record_size;
 	unsigned long	console_size;
 	unsigned long	ftrace_size;
-	unsigned long	pmsg_size;
+	unsigned long	*pmsg_size;
 	int		dump_oops;
 	u32		flags;
 	struct persistent_ram_ecc_info ecc_info;
-- 
2.11.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ