aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJordan Crouse <jcrouse@codeaurora.org>2018-12-11 15:07:45 -0500
committerAndy Gross <andy.gross@linaro.org>2019-02-12 16:27:14 -0500
commit72d1cd033154f50e77cd4feb4e16c227b598632e (patch)
tree06fbf3e0029aa0de953db993e49ae8cef4fb1423
parent4e2256d31f0f24107c36dae35a1d84dff0fced30 (diff)
qcom: soc: llcc-slice: Clear the global drv_data pointer on error
Currently the data structure for llc-slice is devm allocated and stored as a global but never cleared if the probe function fails. This is a problem because devm managed memory gets freed on probe failure the API functions could access the pointer after it has been freed. Initialize the drv_data pointer to an error and reset it to an error on probe failure or device destroy and add protection to the API functions to make sure the memory doesn't get accessed. Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org> Signed-off-by: Andy Gross <andy.gross@linaro.org>
-rw-r--r--drivers/soc/qcom/llcc-sdm845.c6
-rw-r--r--drivers/soc/qcom/llcc-slice.c71
-rw-r--r--include/linux/soc/qcom/llcc-qcom.h6
3 files changed, 66 insertions, 17 deletions
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c
index 2e1e4f0a5db8..86600d97c36d 100644
--- a/drivers/soc/qcom/llcc-sdm845.c
+++ b/drivers/soc/qcom/llcc-sdm845.c
@@ -71,6 +71,11 @@ static struct llcc_slice_config sdm845_data[] = {
71 SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), 71 SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0),
72}; 72};
73 73
74static int sdm845_qcom_llcc_remove(struct platform_device *pdev)
75{
76 return qcom_llcc_remove(pdev);
77}
78
74static int sdm845_qcom_llcc_probe(struct platform_device *pdev) 79static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
75{ 80{
76 return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); 81 return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data));
@@ -87,6 +92,7 @@ static struct platform_driver sdm845_qcom_llcc_driver = {
87 .of_match_table = sdm845_qcom_llcc_of_match, 92 .of_match_table = sdm845_qcom_llcc_of_match,
88 }, 93 },
89 .probe = sdm845_qcom_llcc_probe, 94 .probe = sdm845_qcom_llcc_probe,
95 .remove = sdm845_qcom_llcc_remove,
90}; 96};
91module_platform_driver(sdm845_qcom_llcc_driver); 97module_platform_driver(sdm845_qcom_llcc_driver);
92 98
diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c
index 80667f7be52c..8390bc006a31 100644
--- a/drivers/soc/qcom/llcc-slice.c
+++ b/drivers/soc/qcom/llcc-slice.c
@@ -46,7 +46,7 @@
46 46
47#define BANK_OFFSET_STRIDE 0x80000 47#define BANK_OFFSET_STRIDE 0x80000
48 48
49static struct llcc_drv_data *drv_data; 49static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
50 50
51static const struct regmap_config llcc_regmap_config = { 51static const struct regmap_config llcc_regmap_config = {
52 .reg_bits = 32, 52 .reg_bits = 32,
@@ -68,6 +68,9 @@ struct llcc_slice_desc *llcc_slice_getd(u32 uid)
68 struct llcc_slice_desc *desc; 68 struct llcc_slice_desc *desc;
69 u32 sz, count; 69 u32 sz, count;
70 70
71 if (IS_ERR(drv_data))
72 return ERR_CAST(drv_data);
73
71 cfg = drv_data->cfg; 74 cfg = drv_data->cfg;
72 sz = drv_data->cfg_size; 75 sz = drv_data->cfg_size;
73 76
@@ -108,6 +111,9 @@ static int llcc_update_act_ctrl(u32 sid,
108 u32 slice_status; 111 u32 slice_status;
109 int ret; 112 int ret;
110 113
114 if (IS_ERR(drv_data))
115 return PTR_ERR(drv_data);
116
111 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); 117 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
112 status_reg = LLCC_TRP_STATUSn(sid); 118 status_reg = LLCC_TRP_STATUSn(sid);
113 119
@@ -143,6 +149,9 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
143 int ret; 149 int ret;
144 u32 act_ctrl_val; 150 u32 act_ctrl_val;
145 151
152 If (IS_ERR(drv_data))
153 return PTR_ERR(drv_data);
154
146 if (IS_ERR_OR_NULL(desc)) 155 if (IS_ERR_OR_NULL(desc))
147 return -EINVAL; 156 return -EINVAL;
148 157
@@ -180,6 +189,9 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc)
180 u32 act_ctrl_val; 189 u32 act_ctrl_val;
181 int ret; 190 int ret;
182 191
192 If (IS_ERR(drv_data))
193 return PTR_ERR(drv_data);
194
183 if (IS_ERR_OR_NULL(desc)) 195 if (IS_ERR_OR_NULL(desc))
184 return -EINVAL; 196 return -EINVAL;
185 197
@@ -289,6 +301,14 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
289 return ret; 301 return ret;
290} 302}
291 303
304int qcom_llcc_remove(struct platform_device *pdev)
305{
306 /* Set the global pointer to a error code to avoid referencing it */
307 drv_data = ERR_PTR(-ENODEV);
308 return 0;
309}
310EXPORT_SYMBOL_GPL(qcom_llcc_remove);
311
292int qcom_llcc_probe(struct platform_device *pdev, 312int qcom_llcc_probe(struct platform_device *pdev,
293 const struct llcc_slice_config *llcc_cfg, u32 sz) 313 const struct llcc_slice_config *llcc_cfg, u32 sz)
294{ 314{
@@ -300,35 +320,45 @@ int qcom_llcc_probe(struct platform_device *pdev,
300 struct platform_device *llcc_edac; 320 struct platform_device *llcc_edac;
301 321
302 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); 322 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
303 if (!drv_data) 323 if (!drv_data) {
304 return -ENOMEM; 324 ret = -ENOMEM;
325 goto err;
326 }
305 327
306 llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 328 llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
307 "llcc_base"); 329 "llcc_base");
308 llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res); 330 llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res);
309 if (IS_ERR(llcc_banks_base)) 331 if (IS_ERR(llcc_banks_base)) {
310 return PTR_ERR(llcc_banks_base); 332 ret = PTR_ERR(llcc_banks_base);
333 goto err;
334 }
311 335
312 drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base, 336 drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base,
313 &llcc_regmap_config); 337 &llcc_regmap_config);
314 if (IS_ERR(drv_data->regmap)) 338 if (IS_ERR(drv_data->regmap)) {
315 return PTR_ERR(drv_data->regmap); 339 ret = PTR_ERR(drv_data->regmap);
340 goto err;
341 }
316 342
317 llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 343 llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
318 "llcc_broadcast_base"); 344 "llcc_broadcast_base");
319 llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res); 345 llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res);
320 if (IS_ERR(llcc_bcast_base)) 346 if (IS_ERR(llcc_bcast_base)) {
321 return PTR_ERR(llcc_bcast_base); 347 ret = PTR_ERR(llcc_bcast_base);
348 goto err;
349 }
322 350
323 drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base, 351 drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base,
324 &llcc_regmap_config); 352 &llcc_regmap_config);
325 if (IS_ERR(drv_data->bcast_regmap)) 353 if (IS_ERR(drv_data->bcast_regmap)) {
326 return PTR_ERR(drv_data->bcast_regmap); 354 ret = PTR_ERR(drv_data->bcast_regmap);
355 goto err;
356 }
327 357
328 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, 358 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
329 &num_banks); 359 &num_banks);
330 if (ret) 360 if (ret)
331 return ret; 361 goto err;
332 362
333 num_banks &= LLCC_LB_CNT_MASK; 363 num_banks &= LLCC_LB_CNT_MASK;
334 num_banks >>= LLCC_LB_CNT_SHIFT; 364 num_banks >>= LLCC_LB_CNT_SHIFT;
@@ -340,8 +370,10 @@ int qcom_llcc_probe(struct platform_device *pdev,
340 370
341 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), 371 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
342 GFP_KERNEL); 372 GFP_KERNEL);
343 if (!drv_data->offsets) 373 if (!drv_data->offsets) {
344 return -ENOMEM; 374 ret = -ENOMEM;
375 goto err;
376 }
345 377
346 for (i = 0; i < num_banks; i++) 378 for (i = 0; i < num_banks; i++)
347 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; 379 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
@@ -349,8 +381,10 @@ int qcom_llcc_probe(struct platform_device *pdev,
349 drv_data->bitmap = devm_kcalloc(dev, 381 drv_data->bitmap = devm_kcalloc(dev,
350 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), 382 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
351 GFP_KERNEL); 383 GFP_KERNEL);
352 if (!drv_data->bitmap) 384 if (!drv_data->bitmap) {
353 return -ENOMEM; 385 ret = -ENOMEM;
386 goto err;
387 }
354 388
355 drv_data->cfg = llcc_cfg; 389 drv_data->cfg = llcc_cfg;
356 drv_data->cfg_size = sz; 390 drv_data->cfg_size = sz;
@@ -359,7 +393,7 @@ int qcom_llcc_probe(struct platform_device *pdev,
359 393
360 ret = qcom_llcc_cfg_program(pdev); 394 ret = qcom_llcc_cfg_program(pdev);
361 if (ret) 395 if (ret)
362 return ret; 396 goto err;
363 397
364 drv_data->ecc_irq = platform_get_irq(pdev, 0); 398 drv_data->ecc_irq = platform_get_irq(pdev, 0);
365 if (drv_data->ecc_irq >= 0) { 399 if (drv_data->ecc_irq >= 0) {
@@ -370,6 +404,9 @@ int qcom_llcc_probe(struct platform_device *pdev,
370 dev_err(dev, "Failed to register llcc edac driver\n"); 404 dev_err(dev, "Failed to register llcc edac driver\n");
371 } 405 }
372 406
407 return 0;
408err:
409 drv_data = ERR_PTR(-ENODEV);
373 return ret; 410 return ret;
374} 411}
375EXPORT_SYMBOL_GPL(qcom_llcc_probe); 412EXPORT_SYMBOL_GPL(qcom_llcc_probe);
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
index 69c285b1c990..eb71a50b8afc 100644
--- a/include/linux/soc/qcom/llcc-qcom.h
+++ b/include/linux/soc/qcom/llcc-qcom.h
@@ -162,6 +162,12 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc);
162 */ 162 */
163int qcom_llcc_probe(struct platform_device *pdev, 163int qcom_llcc_probe(struct platform_device *pdev,
164 const struct llcc_slice_config *table, u32 sz); 164 const struct llcc_slice_config *table, u32 sz);
165
166/**
167 * qcom_llcc_remove - remove the sct table
168 * @pdev: Platform device pointer
169 */
170int qcom_llcc_remove(struct platform_device *pdev);
165#else 171#else
166static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) 172static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid)
167{ 173{