mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 12:16:20 +00:00
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:
parent
47cb996b05
commit
a4e623fbc9
3 changed files with 152 additions and 87 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
mutex_unlock(&priv->mutex);
|
if (copy_to_user(userbuf, src, n)) {
|
||||||
sectors_read = -EFAULT;
|
mutex_unlock(&priv->mutex);
|
||||||
goto fail;
|
sectors_read = -EFAULT;
|
||||||
|
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,12 +268,20 @@ 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) {
|
||||||
res = -EFAULT;
|
if (copy_from_user(dst, userbuf, n)) {
|
||||||
goto fail;
|
res = -EFAULT;
|
||||||
|
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);
|
||||||
|
@ -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));
|
||||||
|
|
Loading…
Reference in a new issue