Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0da385b3 authored by Ahmad Masri's avatar Ahmad Masri Committed by Lior David
Browse files

wil6210: prevent device memory access while in reset or suspend



Accessing some of the memory of the device while the device is
resetting or suspending may cause unexpected error as the HW is still
not in a stable state. Prevent this access to guarantee successful
read/write memory operations.

Change-Id: Ia412d6ae82adb32d957e1bb78d5fa9d644f08581
Signed-off-by: default avatarAhmad Masri <amasri@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
[liord@codeaurora.org: fix merge conflicts, SPDX]
Signed-off-by: default avatarLior David <liord@codeaurora.org>
parent 5ee321a5
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -269,6 +269,11 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,

	wil_halp_vote(wil);

	if (wil_mem_access_lock(wil)) {
		wil_halp_unvote(wil);
		return;
	}

	wil_memcpy_fromio_32(&r, off, sizeof(r));
	wil_mbox_ring_le2cpus(&r);
	/*
@@ -334,6 +339,7 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,
	}
 out:
	seq_puts(s, "}\n");
	wil_mem_access_unlock(wil);
	wil_halp_unvote(wil);
}

@@ -622,6 +628,12 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data)
	if (ret < 0)
		return ret;

	ret = wil_mem_access_lock(wil);
	if (ret) {
		wil_pm_runtime_put(wil);
		return ret;
	}

	a = wmi_buffer(wil, cpu_to_le32(mem_addr));

	if (a)
@@ -629,6 +641,8 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data)
	else
		seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);

	wil_mem_access_unlock(wil);

	wil_pm_runtime_put(wil);

	return 0;
@@ -658,10 +672,6 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
	size_t unaligned_bytes, aligned_count, ret;
	int rc;

	if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
	    test_bit(wil_status_suspended, wil_blob->wil->status))
		return 0;

	if (pos < 0)
		return -EINVAL;

@@ -688,11 +698,19 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
		return rc;
	}

	rc = wil_mem_access_lock(wil);
	if (rc) {
		kfree(buf);
		wil_pm_runtime_put(wil);
		return rc;
	}

	wil_memcpy_fromio_32(buf, (const void __iomem *)
			     wil_blob->blob.data + aligned_pos, aligned_count);

	ret = copy_to_user(user_buf, buf + unaligned_bytes, count);

	wil_mem_access_unlock(wil);
	wil_pm_runtime_put(wil);

	kfree(buf);
+31 −10
Original line number Diff line number Diff line
@@ -173,6 +173,28 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
	}
}

/* Device memory access is prohibited while reset or suspend.
 * wil_mem_access_lock protects accessing device memory in these cases
 */
int wil_mem_access_lock(struct wil6210_priv *wil)
{
	if (!down_read_trylock(&wil->mem_lock))
		return -EBUSY;

	if (test_bit(wil_status_suspending, wil->status) ||
	    test_bit(wil_status_suspended, wil->status)) {
		up_read(&wil->mem_lock);
		return -EBUSY;
	}

	return 0;
}

void wil_mem_access_unlock(struct wil6210_priv *wil)
{
	up_read(&wil->mem_lock);
}

static void wil_ring_fini_tx(struct wil6210_priv *wil, int id)
{
	struct wil_ring *ring = &wil->ring_tx[id];
@@ -695,6 +717,7 @@ int wil_priv_init(struct wil6210_priv *wil)
	spin_lock_init(&wil->wmi_ev_lock);
	spin_lock_init(&wil->net_queue_lock);
	init_waitqueue_head(&wil->wq);
	init_rwsem(&wil->mem_lock);

	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi");
	if (!wil->wmi_wq)
@@ -1624,15 +1647,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
	}

	set_bit(wil_status_resetting, wil->status);
	if (test_bit(wil_status_collecting_dumps, wil->status)) {
		/* Device collects crash dump, cancel the reset.
		 * following crash dump collection, reset would take place.
		 */
		wil_dbg_misc(wil, "reject reset while collecting crash dump\n");
		rc = -EBUSY;
		goto out;
	}

	mutex_lock(&wil->vif_mutex);
	wil_abort_scan_all_vifs(wil, false);
	mutex_unlock(&wil->vif_mutex);
@@ -1816,7 +1830,9 @@ int __wil_up(struct wil6210_priv *wil)

	WARN_ON(!mutex_is_locked(&wil->mutex));

	down_write(&wil->mem_lock);
	rc = wil_reset(wil, true);
	up_write(&wil->mem_lock);
	if (rc)
		return rc;

@@ -1888,6 +1904,7 @@ int wil_up(struct wil6210_priv *wil)

