/** * Copyright (c) ???? Jochen Schäuble * Copyright (c) 2003-2004 Joern Engel * * Usage: * * one commend line parameter per device, each in the form: * phram=,, * may be up to 63 characters. * and can be octal, decimal or hexadecimal. If followed * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or * gigabytes. * * Example: * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include struct phram_mtd_list { struct mtd_info mtd; struct list_head list; }; static LIST_HEAD(phram_list); static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) { u_char *start = mtd->priv; memset(start + instr->addr, 0xff, instr->len); /* * This'll catch a few races. Free the thing before returning :) * I don't feel at all ashamed. This kind of thing is possible anyway * with flash, but unlikely. */ instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0; } static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { *virt = mtd->priv + from; *retlen = len; return 0; } static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) { return 0; } static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { u_char *start = mtd->priv; memcpy(buf, start + from, len); *retlen = len; return 0; } static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { u_char *start = mtd->priv; memcpy(start + to, buf, len); *retlen = len; return 0; } static void unregister_devices(void) { struct phram_mtd_list *this, *safe; list_for_each_entry_safe(this, safe, &phram_list, list) { mtd_device_unregister(&this->mtd); iounmap(this->mtd.priv); kfree(this->mtd.name); kfree(this); } } static int register_device(char *name, phys_addr_t start, size_t len) { struct phram_mtd_list *new; int ret = -ENOMEM; new = kzalloc(sizeof(*new), GFP_KERNEL); if (!new) goto out0; ret = -EIO; new->mtd.priv = ioremap(start, len); if (!new->mtd.priv) { pr_err("ioremap failed\n"); goto out1; } new->mtd.name = name; new->mtd.size = len; new->mtd.flags = MTD_CAP_RAM; new->mtd._erase = phram_erase; new->mtd._point = phram_point; new->mtd._unpoint = phram_unpoint; new->mtd._read = phram_read; new->mtd._write = phram_write; new->mtd.owner = THIS_MODULE; new->mtd.type = MTD_RAM; new->mtd.erasesize = PAGE_SIZE; new->mtd.writesize = 1; ret = -EAGAIN; if (mtd_device_register(&new->mtd, NULL, 0)) { pr_err("Failed to register new device\n"); goto out2; } list_add_tail(&new->list, &phram_list); return 0; out2: iounmap(new->mtd.priv); out1: kfree(new); out0: return ret; } static int parse_num64(uint64_t *num64, char *token) { size_t len; int shift = 0; int ret; len = strlen(token); /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ if (len > 2) { if (token[len - 1] == 'i') { switch (token[len - 2]) { case 'G': shift += 10; case 'M': shift += 10; case 'k': shift += 10; token[len - 2] = 0; break; default: return -EINVAL; } } } ret = kstrtou64(token, 0, num64); *num64 <<= shift; return ret; } static int parse_name(char **pname, const char *token) { size_t len; char *name; len = strlen(token) + 1; if (len > 64) return -ENOSPC; name = kstrdup(token, GFP_KERNEL); if (!name) return -ENOMEM; *pname = name; return 0; } static inline void kill_final_newline(char *str) { char *newline = strrchr(str, '\n'); if (newline && !newline[1]) *newline = 0; } #define parse_err(fmt, args...) do { \ pr_err(fmt , ## args); \ return 1; \ } while (0) #ifndef MODULE static int phram_init_called; /* * This shall contain the module parameter if any. It is of the form: * - phram=,
, for module case * - phram.phram=,
, for built-in case * We leave 64 bytes for the device name, 20 for the address and 20 for the * size. * Example: phram.phram=rootfs,0xa0000000,512Mi */ static char phram_paramline[64 + 20 + 20]; #endif static int phram_setup(const char *val) { char buf[64 + 20 + 20], *str = buf; char *token[3]; char *name; uint64_t start; uint64_t len; int i, ret; if (strnlen(val, sizeof(buf)) >= sizeof(buf)) parse_err("parameter too long\n"); strcpy(str, val); kill_final_newline(str); for (i = 0; i < 3; i++) token[i] = strsep(&str, ","); if (str) parse_err("too many arguments\n"); if (!token[2]) parse_err("not enough arguments\n"); ret = parse_name(&name, token[0]); if (ret) return ret; ret = parse_num64(&start, token[1]); if (ret) { kfree(name); parse_err("illegal start address\n"); } ret = parse_num64(&len, token[2]); if (ret) { kfree(name); parse_err("illegal device length\n"); } ret = register_device(name, start, len); if (!ret) pr_info("%s device: %#llx at %#llx\n", name, len, start); else kfree(name); return ret; } static int phram_param_call(const char *val, struct kernel_param *kp) { #ifdef MODULE return phram_setup(val); #else /* * If more parameters are later passed in via * /sys/module/phram/parameters/phram * and init_phram() has already been called, * we can parse the argument now. */ if (phram_init_called) return phram_setup(val); /* * During early boot stage, we only save the parameters * here. We must parse them later: if the param passed * from kernel boot command line, phram_param_call() is * called so early that it is not possible to resolve * the device (even kmalloc() fails). Defer that work to * phram_setup(). */ if (strlen(val) >= sizeof(phram_paramline)) return -ENOSPC; strcpy(phram_paramline, val); return 0; #endif } module_param_call(phram, phram_param_call, NULL, NULL, 000); MODULE_PARM_DESC(phram, "Memory region to map. \"phram=,,\""); static int __init init_phram(void) { int ret = 0; #ifndef MODULE if (phram_paramline[0]) ret = phram_setup(phram_paramline); phram_init_called = 1; #endif return ret; } static void __exit cleanup_phram(void) { unregister_devices(); } module_init(init_phram); module_exit(cleanup_phram); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Joern Engel "); MODULE_DESCRIPTION("MTD driver for physical RAM");