f461a1d80c
Error handling code following a kmalloc should free the allocated data. Note that at the point of the change, node has not yet been stored in d, so it is not affected by the existing cleanup code. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // <smpl> @r exists@ local idexpression x; statement S; expression E; identifier f,l; position p1,p2; expression *ptr != NULL; @@ ( if ((x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...)) == NULL) S | x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S ) <... when != x when != if (...) { <+...x...+> } x->f = E ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // </smpl> Signed-off-by: Julia Lawall <julia@diku.dk> Signed-off-by: Ingo Molnar <mingo@elte.hu>
227 lines
4.1 KiB
C
227 lines
4.1 KiB
C
/*
|
|
* Architecture specific debugfs files
|
|
*
|
|
* Copyright (C) 2007, Intel Corp.
|
|
* Huang Ying <ying.huang@intel.com>
|
|
*
|
|
* This file is released under the GPLv2.
|
|
*/
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
|
|
#include <asm/setup.h>
|
|
|
|
struct dentry *arch_debugfs_dir;
|
|
EXPORT_SYMBOL(arch_debugfs_dir);
|
|
|
|
#ifdef CONFIG_DEBUG_BOOT_PARAMS
|
|
struct setup_data_node {
|
|
u64 paddr;
|
|
u32 type;
|
|
u32 len;
|
|
};
|
|
|
|
static ssize_t
|
|
setup_data_read(struct file *file, char __user *user_buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
struct setup_data_node *node = file->private_data;
|
|
unsigned long remain;
|
|
loff_t pos = *ppos;
|
|
struct page *pg;
|
|
void *p;
|
|
u64 pa;
|
|
|
|
if (pos < 0)
|
|
return -EINVAL;
|
|
if (pos >= node->len)
|
|
return 0;
|
|
|
|
if (count > node->len - pos)
|
|
count = node->len - pos;
|
|
pa = node->paddr + sizeof(struct setup_data) + pos;
|
|
pg = pfn_to_page((pa + count - 1) >> PAGE_SHIFT);
|
|
if (PageHighMem(pg)) {
|
|
p = ioremap_cache(pa, count);
|
|
if (!p)
|
|
return -ENXIO;
|
|
} else {
|
|
p = __va(pa);
|
|
}
|
|
|
|
remain = copy_to_user(user_buf, p, count);
|
|
|
|
if (PageHighMem(pg))
|
|
iounmap(p);
|
|
|
|
if (remain)
|
|
return -EFAULT;
|
|
|
|
*ppos = pos + count;
|
|
|
|
return count;
|
|
}
|
|
|
|
static int setup_data_open(struct inode *inode, struct file *file)
|
|
{
|
|
file->private_data = inode->i_private;
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations fops_setup_data = {
|
|
.read = setup_data_read,
|
|
.open = setup_data_open,
|
|
};
|
|
|
|
static int __init
|
|
create_setup_data_node(struct dentry *parent, int no,
|
|
struct setup_data_node *node)
|
|
{
|
|
struct dentry *d, *type, *data;
|
|
char buf[16];
|
|
int error;
|
|
|
|
sprintf(buf, "%d", no);
|
|
d = debugfs_create_dir(buf, parent);
|
|
if (!d) {
|
|
error = -ENOMEM;
|
|
goto err_return;
|
|
}
|
|
type = debugfs_create_x32("type", S_IRUGO, d, &node->type);
|
|
if (!type) {
|
|
error = -ENOMEM;
|
|
goto err_dir;
|
|
}
|
|
data = debugfs_create_file("data", S_IRUGO, d, node, &fops_setup_data);
|
|
if (!data) {
|
|
error = -ENOMEM;
|
|
goto err_type;
|
|
}
|
|
return 0;
|
|
|
|
err_type:
|
|
debugfs_remove(type);
|
|
err_dir:
|
|
debugfs_remove(d);
|
|
err_return:
|
|
return error;
|
|
}
|
|
|
|
static int __init create_setup_data_nodes(struct dentry *parent)
|
|
{
|
|
struct setup_data_node *node;
|
|
struct setup_data *data;
|
|
int error, no = 0;
|
|
struct dentry *d;
|
|
struct page *pg;
|
|
u64 pa_data;
|
|
|
|
d = debugfs_create_dir("setup_data", parent);
|
|
if (!d) {
|
|
error = -ENOMEM;
|
|
goto err_return;
|
|
}
|
|
|
|
pa_data = boot_params.hdr.setup_data;
|
|
|
|
while (pa_data) {
|
|
node = kmalloc(sizeof(*node), GFP_KERNEL);
|
|
if (!node) {
|
|
error = -ENOMEM;
|
|
goto err_dir;
|
|
}
|
|
pg = pfn_to_page((pa_data+sizeof(*data)-1) >> PAGE_SHIFT);
|
|
if (PageHighMem(pg)) {
|
|
data = ioremap_cache(pa_data, sizeof(*data));
|
|
if (!data) {
|
|
kfree(node);
|
|
error = -ENXIO;
|
|
goto err_dir;
|
|
}
|
|
} else {
|
|
data = __va(pa_data);
|
|
}
|
|
|
|
node->paddr = pa_data;
|
|
node->type = data->type;
|
|
node->len = data->len;
|
|
error = create_setup_data_node(d, no, node);
|
|
pa_data = data->next;
|
|
|
|
if (PageHighMem(pg))
|
|
iounmap(data);
|
|
if (error)
|
|
goto err_dir;
|
|
no++;
|
|
}
|
|
return 0;
|
|
|
|
err_dir:
|
|
debugfs_remove(d);
|
|
err_return:
|
|
return error;
|
|
}
|
|
|
|
static struct debugfs_blob_wrapper boot_params_blob = {
|
|
.data = &boot_params,
|
|
.size = sizeof(boot_params),
|
|
};
|
|
|
|
static int __init boot_params_kdebugfs_init(void)
|
|
{
|
|
struct dentry *dbp, *version, *data;
|
|
int error;
|
|
|
|
dbp = debugfs_create_dir("boot_params", NULL);
|
|
if (!dbp) {
|
|
error = -ENOMEM;
|
|
goto err_return;
|
|
}
|
|
version = debugfs_create_x16("version", S_IRUGO, dbp,
|
|
&boot_params.hdr.version);
|
|
if (!version) {
|
|
error = -ENOMEM;
|
|
goto err_dir;
|
|
}
|
|
data = debugfs_create_blob("data", S_IRUGO, dbp,
|
|
&boot_params_blob);
|
|
if (!data) {
|
|
error = -ENOMEM;
|
|
goto err_version;
|
|
}
|
|
error = create_setup_data_nodes(dbp);
|
|
if (error)
|
|
goto err_data;
|
|
return 0;
|
|
|
|
err_data:
|
|
debugfs_remove(data);
|
|
err_version:
|
|
debugfs_remove(version);
|
|
err_dir:
|
|
debugfs_remove(dbp);
|
|
err_return:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
static int __init arch_kdebugfs_init(void)
|
|
{
|
|
int error = 0;
|
|
|
|
arch_debugfs_dir = debugfs_create_dir("x86", NULL);
|
|
if (!arch_debugfs_dir)
|
|
return -ENOMEM;
|
|
|
|
#ifdef CONFIG_DEBUG_BOOT_PARAMS
|
|
error = boot_params_kdebugfs_init();
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
arch_initcall(arch_kdebugfs_init);
|