int __wil_down(struct wil6210_priv *wil)
{
	int rc;
	WARN_ON(!mutex_is_locked(&wil->mutex));

	set_bit(wil_status_resetting, wil->status);
@@ -1908,7 +1925,11 @@ int __wil_down(struct wil6210_priv *wil)
	wil_abort_scan_all_vifs(wil, false);
	mutex_unlock(&wil->vif_mutex);

	return wil_reset(wil, false);
	down_write(&wil->mem_lock);
	rc = wil_reset(wil, false);
	up_write(&wil->mem_lock);

	return rc;
}

int wil_down(struct wil6210_priv *wil)
+19 −23
Original line number Diff line number Diff line
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 */

#include "wil6210.h"
@@ -195,14 +184,18 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
	wil_dbg_pm(wil, "suspend keep radio on\n");

	/* Prevent handling of new tx and wmi commands */
	set_bit(wil_status_suspending, wil->status);
	if (test_bit(wil_status_collecting_dumps, wil->status)) {
		/* Device collects crash dump, cancel the suspend */
		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
		clear_bit(wil_status_suspending, wil->status);
	rc = down_write_trylock(&wil->mem_lock);
	if (!rc) {
		wil_err(wil,
			"device is busy. down_write_trylock failed, returned (0x%x)\n",
			rc);
		wil->suspend_stats.rejected_by_host++;
		return -EBUSY;
	}

	set_bit(wil_status_suspending, wil->status);
	up_write(&wil->mem_lock);

	wil_pm_stop_all_net_queues(wil);

	if (!wil_is_tx_idle(wil)) {
@@ -311,15 +304,18 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)

	wil_dbg_pm(wil, "suspend radio off\n");

	set_bit(wil_status_suspending, wil->status);
	if (test_bit(wil_status_collecting_dumps, wil->status)) {
		/* Device collects crash dump, cancel the suspend */
		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
		clear_bit(wil_status_suspending, wil->status);
	rc = down_write_trylock(&wil->mem_lock);
	if (!rc) {
		wil_err(wil,
			"device is busy. down_write_trylock failed, returned (0x%x)\n",
			rc);
		wil->suspend_stats.rejected_by_host++;
		return -EBUSY;
	}

	set_bit(wil_status_suspending, wil->status);
	up_write(&wil->mem_lock);

	/* if netif up, hardware is alive, shut it down */
	mutex_lock(&wil->vif_mutex);
	active_ifaces = wil_has_active_ifaces(wil, true, false);
+4 −1
Original line number Diff line number Diff line
@@ -651,7 +651,6 @@ enum { /* for wil6210_priv.status */
	wil_status_suspending, /* suspend in progress */
	wil_status_suspended, /* suspend completed, device is suspended */
	wil_status_resuming, /* resume in progress */
	wil_status_collecting_dumps, /* crashdump collection in progress */
	wil_status_last /* keep last */
};

@@ -987,6 +986,8 @@ struct wil6210_priv {
	struct wil_txrx_ops txrx_ops;

	struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
	/* for synchronizing device memory access while reset or suspend */
	struct rw_semaphore mem_lock;
	/* statistics */
	atomic_t isr_count_rx, isr_count_tx;
	/* debugfs */
@@ -1185,6 +1186,8 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
			  size_t count);
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
			size_t count);
int wil_mem_access_lock(struct wil6210_priv *wil);
void wil_mem_access_unlock(struct wil6210_priv *wil);

struct wil6210_vif *
wil_vif_alloc(struct wil6210_priv *wil, const char *name,
+7 −24
Original line number Diff line number Diff line
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2015,2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 */

#include "wil6210.h"
@@ -57,7 +46,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,

int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
{
	int i;
	int i, rc;
	const struct fw_map *map;
	void *data;
	u32 host_min, dump_size, offset, len;
@@ -73,14 +62,9 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
		return -EINVAL;
	}

	set_bit(wil_status_collecting_dumps, wil->status);
	if (test_bit(wil_status_suspending, wil->status) ||
	    test_bit(wil_status_suspended, wil->status) ||
	    test_bit(wil_status_resetting, wil->status)) {
		wil_err(wil, "cannot collect fw dump during suspend/reset\n");
		clear_bit(wil_status_collecting_dumps, wil->status);
		return -EINVAL;
	}
	rc = wil_mem_access_lock(wil);
	if (rc)
		return rc;

	/* copy to crash dump area */
	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
@@ -100,8 +84,7 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
		wil_memcpy_fromio_32((void * __force)(dest + offset),
				     (const void __iomem * __force)data, len);
	}

	clear_bit(wil_status_collecting_dumps, wil->status);
	wil_mem_access_unlock(wil);

	return 0;
}