ps3: Replace direct file operations by callback

Currently the FLASH database is updated by the kernel using file operations,
meant for userspace only. While this works for us because copy_{from,to}_user()
on powerpc can handle kernel pointers, this is unportable and a bad example.
Replace the file operations by callbacks, registered by the ps3flash driver.

Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Cc: Geoff Levand <geoffrey.levand@am.sony.com>
Acked-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
Geert Uytterhoeven 2009-06-10 04:39:06 +00:00 committed by Benjamin Herrenschmidt
parent 47cb996b05
commit a4e623fbc9
3 changed files with 152 additions and 87 deletions

View file

@ -53,6 +53,13 @@ enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void);
extern u64 ps3_os_area_get_rtc_diff(void); extern u64 ps3_os_area_get_rtc_diff(void);
extern void ps3_os_area_set_rtc_diff(u64 rtc_diff); extern void ps3_os_area_set_rtc_diff(u64 rtc_diff);
struct ps3_os_area_flash_ops {
ssize_t (*read)(void *buf, size_t count, loff_t pos);
ssize_t (*write)(const void *buf, size_t count, loff_t pos);
};
extern void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops);
/* dma routines */ /* dma routines */
enum ps3_dma_page_size { enum ps3_dma_page_size {

View file

@ -226,6 +226,44 @@ static struct property property_av_multi_out = {
.value = &saved_params.av_multi_out, .value = &saved_params.av_multi_out,
}; };
static DEFINE_MUTEX(os_area_flash_mutex);
static const struct ps3_os_area_flash_ops *os_area_flash_ops;
void ps3_os_area_flash_register(const struct ps3_os_area_flash_ops *ops)
{
mutex_lock(&os_area_flash_mutex);
os_area_flash_ops = ops;
mutex_unlock(&os_area_flash_mutex);
}
EXPORT_SYMBOL_GPL(ps3_os_area_flash_register);
static ssize_t os_area_flash_read(void *buf, size_t count, loff_t pos)
{
ssize_t res = -ENODEV;
mutex_lock(&os_area_flash_mutex);
if (os_area_flash_ops)
res = os_area_flash_ops->read(buf, count, pos);
mutex_unlock(&os_area_flash_mutex);
return res;
}
static ssize_t os_area_flash_write(const void *buf, size_t count, loff_t pos)
{
ssize_t res = -ENODEV;
mutex_lock(&os_area_flash_mutex);
if (os_area_flash_ops)
res = os_area_flash_ops->write(buf, count, pos);
mutex_unlock(&os_area_flash_mutex);
return res;
}
/** /**
* os_area_set_property - Add or overwrite a saved_params value to the device tree. * os_area_set_property - Add or overwrite a saved_params value to the device tree.
* *
@ -352,12 +390,12 @@ static int db_verify(const struct os_area_db *db)
if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM, if (memcmp(db->magic_num, OS_AREA_DB_MAGIC_NUM,
sizeof(db->magic_num))) { sizeof(db->magic_num))) {
pr_debug("%s:%d magic_num failed\n", __func__, __LINE__); pr_debug("%s:%d magic_num failed\n", __func__, __LINE__);
return -1; return -EINVAL;
} }
if (db->version != 1) { if (db->version != 1) {
pr_debug("%s:%d version failed\n", __func__, __LINE__); pr_debug("%s:%d version failed\n", __func__, __LINE__);
return -1; return -EINVAL;
} }
return 0; return 0;
@ -578,59 +616,48 @@ static void os_area_db_init(struct os_area_db *db)
* *
*/ */
static void __maybe_unused update_flash_db(void) static int update_flash_db(void)
{ {
int result; const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE;
int file; struct os_area_header *header;
off_t offset;
ssize_t count; ssize_t count;
static const unsigned int buf_len = 8 * OS_AREA_SEGMENT_SIZE; int error;
const struct os_area_header *header; loff_t pos;
struct os_area_db* db; struct os_area_db* db;
/* Read in header and db from flash. */ /* Read in header and db from flash. */
file = sys_open("/dev/ps3flash", O_RDWR, 0);
if (file < 0) {
pr_debug("%s:%d sys_open failed\n", __func__, __LINE__);
goto fail_open;
}
header = kmalloc(buf_len, GFP_KERNEL); header = kmalloc(buf_len, GFP_KERNEL);
if (!header) { if (!header) {
pr_debug("%s:%d kmalloc failed\n", __func__, __LINE__); pr_debug("%s: kmalloc failed\n", __func__);
goto fail_malloc; return -ENOMEM;
} }
offset = sys_lseek(file, 0, SEEK_SET); count = os_area_flash_read(header, buf_len, 0);
if (count < 0) {
if (offset != 0) { pr_debug("%s: os_area_flash_read failed %zd\n", __func__,
pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__); count);
goto fail_header_seek; error = count;
goto fail;
} }
count = sys_read(file, (char __user *)header, buf_len); pos = header->db_area_offset * OS_AREA_SEGMENT_SIZE;
if (count < OS_AREA_SEGMENT_SIZE || verify_header(header) ||
result = count < OS_AREA_SEGMENT_SIZE || verify_header(header) count < pos) {
|| count < header->db_area_offset * OS_AREA_SEGMENT_SIZE; pr_debug("%s: verify_header failed\n", __func__);
if (result) {
pr_debug("%s:%d verify_header failed\n", __func__, __LINE__);
dump_header(header); dump_header(header);
goto fail_header; error = -EINVAL;
goto fail;
} }
/* Now got a good db offset and some maybe good db data. */ /* Now got a good db offset and some maybe good db data. */
db = (void*)header + header->db_area_offset * OS_AREA_SEGMENT_SIZE; db = (void *)header + pos;
result = db_verify(db); error = db_verify(db);
if (error) {
if (result) { pr_notice("%s: Verify of flash database failed, formatting.\n",
printk(KERN_NOTICE "%s:%d: Verify of flash database failed, " __func__);
"formatting.\n", __func__, __LINE__);
dump_db(db); dump_db(db);
os_area_db_init(db); os_area_db_init(db);
} }
@ -639,29 +666,16 @@ static void __maybe_unused update_flash_db(void)
db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff); db_set_64(db, &os_area_db_id_rtc_diff, saved_params.rtc_diff);
offset = sys_lseek(file, header->db_area_offset * OS_AREA_SEGMENT_SIZE, count = os_area_flash_write(db, sizeof(struct os_area_db), pos);
SEEK_SET);
if (offset != header->db_area_offset * OS_AREA_SEGMENT_SIZE) {
pr_debug("%s:%d sys_lseek failed\n", __func__, __LINE__);
goto fail_db_seek;
}
count = sys_write(file, (const char __user *)db,
sizeof(struct os_area_db));
if (count < sizeof(struct os_area_db)) { if (count < sizeof(struct os_area_db)) {
pr_debug("%s:%d sys_write failed\n", __func__, __LINE__); pr_debug("%s: os_area_flash_write failed %zd\n", __func__,
count);
error = count < 0 ? count : -EIO;
} }
fail_db_seek: fail:
fail_header:
fail_header_seek:
kfree(header); kfree(header);
fail_malloc: return error;
sys_close(file);
fail_open:
return;
} }
/** /**
@ -674,11 +688,11 @@ fail_open:
static void os_area_queue_work_handler(struct work_struct *work) static void os_area_queue_work_handler(struct work_struct *work)
{ {
struct device_node *node; struct device_node *node;
int error;
pr_debug(" -> %s:%d\n", __func__, __LINE__); pr_debug(" -> %s:%d\n", __func__, __LINE__);
node = of_find_node_by_path("/"); node = of_find_node_by_path("/");
if (node) { if (node) {
os_area_set_property(node, &property_rtc_diff); os_area_set_property(node, &property_rtc_diff);
of_node_put(node); of_node_put(node);
@ -686,12 +700,10 @@ static void os_area_queue_work_handler(struct work_struct *work)
pr_debug("%s:%d of_find_node_by_path failed\n", pr_debug("%s:%d of_find_node_by_path failed\n",
__func__, __LINE__); __func__, __LINE__);
#if defined(CONFIG_PS3_FLASH) || defined(CONFIG_PS3_FLASH_MODULE) error = update_flash_db();
update_flash_db(); if (error)
#else pr_warning("%s: Could not update FLASH ROM\n", __func__);
printk(KERN_WARNING "%s:%d: No flash rom driver configured.\n",
__func__, __LINE__);
#endif
pr_debug(" <- %s:%d\n", __func__, __LINE__); pr_debug(" <- %s:%d\n", __func__, __LINE__);
} }

View file

@ -104,18 +104,19 @@ out:
return res; return res;
} }
static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf,
loff_t *pos) size_t count, loff_t *pos)
{ {
struct ps3_storage_device *dev = ps3flash_dev; struct ps3_storage_device *dev = ps3flash_dev;
struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
u64 size, start_sector, end_sector, offset; u64 size, start_sector, end_sector, offset;
ssize_t sectors_read; ssize_t sectors_read;
size_t remaining, n; size_t remaining, n;
const void *src;
dev_dbg(&dev->sbd.core, dev_dbg(&dev->sbd.core,
"%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n",
__func__, __LINE__, count, *pos, buf); __func__, __LINE__, count, *pos, userbuf, kernelbuf);
size = dev->regions[dev->region_idx].size*dev->blk_size; size = dev->regions[dev->region_idx].size*dev->blk_size;
if (*pos >= size || !count) if (*pos >= size || !count)
@ -145,19 +146,26 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count,
} }
n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); n = min_t(u64, remaining, sectors_read*dev->blk_size-offset);
src = dev->bounce_buf+offset;
dev_dbg(&dev->sbd.core, dev_dbg(&dev->sbd.core,
"%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n",
__func__, __LINE__, n, dev->bounce_buf+offset, buf); __func__, __LINE__, n, src, userbuf, kernelbuf);
if (copy_to_user(buf, dev->bounce_buf+offset, n)) { if (userbuf) {
if (copy_to_user(userbuf, src, n)) {
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
sectors_read = -EFAULT; sectors_read = -EFAULT;
goto fail; goto fail;
} }
userbuf += n;
}
if (kernelbuf) {
memcpy(kernelbuf, src, n);
kernelbuf += n;
}
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
*pos += n; *pos += n;
buf += n;
remaining -= n; remaining -= n;
start_sector += sectors_read; start_sector += sectors_read;
offset = 0; offset = 0;
@ -169,8 +177,8 @@ fail:
return sectors_read; return sectors_read;
} }
static ssize_t ps3flash_write(struct file *file, const char __user *buf, static ssize_t ps3flash_write(const char __user *userbuf,
size_t count, loff_t *pos) const void *kernelbuf, size_t count, loff_t *pos)
{ {
struct ps3_storage_device *dev = ps3flash_dev; struct ps3_storage_device *dev = ps3flash_dev;
struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
@ -179,10 +187,11 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf,
ssize_t res; ssize_t res;
size_t remaining, n; size_t remaining, n;
unsigned int sec_off; unsigned int sec_off;
void *dst;
dev_dbg(&dev->sbd.core, dev_dbg(&dev->sbd.core,
"%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n",
__func__, __LINE__, count, *pos, buf); __func__, __LINE__, count, *pos, userbuf, kernelbuf);
size = dev->regions[dev->region_idx].size*dev->blk_size; size = dev->regions[dev->region_idx].size*dev->blk_size;
if (*pos >= size || !count) if (*pos >= size || !count)
@ -259,13 +268,21 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf,
} }
n = min_t(u64, remaining, dev->bounce_size-offset); n = min_t(u64, remaining, dev->bounce_size-offset);
dst = dev->bounce_buf+offset;
dev_dbg(&dev->sbd.core, dev_dbg(&dev->sbd.core,
"%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n",
__func__, __LINE__, n, buf, dev->bounce_buf+offset); __func__, __LINE__, n, userbuf, kernelbuf, dst);
if (copy_from_user(dev->bounce_buf+offset, buf, n)) { if (userbuf) {
if (copy_from_user(dst, userbuf, n)) {
res = -EFAULT; res = -EFAULT;
goto fail; goto fail;
} }
userbuf += n;
}
if (kernelbuf) {
memcpy(dst, kernelbuf, n);
kernelbuf += n;
}
res = ps3flash_write_chunk(dev, start_write_sector); res = ps3flash_write_chunk(dev, start_write_sector);
if (res < 0) if (res < 0)
@ -274,7 +291,6 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf,
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
*pos += n; *pos += n;
buf += n;
remaining -= n; remaining -= n;
start_write_sector += chunk_sectors; start_write_sector += chunk_sectors;
head = 0; head = 0;
@ -288,6 +304,29 @@ fail:
return res; return res;
} }
static ssize_t ps3flash_user_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
return ps3flash_read(buf, NULL, count, pos);
}
static ssize_t ps3flash_user_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
return ps3flash_write(buf, NULL, count, pos);
}
static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos)
{
return ps3flash_read(NULL, buf, count, &pos);
}
static ssize_t ps3flash_kernel_write(const void *buf, size_t count,
loff_t pos)
{
return ps3flash_write(NULL, buf, count, &pos);
}
static irqreturn_t ps3flash_interrupt(int irq, void *data) static irqreturn_t ps3flash_interrupt(int irq, void *data)
{ {
@ -312,12 +351,16 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct file_operations ps3flash_fops = { static const struct file_operations ps3flash_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = ps3flash_llseek, .llseek = ps3flash_llseek,
.read = ps3flash_read, .read = ps3flash_user_read,
.write = ps3flash_write, .write = ps3flash_user_write,
};
static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = {
.read = ps3flash_kernel_read,
.write = ps3flash_kernel_write,
}; };
static struct miscdevice ps3flash_misc = { static struct miscdevice ps3flash_misc = {
@ -386,6 +429,8 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n",
__func__, __LINE__, ps3flash_misc.minor); __func__, __LINE__, ps3flash_misc.minor);
ps3_os_area_flash_register(&ps3flash_kernel_ops);
return 0; return 0;
fail_teardown: fail_teardown:
@ -402,6 +447,7 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev)
{ {
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
ps3_os_area_flash_register(NULL);
misc_deregister(&ps3flash_misc); misc_deregister(&ps3flash_misc);
ps3stor_teardown(dev); ps3stor_teardown(dev);
kfree(ps3_system_bus_get_drvdata(&dev->sbd)); kfree(ps3_system_bus_get_drvdata(&dev->sbd));