Commit 174beab7 authored by Andrea Merello's avatar Andrea Merello Committed by John W. Linville

at76c50x-usb: Don't perform DMA from stack memory

Loading the driver with DMA debugging enabled makes the kernel to complain
about the ehci driver trying to perform DMA from memory from the stack.

[ 9848.229514] WARNING: CPU: 1 PID: 627 at lib/dma-debug.c:1153 check_for_stack+0xa4/0xf0()
[ 9848.237678] ehci-pci 0000:00:04.1: DMA-API: device driver maps memory fromstack [addr=ffff88006c80da01]

This is due to at76c50x-usb driver passing buffers allocated on the stack to
the USB layer, that attempts DMA. This occurs is several places.

This patch fixes the problem by allocating those buffers via kmalloc.

Since this adds some kfree() before leaving a couple of functions, I caught the
occasion to clean-up the exit path on error.
Signed-off-by: default avatarAndrea Merello <andrea.merello@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 7dd74f5f
...@@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s) ...@@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s)
static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
int manifest_sync_timeout) int manifest_sync_timeout)
{ {
u8 *block;
struct dfu_status dfu_stat_buf;
int ret = 0; int ret = 0;
int need_dfu_state = 1; int need_dfu_state = 1;
int is_done = 0; int is_done = 0;
u8 dfu_state = 0;
u32 dfu_timeout = 0; u32 dfu_timeout = 0;
int bsize = 0; int bsize = 0;
int blockno = 0; int blockno = 0;
struct dfu_status *dfu_stat_buf = NULL;
u8 *dfu_state = NULL;
u8 *block = NULL;
at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,
manifest_sync_timeout); manifest_sync_timeout);
...@@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, ...@@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
return -EINVAL; return -EINVAL;
} }
dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL);
if (!dfu_stat_buf) {
ret = -ENOMEM;
goto exit;
}
block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
if (!block) if (!block) {
return -ENOMEM; ret = -ENOMEM;
goto exit;
}
dfu_state = kmalloc(sizeof(u8), GFP_KERNEL);
if (!dfu_state) {
ret = -ENOMEM;
goto exit;
}
*dfu_state = 0;
do { do {
if (need_dfu_state) { if (need_dfu_state) {
ret = at76_dfu_get_state(udev, &dfu_state); ret = at76_dfu_get_state(udev, dfu_state);
if (ret < 0) { if (ret < 0) {
dev_err(&udev->dev, dev_err(&udev->dev,
"cannot get DFU state: %d\n", ret); "cannot get DFU state: %d\n", ret);
...@@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, ...@@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
need_dfu_state = 0; need_dfu_state = 0;
} }
switch (dfu_state) { switch (*dfu_state) {
case STATE_DFU_DOWNLOAD_SYNC: case STATE_DFU_DOWNLOAD_SYNC:
at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC");
ret = at76_dfu_get_status(udev, &dfu_stat_buf); ret = at76_dfu_get_status(udev, dfu_stat_buf);
if (ret >= 0) { if (ret >= 0) {
dfu_state = dfu_stat_buf.state; *dfu_state = dfu_stat_buf->state;
dfu_timeout = at76_get_timeout(&dfu_stat_buf); dfu_timeout = at76_get_timeout(dfu_stat_buf);
need_dfu_state = 0; need_dfu_state = 0;
} else } else
dev_err(&udev->dev, dev_err(&udev->dev,
...@@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, ...@@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
case STATE_DFU_MANIFEST_SYNC: case STATE_DFU_MANIFEST_SYNC:
at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC");
ret = at76_dfu_get_status(udev, &dfu_stat_buf); ret = at76_dfu_get_status(udev, dfu_stat_buf);
if (ret < 0) if (ret < 0)
break; break;
dfu_state = dfu_stat_buf.state; *dfu_state = dfu_stat_buf->state;
dfu_timeout = at76_get_timeout(&dfu_stat_buf); dfu_timeout = at76_get_timeout(dfu_stat_buf);
need_dfu_state = 0; need_dfu_state = 0;
/* override the timeout from the status response, /* override the timeout from the status response,
...@@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, ...@@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
break; break;
default: default:
at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state);
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
} while (!is_done && (ret >= 0)); } while (!is_done && (ret >= 0));
exit: exit:
kfree(dfu_state);
kfree(block); kfree(block);
kfree(dfu_stat_buf);
if (ret >= 0) if (ret >= 0)
ret = 0; ret = 0;
...@@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) ...@@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
dev_err(&udev->dev, dev_err(&udev->dev,
"loading %dth firmware block failed: %d\n", "loading %dth firmware block failed: %d\n",
blockno, ret); blockno, ret);
ret = -EIO;
goto exit; goto exit;
} }
buf += bsize; buf += bsize;
...@@ -2330,16 +2349,22 @@ static int at76_probe(struct usb_interface *interface, ...@@ -2330,16 +2349,22 @@ static int at76_probe(struct usb_interface *interface,
struct usb_device *udev; struct usb_device *udev;
int op_mode; int op_mode;
int need_ext_fw = 0; int need_ext_fw = 0;
struct mib_fw_version fwv; struct mib_fw_version *fwv = NULL;
int board_type = (int)id->driver_info; int board_type = (int)id->driver_info;
udev = usb_get_dev(interface_to_usbdev(interface)); udev = usb_get_dev(interface_to_usbdev(interface));
fwv = kmalloc(sizeof(*fwv), GFP_KERNEL);
if (!fwv) {
ret = -ENOMEM;
goto exit;
}
/* Load firmware into kernel memory */ /* Load firmware into kernel memory */
fwe = at76_load_firmware(udev, board_type); fwe = at76_load_firmware(udev, board_type);
if (!fwe) { if (!fwe) {
ret = -ENOENT; ret = -ENOENT;
goto error; goto exit;
} }
op_mode = at76_get_op_mode(udev); op_mode = at76_get_op_mode(udev);
...@@ -2353,7 +2378,7 @@ static int at76_probe(struct usb_interface *interface, ...@@ -2353,7 +2378,7 @@ static int at76_probe(struct usb_interface *interface,
dev_err(&interface->dev, dev_err(&interface->dev,
"cannot handle a device in HW_CONFIG_MODE\n"); "cannot handle a device in HW_CONFIG_MODE\n");
ret = -EBUSY; ret = -EBUSY;
goto error; goto exit;
} }
if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
...@@ -2366,10 +2391,10 @@ static int at76_probe(struct usb_interface *interface, ...@@ -2366,10 +2391,10 @@ static int at76_probe(struct usb_interface *interface,
dev_err(&interface->dev, dev_err(&interface->dev,
"error %d downloading internal firmware\n", "error %d downloading internal firmware\n",
ret); ret);
goto error; goto exit;
} }
usb_put_dev(udev); usb_put_dev(udev);
return ret; goto exit;
} }
/* Internal firmware already inside the device. Get firmware /* Internal firmware already inside the device. Get firmware
...@@ -2382,8 +2407,8 @@ static int at76_probe(struct usb_interface *interface, ...@@ -2382,8 +2407,8 @@ static int at76_probe(struct usb_interface *interface,
* query the device for the fw version */ * query the device for the fw version */
if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
|| (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
if (ret < 0 || (fwv.major | fwv.minor) == 0) if (ret < 0 || (fwv->major | fwv->minor) == 0)
need_ext_fw = 1; need_ext_fw = 1;
} else } else
/* No way to check firmware version, reload to be sure */ /* No way to check firmware version, reload to be sure */
...@@ -2394,37 +2419,37 @@ static int at76_probe(struct usb_interface *interface, ...@@ -2394,37 +2419,37 @@ static int at76_probe(struct usb_interface *interface,
"downloading external firmware\n"); "downloading external firmware\n");
ret = at76_load_external_fw(udev, fwe); ret = at76_load_external_fw(udev, fwe);
if (ret) if (ret < 0)
goto error; goto exit;
/* Re-check firmware version */ /* Re-check firmware version */
ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
if (ret < 0) { if (ret < 0) {
dev_err(&interface->dev, dev_err(&interface->dev,
"error %d getting firmware version\n", ret); "error %d getting firmware version\n", ret);
goto error; goto exit;
} }
} }
priv = at76_alloc_new_device(udev); priv = at76_alloc_new_device(udev);
if (!priv) { if (!priv) {
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto exit;
} }
usb_set_intfdata(interface, priv); usb_set_intfdata(interface, priv);
memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version));
priv->board_type = board_type; priv->board_type = board_type;
ret = at76_init_new_device(priv, interface); ret = at76_init_new_device(priv, interface);
if (ret < 0) if (ret < 0)
at76_delete_device(priv); at76_delete_device(priv);
return ret; exit:
kfree(fwv);
error: if (ret < 0)
usb_put_dev(udev); usb_put_dev(udev);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment