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

Commit 80947559 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss2: Validate IOVA space range only for bengal"

parents 148f339b 2c77cb2e
Loading
Loading
Loading
Loading
+121 −43
Original line number Diff line number Diff line
@@ -1095,47 +1095,61 @@ static int icnss_driver_event_unregister_driver(struct icnss_priv *priv,
	return 0;
}

static int icnss_call_driver_remove(struct icnss_priv *priv)
static int icnss_fw_crashed(struct icnss_priv *priv,
			    struct icnss_event_pd_service_down_data *event_data)
{
	icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state);
	icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);

	set_bit(ICNSS_PD_RESTART, &priv->state);
	clear_bit(ICNSS_FW_READY, &priv->state);

	if (test_bit(ICNSS_DRIVER_UNLOADING, &priv->state))
		return 0;
	icnss_pm_stay_awake(priv);

	if (!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
		return 0;
	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
		icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);

	if (event_data && event_data->fw_rejuvenate)
		wlfw_rejuvenate_ack_send_sync_msg(priv);

	if (!priv->ops || !priv->ops->remove)
	return 0;
}

	set_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
	priv->ops->remove(&priv->pdev->dev);
int icnss_update_hang_event_data(struct icnss_priv *priv,
				 struct icnss_uevent_hang_data *hang_data)
{
	if (!priv->hang_event_data_va)
		return -EINVAL;

	clear_bit(ICNSS_DRIVER_UNLOADING, &priv->state);
	clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
	priv->hang_event_data = kmemdup(priv->hang_event_data_va,
					priv->hang_event_data_len,
					GFP_ATOMIC);
	if (!priv->hang_event_data)
		return -ENOMEM;

	icnss_hw_power_off(priv);
	// Update the hang event params
	hang_data->hang_event_data = priv->hang_event_data;
	hang_data->hang_event_data_len = priv->hang_event_data_len;

	return 0;
}

static int icnss_fw_crashed(struct icnss_priv *priv,
			    struct icnss_event_pd_service_down_data *event_data)
int icnss_send_hang_event_data(struct icnss_priv *priv)
{
	icnss_pr_dbg("FW crashed, state: 0x%lx\n", priv->state);

	set_bit(ICNSS_PD_RESTART, &priv->state);
	clear_bit(ICNSS_FW_READY, &priv->state);

	icnss_pm_stay_awake(priv);
	struct icnss_uevent_hang_data hang_data = {0};
	int ret = 0xFF;

	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
		icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
	if (priv->early_crash_ind) {
		ret = icnss_update_hang_event_data(priv, &hang_data);
		if (ret)
			icnss_pr_err("Unable to allocate memory for Hang event data\n");
	}
	icnss_call_driver_uevent(priv, ICNSS_UEVENT_HANG_DATA,
				 &hang_data);

	if (event_data && event_data->fw_rejuvenate)
		wlfw_rejuvenate_ack_send_sync_msg(priv);
	if (!ret) {
		kfree(priv->hang_event_data);
		priv->hang_event_data = NULL;
	}

	return 0;
}
@@ -1153,6 +1167,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
	if (priv->force_err_fatal)
		ICNSS_ASSERT(0);

	if (priv->device_id == ADRASTEA_DEVICE_ID)
		icnss_send_hang_event_data(priv);

	if (priv->early_crash_ind) {
		icnss_pr_dbg("PD Down ignored as early indication is processed: %d, state: 0x%lx\n",
			     event_data->crashed, priv->state);
@@ -1431,8 +1448,13 @@ static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv,
		if (atomic_read(&priv->is_shutdown)) {
			atomic_set(&priv->is_shutdown, false);
			if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
				!test_bit(ICNSS_SHUTDOWN_DONE, &priv->state)) {
				icnss_call_driver_remove(priv);
				!test_bit(ICNSS_SHUTDOWN_DONE, &priv->state) &&
				!test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
				clear_bit(ICNSS_FW_READY, &priv->state);
				icnss_driver_event_post(priv,
					  ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
					  ICNSS_EVENT_SYNC_UNINTERRUPTIBLE,
					  NULL);
			}
		}

@@ -1458,18 +1480,18 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
	struct notif_data *notif = data;
	struct icnss_priv *priv = container_of(nb, struct icnss_priv,
					       modem_ssr_nb);
	struct icnss_uevent_fw_down_data fw_down_data;
	struct icnss_uevent_fw_down_data fw_down_data = {0};

	icnss_pr_vdbg("Modem-Notify: event %lu\n", code);

	if (code == SUBSYS_AFTER_SHUTDOWN) {
		icnss_pr_info("Collecting msa0 segment dump\n");
		icnss_msa0_ramdump(priv);
		return NOTIFY_OK;
		goto out;
	}

	if (code != SUBSYS_BEFORE_SHUTDOWN)
		return NOTIFY_OK;
		goto out;

	priv->is_ssr = true;

@@ -1479,12 +1501,13 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
		set_bit(ICNSS_FW_DOWN, &priv->state);
		icnss_ignore_fw_timeout(true);

		if (test_bit(ICNSS_FW_READY, &priv->state)) {
			fw_down_data.crashed = !!notif->crashed;
		if (test_bit(ICNSS_FW_READY, &priv->state))
			icnss_call_driver_uevent(priv,
						 ICNSS_UEVENT_FW_DOWN,
						 &fw_down_data);
		return NOTIFY_OK;
		}
		goto out;
	}

	icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
@@ -1501,20 +1524,24 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,

	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);

	if (event_data == NULL)
	if (event_data == NULL) {
		icnss_pr_vdbg("Exit %s, event_data is NULL\n", __func__);
		return notifier_from_errno(-ENOMEM);
	}

	event_data->crashed = notif->crashed;

	fw_down_data.crashed = !!notif->crashed;
	if (test_bit(ICNSS_FW_READY, &priv->state))
	if (test_bit(ICNSS_FW_READY, &priv->state)) {
		fw_down_data.crashed = !!notif->crashed;
		icnss_call_driver_uevent(priv,
					 ICNSS_UEVENT_FW_DOWN,
					 &fw_down_data);

	}
	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
				ICNSS_EVENT_SYNC, event_data);

out:
	icnss_pr_vdbg("Exit %s,state: 0x%lx\n", __func__, priv->state);
	return NOTIFY_OK;
}

@@ -1575,7 +1602,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
					       service_notifier_nb);
	enum pd_subsys_state *state = data;
	struct icnss_event_pd_service_down_data *event_data;
	struct icnss_uevent_fw_down_data fw_down_data;
	struct icnss_uevent_fw_down_data fw_down_data = {0};
	enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH;

	icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
@@ -1628,8 +1655,8 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
		set_bit(ICNSS_FW_DOWN, &priv->state);
		icnss_ignore_fw_timeout(true);

		fw_down_data.crashed = event_data->crashed;
		if (test_bit(ICNSS_FW_READY, &priv->state))
			fw_down_data.crashed = event_data->crashed;
			icnss_call_driver_uevent(priv,
						 ICNSS_UEVENT_FW_DOWN,
						 &fw_down_data);
@@ -1979,7 +2006,7 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops)

	icnss_pr_dbg("Unregistering driver, state: 0x%lx\n", priv->state);

	if (!priv->ops) {
	if (!priv->ops || (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))) {
		icnss_pr_err("Driver not registered\n");
		ret = -ENOENT;
		goto out;
@@ -2487,6 +2514,7 @@ int icnss_smmu_map(struct device *dev,
{
	struct icnss_priv *priv = dev_get_drvdata(dev);
	unsigned long iova;
	int prop_len = 0;
	size_t len;
	int ret = 0;

@@ -2503,9 +2531,10 @@ int icnss_smmu_map(struct device *dev,
	}

	len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE);
	iova = roundup(priv->smmu_iova_ipa_start, PAGE_SIZE);
	iova = roundup(priv->smmu_iova_ipa_current, PAGE_SIZE);

	if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
	if (of_get_property(dev->of_node, "qcom,iommu-geometry", &prop_len) &&
	    iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
		icnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
			     iova,
			     &priv->smmu_iova_ipa_start,
@@ -2513,6 +2542,8 @@ int icnss_smmu_map(struct device *dev,
		return -ENOMEM;
	}

	icnss_pr_dbg("IOMMU Map: iova %lx, len %zu\n", iova, len);

	ret = iommu_map(priv->iommu_domain, iova,
			rounddown(paddr, PAGE_SIZE), len,
			IOMMU_READ | IOMMU_WRITE);
@@ -2521,13 +2552,59 @@ int icnss_smmu_map(struct device *dev,
		return ret;
	}

	priv->smmu_iova_ipa_start = iova + len;
	priv->smmu_iova_ipa_current = iova + len;
	*iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE));

	icnss_pr_dbg("IOVA addr mapped to physical addr %lx\n", *iova_addr);
	return 0;
}
EXPORT_SYMBOL(icnss_smmu_map);

int icnss_smmu_unmap(struct device *dev,
		     uint32_t iova_addr, size_t size)
{
	struct icnss_priv *priv = dev_get_drvdata(dev);
	unsigned long iova;
	size_t len, unmapped_len;

	if (!priv) {
		icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
			     dev, priv);
		return -EINVAL;
	}

	if (!iova_addr) {
		icnss_pr_err("iova_addr is NULL, size %zu\n",
			     size);
		return -EINVAL;
	}

	len = roundup(size + iova_addr - rounddown(iova_addr, PAGE_SIZE),
		      PAGE_SIZE);
	iova = rounddown(iova_addr, PAGE_SIZE);

	if (iova >= priv->smmu_iova_ipa_start + priv->smmu_iova_ipa_len) {
		icnss_pr_err("Out of IOVA space during unmap, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n",
			     iova,
			     &priv->smmu_iova_ipa_start,
			     priv->smmu_iova_ipa_len);
		return -ENOMEM;
	}

	icnss_pr_dbg("IOMMU Unmap: iova %lx, len %zu\n",
		     iova, len);

	unmapped_len = iommu_unmap(priv->iommu_domain, iova, len);
	if (unmapped_len != len) {
		icnss_pr_err("Failed to unmap, %zu\n", unmapped_len);
		return -EINVAL;
	}

	priv->smmu_iova_ipa_current = iova;
	return 0;
}
EXPORT_SYMBOL(icnss_smmu_unmap);

unsigned int icnss_socinfo_get_serial_number(struct device *dev)
{
	return socinfo_get_serial_number();
@@ -2921,6 +2998,7 @@ static int icnss_smmu_dt_parse(struct icnss_priv *priv)
			icnss_pr_err("SMMU IOVA IPA not found\n");
		} else {
			priv->smmu_iova_ipa_start = res->start;
			priv->smmu_iova_ipa_current = res->start;
			priv->smmu_iova_ipa_len = resource_size(res);
			icnss_pr_dbg("SMMU IOVA IPA start: %pa, len: %zx\n",
				     &priv->smmu_iova_ipa_start,
+6 −0
Original line number Diff line number Diff line
@@ -245,6 +245,7 @@ struct icnss_stats {
#define WLFW_MAX_NUM_CE 12
#define WLFW_MAX_NUM_SVC 24
#define WLFW_MAX_NUM_SHADOW_REG 24
#define WLFW_MAX_HANG_EVENT_DATA_SIZE 400

struct service_notifier_context {
	void *handle;
@@ -303,6 +304,7 @@ struct icnss_priv {
	u32 mem_base_size;
	struct iommu_domain *iommu_domain;
	dma_addr_t smmu_iova_ipa_start;
	dma_addr_t smmu_iova_ipa_current;
	size_t smmu_iova_ipa_len;
	struct qmi_handle qmi;
	struct list_head event_list;
@@ -374,6 +376,10 @@ struct icnss_priv {
	struct thermal_cooling_device *tcdev;
	unsigned long curr_thermal_state;
	unsigned long max_thermal_state;
	phys_addr_t hang_event_data_pa;
	void __iomem *hang_event_data_va;
	uint16_t hang_event_data_len;
	void *hang_event_data;
};

struct icnss_reg_info {
+59 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/thread_info.h>
#include <linux/firmware.h>
#include <linux/soc/qcom/qmi.h>
#include <linux/platform_device.h>
#include <soc/qcom/icnss2.h>
#include <soc/qcom/service-locator.h>
#include <soc/qcom/service-notifier.h>
@@ -1460,7 +1461,7 @@ int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
void icnss_handle_rejuvenate(struct icnss_priv *priv)
{
	struct icnss_event_pd_service_down_data *event_data;
	struct icnss_uevent_fw_down_data fw_down_data;
	struct icnss_uevent_fw_down_data fw_down_data = {0};

	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
	if (event_data == NULL)
@@ -1574,6 +1575,10 @@ static void msa_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
			     struct qmi_txn *txn, const void *data)
{
	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
	struct device *dev = &priv->pdev->dev;
	const struct wlfw_msa_ready_ind_msg_v01 *ind_msg = data;
	uint64_t msa_base_addr = priv->msa_pa;
	phys_addr_t hang_data_phy_addr;

	icnss_pr_dbg("Received MSA Ready Indication\n");

@@ -1583,6 +1588,59 @@ static void msa_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
	}

	priv->stats.msa_ready_ind++;

	/* Check if the length is valid &
	 * the length should not be 0 and
	 * should be <=  WLFW_MAX_HANG_EVENT_DATA_SIZE(400)
	 */

	if (ind_msg->hang_data_length_valid &&
	    ind_msg->hang_data_length &&
	    ind_msg->hang_data_length <= WLFW_MAX_HANG_EVENT_DATA_SIZE)
		priv->hang_event_data_len = ind_msg->hang_data_length;
	else
		goto out;

	/* Check if the offset is valid &
	 * the offset should be in range of 0 to msa_mem_size-hang_data_length
	 */

	if (ind_msg->hang_data_addr_offset_valid &&
	    (ind_msg->hang_data_addr_offset <= (priv->msa_mem_size -
						 ind_msg->hang_data_length)))
		hang_data_phy_addr = msa_base_addr +
						ind_msg->hang_data_addr_offset;
	else
		goto out;

	if (priv->hang_event_data_pa == hang_data_phy_addr)
		goto exit;

	priv->hang_event_data_pa = hang_data_phy_addr;
	priv->hang_event_data_va = devm_ioremap(dev, priv->hang_event_data_pa,
						ind_msg->hang_data_length);

	if (!priv->hang_event_data_va) {
		icnss_pr_err("Hang Data ioremap failed: phy addr: %pa\n",
			     &priv->hang_event_data_pa);
		goto fail;
	}
exit:
	icnss_pr_dbg("Hang Event Data details,Offset:0x%x, Length:0x%x,va_addr: 0x%pK\n",
		     ind_msg->hang_data_addr_offset,
		     ind_msg->hang_data_length,
		     priv->hang_event_data_va);

	return;

out:
	icnss_pr_err("Invalid Hang Data details, Offset:0x%x, Length:0x%x",
		     ind_msg->hang_data_addr_offset,
		     ind_msg->hang_data_length);
fail:
	priv->hang_event_data_va = NULL;
	priv->hang_event_data_pa = 0;
	priv->hang_event_data_len = 0;
}

static void pin_connect_result_ind_cb(struct qmi_handle *qmi,
+8 −0
Original line number Diff line number Diff line
@@ -19,6 +19,12 @@
enum icnss_uevent {
	ICNSS_UEVENT_FW_CRASHED,
	ICNSS_UEVENT_FW_DOWN,
	ICNSS_UEVENT_HANG_DATA,
};

struct icnss_uevent_hang_data {
	void *hang_event_data;
	uint16_t hang_event_data_len;
};

struct icnss_uevent_fw_down_data {
@@ -149,6 +155,8 @@ extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev);
extern struct iommu_domain *icnss_smmu_get_domain(struct device *dev);
extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
			  uint32_t *iova_addr, size_t size);
extern int icnss_smmu_unmap(struct device *dev,
			    uint32_t iova_addr, size_t size);
extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
extern bool icnss_is_qmi_disable(struct device *dev);
extern bool icnss_is_fw_ready(void);