diff options
author | Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> | 2009-06-10 00:39:06 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-06-15 02:47:26 -0400 |
commit | a4e623fbc9b201930abcf78df6db5e49aa8e00cb (patch) | |
tree | 60017689df8260806cba7c007f9f9d85e3758b31 /drivers/char | |
parent | 47cb996b059e0e5696b8daa1f62881a6462a251a (diff) |
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>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ps3flash.c | 94 |
1 files changed, 70 insertions, 24 deletions
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 184e6ed2393d..f7f21f47ea02 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c | |||
@@ -104,18 +104,19 @@ out: | |||
104 | return res; | 104 | return res; |
105 | } | 105 | } |
106 | 106 | ||
107 | static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, | 107 | static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, |
108 | loff_t *pos) | 108 | size_t count, loff_t *pos) |
109 | { | 109 | { |
110 | struct ps3_storage_device *dev = ps3flash_dev; | 110 | struct ps3_storage_device *dev = ps3flash_dev; |
111 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); | 111 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); |
112 | u64 size, start_sector, end_sector, offset; | 112 | u64 size, start_sector, end_sector, offset; |
113 | ssize_t sectors_read; | 113 | ssize_t sectors_read; |
114 | size_t remaining, n; | 114 | size_t remaining, n; |
115 | const void *src; | ||
115 | 116 | ||
116 | dev_dbg(&dev->sbd.core, | 117 | dev_dbg(&dev->sbd.core, |
117 | "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", | 118 | "%s:%u: Reading %zu bytes at position %lld to U0x%p/K0x%p\n", |
118 | __func__, __LINE__, count, *pos, buf); | 119 | __func__, __LINE__, count, *pos, userbuf, kernelbuf); |
119 | 120 | ||
120 | size = dev->regions[dev->region_idx].size*dev->blk_size; | 121 | size = dev->regions[dev->region_idx].size*dev->blk_size; |
121 | if (*pos >= size || !count) | 122 | if (*pos >= size || !count) |
@@ -145,19 +146,26 @@ static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, | |||
145 | } | 146 | } |
146 | 147 | ||
147 | n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); | 148 | n = min_t(u64, remaining, sectors_read*dev->blk_size-offset); |
149 | src = dev->bounce_buf+offset; | ||
148 | dev_dbg(&dev->sbd.core, | 150 | dev_dbg(&dev->sbd.core, |
149 | "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", | 151 | "%s:%u: copy %lu bytes from 0x%p to U0x%p/K0x%p\n", |
150 | __func__, __LINE__, n, dev->bounce_buf+offset, buf); | 152 | __func__, __LINE__, n, src, userbuf, kernelbuf); |
151 | if (copy_to_user(buf, dev->bounce_buf+offset, n)) { | 153 | if (userbuf) { |
152 | mutex_unlock(&priv->mutex); | 154 | if (copy_to_user(userbuf, src, n)) { |
153 | sectors_read = -EFAULT; | 155 | mutex_unlock(&priv->mutex); |
154 | goto fail; | 156 | sectors_read = -EFAULT; |
157 | goto fail; | ||
158 | } | ||
159 | userbuf += n; | ||
160 | } | ||
161 | if (kernelbuf) { | ||
162 | memcpy(kernelbuf, src, n); | ||
163 | kernelbuf += n; | ||
155 | } | 164 | } |
156 | 165 | ||
157 | mutex_unlock(&priv->mutex); | 166 | mutex_unlock(&priv->mutex); |
158 | 167 | ||
159 | *pos += n; | 168 | *pos += n; |
160 | buf += n; | ||
161 | remaining -= n; | 169 | remaining -= n; |
162 | start_sector += sectors_read; | 170 | start_sector += sectors_read; |
163 | offset = 0; | 171 | offset = 0; |
@@ -169,8 +177,8 @@ fail: | |||
169 | return sectors_read; | 177 | return sectors_read; |
170 | } | 178 | } |
171 | 179 | ||
172 | static ssize_t ps3flash_write(struct file *file, const char __user *buf, | 180 | static ssize_t ps3flash_write(const char __user *userbuf, |
173 | size_t count, loff_t *pos) | 181 | const void *kernelbuf, size_t count, loff_t *pos) |
174 | { | 182 | { |
175 | struct ps3_storage_device *dev = ps3flash_dev; | 183 | struct ps3_storage_device *dev = ps3flash_dev; |
176 | struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); | 184 | 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, | |||
179 | ssize_t res; | 187 | ssize_t res; |
180 | size_t remaining, n; | 188 | size_t remaining, n; |
181 | unsigned int sec_off; | 189 | unsigned int sec_off; |
190 | void *dst; | ||
182 | 191 | ||
183 | dev_dbg(&dev->sbd.core, | 192 | dev_dbg(&dev->sbd.core, |
184 | "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", | 193 | "%s:%u: Writing %zu bytes at position %lld from U0x%p/K0x%p\n", |
185 | __func__, __LINE__, count, *pos, buf); | 194 | __func__, __LINE__, count, *pos, userbuf, kernelbuf); |
186 | 195 | ||
187 | size = dev->regions[dev->region_idx].size*dev->blk_size; | 196 | size = dev->regions[dev->region_idx].size*dev->blk_size; |
188 | if (*pos >= size || !count) | 197 | if (*pos >= size || !count) |
@@ -259,12 +268,20 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, | |||
259 | } | 268 | } |
260 | 269 | ||
261 | n = min_t(u64, remaining, dev->bounce_size-offset); | 270 | n = min_t(u64, remaining, dev->bounce_size-offset); |
271 | dst = dev->bounce_buf+offset; | ||
262 | dev_dbg(&dev->sbd.core, | 272 | dev_dbg(&dev->sbd.core, |
263 | "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", | 273 | "%s:%u: copy %lu bytes from U0x%p/K0x%p to 0x%p\n", |
264 | __func__, __LINE__, n, buf, dev->bounce_buf+offset); | 274 | __func__, __LINE__, n, userbuf, kernelbuf, dst); |
265 | if (copy_from_user(dev->bounce_buf+offset, buf, n)) { | 275 | if (userbuf) { |
266 | res = -EFAULT; | 276 | if (copy_from_user(dst, userbuf, n)) { |
267 | goto fail; | 277 | res = -EFAULT; |
278 | goto fail; | ||
279 | } | ||
280 | userbuf += n; | ||
281 | } | ||
282 | if (kernelbuf) { | ||
283 | memcpy(dst, kernelbuf, n); | ||
284 | kernelbuf += n; | ||
268 | } | 285 | } |
269 | 286 | ||
270 | res = ps3flash_write_chunk(dev, start_write_sector); | 287 | res = ps3flash_write_chunk(dev, start_write_sector); |
@@ -274,7 +291,6 @@ static ssize_t ps3flash_write(struct file *file, const char __user *buf, | |||
274 | mutex_unlock(&priv->mutex); | 291 | mutex_unlock(&priv->mutex); |
275 | 292 | ||
276 | *pos += n; | 293 | *pos += n; |
277 | buf += n; | ||
278 | remaining -= n; | 294 | remaining -= n; |
279 | start_write_sector += chunk_sectors; | 295 | start_write_sector += chunk_sectors; |
280 | head = 0; | 296 | head = 0; |
@@ -288,6 +304,29 @@ fail: | |||
288 | return res; | 304 | return res; |
289 | } | 305 | } |
290 | 306 | ||
307 | static ssize_t ps3flash_user_read(struct file *file, char __user *buf, | ||
308 | size_t count, loff_t *pos) | ||
309 | { | ||
310 | return ps3flash_read(buf, NULL, count, pos); | ||
311 | } | ||
312 | |||
313 | static ssize_t ps3flash_user_write(struct file *file, const char __user *buf, | ||
314 | size_t count, loff_t *pos) | ||
315 | { | ||
316 | return ps3flash_write(buf, NULL, count, pos); | ||
317 | } | ||
318 | |||
319 | static ssize_t ps3flash_kernel_read(void *buf, size_t count, loff_t pos) | ||
320 | { | ||
321 | return ps3flash_read(NULL, buf, count, &pos); | ||
322 | } | ||
323 | |||
324 | static ssize_t ps3flash_kernel_write(const void *buf, size_t count, | ||
325 | loff_t pos) | ||
326 | { | ||
327 | return ps3flash_write(NULL, buf, count, &pos); | ||
328 | } | ||
329 | |||
291 | 330 | ||
292 | static irqreturn_t ps3flash_interrupt(int irq, void *data) | 331 | static irqreturn_t ps3flash_interrupt(int irq, void *data) |
293 | { | 332 | { |
@@ -312,12 +351,16 @@ static irqreturn_t ps3flash_interrupt(int irq, void *data) | |||
312 | return IRQ_HANDLED; | 351 | return IRQ_HANDLED; |
313 | } | 352 | } |
314 | 353 | ||
315 | |||
316 | static const struct file_operations ps3flash_fops = { | 354 | static const struct file_operations ps3flash_fops = { |
317 | .owner = THIS_MODULE, | 355 | .owner = THIS_MODULE, |
318 | .llseek = ps3flash_llseek, | 356 | .llseek = ps3flash_llseek, |
319 | .read = ps3flash_read, | 357 | .read = ps3flash_user_read, |
320 | .write = ps3flash_write, | 358 | .write = ps3flash_user_write, |
359 | }; | ||
360 | |||
361 | static const struct ps3_os_area_flash_ops ps3flash_kernel_ops = { | ||
362 | .read = ps3flash_kernel_read, | ||
363 | .write = ps3flash_kernel_write, | ||
321 | }; | 364 | }; |
322 | 365 | ||
323 | static struct miscdevice ps3flash_misc = { | 366 | static struct miscdevice ps3flash_misc = { |
@@ -386,6 +429,8 @@ static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) | |||
386 | 429 | ||
387 | dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", | 430 | dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", |
388 | __func__, __LINE__, ps3flash_misc.minor); | 431 | __func__, __LINE__, ps3flash_misc.minor); |
432 | |||
433 | ps3_os_area_flash_register(&ps3flash_kernel_ops); | ||
389 | return 0; | 434 | return 0; |
390 | 435 | ||
391 | fail_teardown: | 436 | fail_teardown: |
@@ -402,6 +447,7 @@ static int ps3flash_remove(struct ps3_system_bus_device *_dev) | |||
402 | { | 447 | { |
403 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); | 448 | struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); |
404 | 449 | ||
450 | ps3_os_area_flash_register(NULL); | ||
405 | misc_deregister(&ps3flash_misc); | 451 | misc_deregister(&ps3flash_misc); |
406 | ps3stor_teardown(dev); | 452 | ps3stor_teardown(dev); |
407 | kfree(ps3_system_bus_get_drvdata(&dev->sbd)); | 453 | kfree(ps3_system_bus_get_drvdata(&dev->sbd)); |