File Index
Files and directories
Sign in
FILE

phram.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/devices/phram.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) ????		Jochen Schäuble <psionic@psionic.de>
  4 * Copyright (c) 2003-2004	Joern Engel <joern@wh.fh-wedel.de>
  5 *
  6 * Usage:
  7 *
  8 * one commend line parameter per device, each in the form:
  9 *   phram=<name>,<start>,<len>[,<erasesize>]
 10 * <name> may be up to 63 characters.
 11 * <start>, <len>, and <erasesize> can be octal, decimal or hexadecimal.  If followed
 12 * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
 13 * gigabytes. <erasesize> is optional and defaults to PAGE_SIZE.
 14 *
 15 * Example:
 16 *	phram=swap,64Mi,128Mi phram=test,900Mi,1Mi,64Ki
 17 */
 18
 19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 20
 21#include <linux/io.h>
 22#include <linux/init.h>
 23#include <linux/kernel.h>
 24#include <linux/list.h>
 25#include <linux/module.h>
 26#include <linux/moduleparam.h>
 27#include <linux/slab.h>
 28#include <linux/mtd/mtd.h>
 29#include <asm/div64.h>
 30#include <linux/platform_device.h>
 31#include <linux/of_address.h>
 32#include <linux/of.h>
 33#include <linux/security.h>
 34
 35struct phram_mtd_list {
 36	struct mtd_info mtd;
 37	struct list_head list;
 38	bool cached;
 39};
 40
 41static LIST_HEAD(phram_list);
 42
 43static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
 44{
 45	u_char *start = mtd->priv;
 46
 47	memset(start + instr->addr, 0xff, instr->len);
 48
 49	return 0;
 50}
 51
 52static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
 53		size_t *retlen, void **virt, resource_size_t *phys)
 54{
 55	*virt = mtd->priv + from;
 56	*retlen = len;
 57	return 0;
 58}
 59
 60static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 61{
 62	return 0;
 63}
 64
 65static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
 66		size_t *retlen, u_char *buf)
 67{
 68	u_char *start = mtd->priv;
 69
 70	memcpy(buf, start + from, len);
 71	*retlen = len;
 72	return 0;
 73}
 74
 75static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
 76		size_t *retlen, const u_char *buf)
 77{
 78	u_char *start = mtd->priv;
 79
 80	memcpy(start + to, buf, len);
 81	*retlen = len;
 82	return 0;
 83}
 84
 85static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)
 86{
 87	void *addr = NULL;
 88
 89	if (phram->cached)
 90		addr = memremap(start, len, MEMREMAP_WB);
 91	else
 92		addr = (void __force *)ioremap(start, len);
 93	if (!addr)
 94		return -EIO;
 95
 96	phram->mtd.priv = addr;
 97
 98	return 0;
 99}
