media: rockchip: vpss: prevent NULL dereference in rkvpss runtime resume

- Validate vpss[i] and sw_base_addr before memset/memcpy operations
- Add bounds checking for dev_num to prevent array overflow
- Add error logging for invalid sw_base_addr during register restoration

This resolves kernel panic caused by accessing uninitialized sw_base_addr
during power management resume path. The issue occurred when restoring
VPSS hardware registers from suspend state.

Boot up on as below logs:

[    6.869681] lr : rkvpss_hw_runtime_resume+0x160/0x198
[    6.870141] sp : ffffffc00969b930
[    6.870444] x29: ffffffc00969b930 x28: ffffff80035e9800 x27: ffffff80020ad200
[    6.871088] x26: 0000000000000003 x25: ffffffc0091f4000 x24: ffffff8001ed40a8
[    6.871730] x23: ffffffc008cbf000 x22: ffffff8001953010 x21: ffffff8001ed40c8
[    6.872373] x20: 0000000000000002 x19: ffffff8001ed4080 x18: 0000000000000000
[    6.873018] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[    6.873652] x14: ffffff8001fb5025 x13: 0000000000000007 x12: ffffff800142c900
[    6.874284] x11: 0000000000000002 x10: 00000007305dcf59 x9 : 0000000000000000
[    6.874926] x8 : 0000018000000000 x7 : 0000000000000000 x6 : 000000000000003f
[    6.875567] x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004
[    6.876208] x2 : 0000000000006f40 x1 : 0000000000000000 x0 : 0000018000000000
[    6.876851] Call trace:
[    6.877076]  __memset+0x16c/0x188
[    6.877375]  pm_generic_runtime_resume+0x24/0x34
[    6.877787]  __rpm_callback+0x40/0x11c
[    6.878129]  rpm_callback+0x68/0x6c
[    6.878448]  rpm_resume+0x304/0x37c
[    6.878767]  __pm_runtime_resume+0x50/0x74
[    6.879132]  ofl_open+0x4c/0xc8
[    6.879419]  v4l2_open+0x98/0xec
[    6.879714]  chrdev_open+0x158/0x164
[    6.880048]  do_dentry_open+0x2d4/0x350

Change-Id: Iab179899258189d7bdf9ccc9bb13d07c25d80910
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
Signed-off-by: Cai YiWei <cyw@rock-chips.com>
This commit is contained in:
Caesar Wang
2025-03-25 10:16:40 +08:00
committed by Tao Huang
parent ae4ced56fe
commit 39d1fb94fa
9 changed files with 35 additions and 10 deletions

View File

@@ -165,3 +165,13 @@ int rkvpss_attach_hw(struct rkvpss_device *vpss)
return 0;
}
void rkvpss_detach_hw(struct rkvpss_device *vpss)
{
struct rkvpss_hw_dev *hw = vpss->hw_dev;
if (hw) {
hw->vpss[vpss->dev_id] = NULL;
vpss->hw_dev = NULL;
}
}

View File

@@ -123,5 +123,6 @@ void rkvpss_unite_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask);
void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end);
int rkvpss_attach_hw(struct rkvpss_device *vpss);
void rkvpss_detach_hw(struct rkvpss_device *vpss);
void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate);
#endif

View File

@@ -307,7 +307,7 @@ static int rkvpss_plat_probe(struct platform_device *pdev)
ret = v4l2_device_register(vpss_dev->dev, v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "register v4l2 device failed:%d\n", ret);
return ret;
goto err_detach;
}
media_device_init(&vpss_dev->media_dev);
ret = media_device_register(&vpss_dev->media_dev);
@@ -324,16 +324,18 @@ static int rkvpss_plat_probe(struct platform_device *pdev)
atomic_set(&vpss_dev->pipe_stream_cnt, 0);
rkvpss_proc_init(vpss_dev);
pm_runtime_enable(&pdev->dev);
vpss_dev->is_probe_end = true;
init_waitqueue_head(&vpss_dev->stop_done);
vpss_dev->is_suspend = false;
vpss_dev->is_idle = true;
vpss_dev->is_probe_end = true;
return 0;
err_unreg_media_dev:
media_device_unregister(&vpss_dev->media_dev);
err_unreg_v4l2_dev:
v4l2_device_unregister(&vpss_dev->v4l2_dev);
err_detach:
rkvpss_detach_hw(vpss_dev);
return ret;
}
@@ -353,6 +355,7 @@ static int rkvpss_plat_remove(struct platform_device *pdev)
media_device_unregister(&vpss_dev->media_dev);
v4l2_device_unregister(&vpss_dev->v4l2_dev);
mutex_destroy(&vpss_dev->apilock);
rkvpss_detach_hw(vpss_dev);
return 0;
}

View File

@@ -854,7 +854,6 @@ static int rkvpss_hw_probe(struct platform_device *pdev)
hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL);
if (!hw_dev)
return -ENOMEM;
hw_dev->sw_reg = devm_kzalloc(dev, RKVPSS_SW_REG_SIZE, GFP_KERNEL);
if (!hw_dev->sw_reg)
return -ENOMEM;
@@ -950,7 +949,7 @@ static int rkvpss_hw_probe(struct platform_device *pdev)
rkvpss_register_offline(hw_dev);
pm_runtime_enable(&pdev->dev);
hw_dev->is_probe_end = true;
return 0;
err:
return ret;
@@ -1023,10 +1022,16 @@ static int __maybe_unused rkvpss_hw_runtime_resume(struct device *dev)
if (dev->power.runtime_status) {
for (i = 0; i < hw_dev->dev_num; i++) {
void *buf = hw_dev->vpss[i]->sw_base_addr;
struct rkvpss_device *vpss = hw_dev->vpss[i];
memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX);
memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE);
if (!vpss || !vpss->is_probe_end)
continue;
if (vpss->sw_base_addr) {
void *buf = vpss->sw_base_addr;
memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX);
memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE);
}
}
hw_dev->is_first = true;
} else {

View File

@@ -69,6 +69,7 @@ struct rkvpss_hw_dev {
bool is_shutdown;
bool is_suspend;
bool is_first;
bool is_probe_end;
};
#ifdef CONFIG_VIDEO_ROCKCHIP_VPSS_V10

View File

@@ -2075,7 +2075,8 @@ static int rkvpss_fh_open(struct file *file)
struct rkvpss_device *dev = stream->dev;
int ret;
if (!dev->is_probe_end)
if (!dev || !dev->is_probe_end ||
!dev->hw_dev || !dev->hw_dev->is_probe_end)
return -EINVAL;
ret = v4l2_fh_open(file);

View File

@@ -2221,7 +2221,8 @@ static int rkvpss_fh_open(struct file *file)
struct rkvpss_device *dev = stream->dev;
int ret;
if (!dev->is_probe_end)
if (!dev || !dev->is_probe_end ||
!dev->hw_dev || !dev->hw_dev->is_probe_end)
return -EINVAL;
ret = v4l2_fh_open(file);

View File

@@ -2161,6 +2161,9 @@ static int ofl_open(struct file *file)
struct rkvpss_offline_dev *ofl = video_drvdata(file);
int ret;
if (!ofl || !ofl->hw || !ofl->hw->is_probe_end)
return -EINVAL;
ret = v4l2_fh_open(file);
if (ret)
goto end;

View File

@@ -2548,7 +2548,7 @@ static int ofl_open(struct file *file)
struct rkvpss_offline_dev *ofl = video_drvdata(file);
int ret;
if (!ofl || !ofl->hw) {
if (!ofl || !ofl->hw || !ofl->hw->is_probe_end) {
ret = -ENODEV;
goto end;
}