100
101static void phram_unmap(struct phram_mtd_list *phram)
102{
103	void *addr = phram->mtd.priv;
104
105	if (phram->cached) {
106		memunmap(addr);
107		return;
108	}
109
110	iounmap((void __iomem *)addr);
111}
112
113static void unregister_devices(void)
114{
115	struct phram_mtd_list *this, *safe;
116
117	list_for_each_entry_safe(this, safe, &phram_list, list) {
118		mtd_device_unregister(&this->mtd);
119		phram_unmap(this);
120		kfree(this->mtd.name);
121		kfree(this);
122	}
123}
124
125static int register_device(struct platform_device *pdev, const char *name,
126			   phys_addr_t start, size_t len, uint32_t erasesize)
127{
128	struct device_node *np = pdev ? pdev->dev.of_node : NULL;
129	bool cached = np ? !of_property_read_bool(np, "no-map") : false;
130	struct phram_mtd_list *new;
131	int ret = -ENOMEM;
132
133	new = kzalloc(sizeof(*new), GFP_KERNEL);
134	if (!new)
135		goto out0;
136
137	new->cached = cached;
138
139	ret = phram_map(new, start, len);
140	if (ret) {
141		pr_err("ioremap failed\n");
142		goto out1;
143	}
144
145
146	new->mtd.name = name;
147	new->mtd.size = len;
148	new->mtd.flags = MTD_CAP_RAM;
149	new->mtd._erase = phram_erase;
150	new->mtd._point = phram_point;
151	new->mtd._unpoint = phram_unpoint;
152	new->mtd._read = phram_read;
153	new->mtd._write = phram_write;
154	new->mtd.owner = THIS_MODULE;
155	new->mtd.type = MTD_RAM;
156	new->mtd.erasesize = erasesize;
157	new->mtd.writesize = 1;
158
159	mtd_set_of_node(&new->mtd, np);
160
161	ret = -EAGAIN;
162	if (mtd_device_register(&new->mtd, NULL, 0)) {
163		pr_err("Failed to register new device\n");
164		goto out2;
165	}
166
167	if (pdev)
168		platform_set_drvdata(pdev, new);
169	else
170		list_add_tail(&new->list, &phram_list);
171
172	return 0;
173
174out2:
175	phram_unmap(new);
176out1:
177	kfree(new);
178out0:
179	return ret;
180}
181
182static int parse_num64(uint64_t *num64, char *token)
183{
184	size_t len;
185	int shift = 0;
186	int ret;
187
188	len = strlen(token);
189	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
190	if (len > 2) {
191		if (token[len - 1] == 'i') {
192			switch (token[len - 2]) {
193			case 'G':
194				shift += 10;
195				fallthrough;
196			case 'M':
197				shift += 10;
198				fallthrough;
199			case 'k':
200				shift += 10;
201				token[len - 2] = 0;
202				break;
203			default:
204				return -EINVAL;
205			}
206		}
207	}
208
209	ret = kstrtou64(token, 0, num64);
210	*num64 <<= shift;
211
212	return ret;
213}
214
215static int parse_name(char **pname, const char *token)
216{
217	size_t len;
218	char *name;
219
220	len = strlen(token) + 1;
221	if (len > 64)
222		return -ENOSPC;
223
224	name = kstrdup(token, GFP_KERNEL);
225	if (!name)
226		return -ENOMEM;
227
228	*pname = name;
229	return 0;
230}
231
232
233static inline void kill_final_newline(char *str)
234{
235	char *newline = strrchr(str, '\n');
236
237	if (newline && !newline[1])
238		*newline = 0;
239}
240
241
242#define parse_err(fmt, args...) do {	\
243	pr_err(fmt , ## args);	\
244	return 1;		\
245} while (0)
246
247#ifndef MODULE
248static int phram_init_called;
249/*
250 * This shall contain the module parameter if any. It is of the form:
251 * - phram=<device>,<address>,<size>[,<erasesize>] for module case
252 * - phram.phram=<device>,<address>,<size>[,<erasesize>] for built-in case
253 * We leave 64 bytes for the device name, 20 for the address , 20 for the
254 * size and 20 for the erasesize.
255 * Example: phram.phram=rootfs,0xa0000000,512Mi,65536
256 */
257static char phram_paramline[64 + 20 + 20 + 20];
258#endif
259
260static int phram_setup(const char *val)
261{
262	char buf[64 + 20 + 20 + 20], *str = buf;
263	char *token[4];
264	char *name;
265	uint64_t start;
266	uint64_t len;
267	uint64_t erasesize = PAGE_SIZE;
268	uint32_t rem;
269	int i, ret;
270
271	if (strnlen(val, sizeof(buf)) >= sizeof(buf))
272		parse_err("parameter too long\n");
273
274	strcpy(str, val);
275	kill_final_newline(str);
276
277	for (i = 0; i < 4; i++)
278		token[i] = strsep(&str, ",");
279
280	if (str)
281		parse_err("too many arguments\n");
282
283	if (!token[2])
284		parse_err("not enough arguments\n");
285
286	ret = parse_name(&name, token[0]);
287	if (ret)
288		return ret;
289
290	ret = parse_num64(&start, token[1]);
291	if (ret) {
292		parse_err("illegal start address\n");
293		goto error;
294	}
295
296	ret = parse_num64(&len, token[2]);
297	if (ret) {
298		parse_err("illegal device length\n");
299		goto error;
300	}
301
302	if (token[3]) {
303		ret = parse_num64(&erasesize, token[3]);
304		if (ret) {
305			parse_err("illegal erasesize\n");
306			goto error;
307		}
308	}
309
310	if (len == 0 || erasesize == 0 || erasesize > len
311	    || erasesize > UINT_MAX) {
312		parse_err("illegal erasesize or len\n");
313		ret = -EINVAL;
314		goto error;
315	}
316
317	div_u64_rem(len, (uint32_t)erasesize, &rem);
318	if (rem) {
319		parse_err("len is not multiple of erasesize\n");
320		ret = -EINVAL;
321		goto error;
322	}
323
324	ret = register_device(NULL, name, start, len, (uint32_t)erasesize);
325	if (ret)
326		goto error;
327
328	pr_info("%s device: %#llx at %#llx for erasesize %#llx\n", name, len, start, erasesize);
329	return 0;
330
331error:
332	kfree(name);
333	return ret;
334}
335
336static int phram_param_call(const char *val, const struct kernel_param *kp)
337{
338#ifdef MODULE
339	return phram_setup(val);
340#else
341	/*
342	 * If more parameters are later passed in via
343	 * /sys/module/phram/parameters/phram
344	 * and init_phram() has already been called,
345	 * we can parse the argument now.
346	 */
347
348	if (phram_init_called)
349		return phram_setup(val);
350
351	/*
352	 * During early boot stage, we only save the parameters
353	 * here. We must parse them later: if the param passed
354	 * from kernel boot command line, phram_param_call() is
355	 * called so early that it is not possible to resolve
356	 * the device (even kmalloc() fails). Defer that work to
357	 * phram_setup().
358	 */
359
360	if (strlen(val) >= sizeof(phram_paramline))
361		return -ENOSPC;
362	strcpy(phram_paramline, val);
363
364	return 0;
365#endif
366}
367
368module_param_call(phram, phram_param_call, NULL, NULL, 0200);
369MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
370
371#ifdef CONFIG_OF
372static const struct of_device_id phram_of_match[] = {
373	{ .compatible = "phram" },
374	{}
375};
376MODULE_DEVICE_TABLE(of, phram_of_match);
377#endif
378
379static int phram_probe(struct platform_device *pdev)
380{
381	struct resource *res;
382
383	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
384	if (!res)
385		return -ENOMEM;
386
387	/* mtd_set_of_node() reads name from "label" */
388	return register_device(pdev, NULL, res->start, resource_size(res),
389			       PAGE_SIZE);
390}
391
392static void phram_remove(struct platform_device *pdev)
393{
394	struct phram_mtd_list *phram = platform_get_drvdata(pdev);
395
396	mtd_device_unregister(&phram->mtd);
397	phram_unmap(phram);
398	kfree(phram);
399}
400
401static struct platform_driver phram_driver = {
402	.probe		= phram_probe,
403	.remove		= phram_remove,
404	.driver		= {
405		.name		= "phram",
406		.of_match_table	= of_match_ptr(phram_of_match),
407	},
408};
409
410static int __init init_phram(void)
411{
412	int ret;
413
414	ret = security_locked_down(LOCKDOWN_DEV_MEM);
415	if (ret)
416		return ret;
417
418	ret = platform_driver_register(&phram_driver);
419	if (ret)
420		return ret;
421
422#ifndef MODULE
423	if (phram_paramline[0]) {
424		ret = phram_setup(phram_paramline);
425		if (ret)
426			platform_driver_unregister(&phram_driver);
427	}
428	phram_init_called = 1;
429#endif
430
431	return ret;
432}
433
434static void __exit cleanup_phram(void)
435{
436	unregister_devices();
437	platform_driver_unregister(&phram_driver);
438}
439
440module_init(init_phram);
441module_exit(cleanup_phram);
442
443MODULE_LICENSE("GPL");
444MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>");
445MODULE_DESCRIPTION("MTD driver for physical RAM");
~/ Read-only file preview.