Message ID | 20210421085859.17761-1-kuohsiang_chou@aspeedtech.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v5] drm/ast: Fixed CVE for DP501 | expand |
Hi Am 21.04.21 um 10:58 schrieb KuoHsiang Chou: > [Bug][DP501] > If ASPEED P2A (PCI to AHB) bridge is disabled and disallowed for > CVE_2019_6260 item3, and then the monitor's EDID is unable read through > Parade DP501. > The reason is the DP501's FW is mapped to BMC addressing space rather > than Host addressing space. > The resolution is that using "pci_iomap_range()" maps to DP501's FW that > stored on the end of FB (Frame Buffer). > In this case, FrameBuffer reserves the last 2MB used for the image of > DP501. > Your patches are missing a short changelog, so that reviewers can see what changed between versions. Anyway, I merged your patch into drm-misc-next now. Thanks for the fix. More generally speaking, the DP501 code needs a major refactoring. It's currently bolted onto the regular VGA connector code. It should rather be a separate connector or a DRM bridge. I always wanted to work on this, but don't have a device for testing. If I'd provide patches, would you be in a position to test them? Best regards Thomas > Signed-off-by: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> > Reported-by: kernel test robot <lkp@intel.com> > --- > drivers/gpu/drm/ast/ast_dp501.c | 139 +++++++++++++++++++++++--------- > drivers/gpu/drm/ast/ast_drv.h | 12 +++ > drivers/gpu/drm/ast/ast_main.c | 11 ++- > 3 files changed, 125 insertions(+), 37 deletions(-) > > diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c > index 88121c0e0..cd93c44f2 100644 > --- a/drivers/gpu/drm/ast/ast_dp501.c > +++ b/drivers/gpu/drm/ast/ast_dp501.c > @@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) > u32 i, data; > u32 boot_address; > > + if (ast->config_mode != ast_use_p2a) > + return false; > + > data = ast_mindwm(ast, 0x1e6e2100) & 0x01; > if (data) { > boot_address = get_fw_base(ast); > @@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev) > u8 *fw_addr = NULL; > u8 jreg; > > + if (ast->config_mode != ast_use_p2a) > + return false; > + > data = ast_mindwm(ast, 0x1e6e2100) & 0x01; > if (!data) { > > @@ -271,25 +277,55 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) > struct ast_private *ast = to_ast_private(dev); > u32 boot_address, offset, data; > u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; > + u32 *plinkcap; > > - boot_address = get_fw_base(ast); > - > - /* validate FW version */ > - offset = 0xf000; > - data = ast_mindwm(ast, boot_address + offset); > - if ((data & 0xf0) != 0x10) /* version: 1x */ > - return maxclk; > - > - /* Read Link Capability */ > - offset = 0xf014; > - *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); > - if (linkcap[2] == 0) { > - linkrate = linkcap[0]; > - linklanes = linkcap[1]; > - data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > - if (data > 0xff) > - data = 0xff; > - maxclk = (u8)data; > + if (ast->config_mode == ast_use_p2a) { > + boot_address = get_fw_base(ast); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = ast_mindwm(ast, boot_address + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ > + return maxclk; > + > + /* Read Link Capability */ > + offset = AST_DP501_LINKRATE; > + plinkcap = (u32 *)linkcap; > + *plinkcap = ast_mindwm(ast, boot_address + offset); > + if (linkcap[2] == 0) { > + linkrate = linkcap[0]; > + linklanes = linkcap[1]; > + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > + if (data > 0xff) > + data = 0xff; > + maxclk = (u8)data; > + } > + } else { > + if (!ast->dp501_fw_buf) > + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ > + > + /* dummy read */ > + offset = 0x0000; > + data = readl(ast->dp501_fw_buf + offset); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = readl(ast->dp501_fw_buf + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ > + return maxclk; > + > + /* Read Link Capability */ > + offset = AST_DP501_LINKRATE; > + plinkcap = (u32 *)linkcap; > + *plinkcap = readl(ast->dp501_fw_buf + offset); > + if (linkcap[2] == 0) { > + linkrate = linkcap[0]; > + linklanes = linkcap[1]; > + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > + if (data > 0xff) > + data = 0xff; > + maxclk = (u8)data; > + } > } > return maxclk; > } > @@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) > { > struct ast_private *ast = to_ast_private(dev); > u32 i, boot_address, offset, data; > + u32 *pEDIDidx; > > - boot_address = get_fw_base(ast); > - > - /* validate FW version */ > - offset = 0xf000; > - data = ast_mindwm(ast, boot_address + offset); > - if ((data & 0xf0) != 0x10) > - return false; > - > - /* validate PnP Monitor */ > - offset = 0xf010; > - data = ast_mindwm(ast, boot_address + offset); > - if (!(data & 0x01)) > - return false; > + if (ast->config_mode == ast_use_p2a) { > + boot_address = get_fw_base(ast); > > - /* Read EDID */ > - offset = 0xf020; > - for (i = 0; i < 128; i += 4) { > - data = ast_mindwm(ast, boot_address + offset + i); > - *(u32 *)(ediddata + i) = data; > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = ast_mindwm(ast, boot_address + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) > + return false; > + > + /* validate PnP Monitor */ > + offset = AST_DP501_PNPMONITOR; > + data = ast_mindwm(ast, boot_address + offset); > + if (!(data & AST_DP501_PNP_CONNECTED)) > + return false; > + > + /* Read EDID */ > + offset = AST_DP501_EDID_DATA; > + for (i = 0; i < 128; i += 4) { > + data = ast_mindwm(ast, boot_address + offset + i); > + pEDIDidx = (u32 *)(ediddata + i); > + *pEDIDidx = data; > + } > + } else { > + if (!ast->dp501_fw_buf) > + return false; > + > + /* dummy read */ > + offset = 0x0000; > + data = readl(ast->dp501_fw_buf + offset); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = readl(ast->dp501_fw_buf + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) > + return false; > + > + /* validate PnP Monitor */ > + offset = AST_DP501_PNPMONITOR; > + data = readl(ast->dp501_fw_buf + offset); > + if (!(data & AST_DP501_PNP_CONNECTED)) > + return false; > + > + /* Read EDID */ > + offset = AST_DP501_EDID_DATA; > + for (i = 0; i < 128; i += 4) { > + data = readl(ast->dp501_fw_buf + offset + i); > + pEDIDidx = (u32 *)(ediddata + i); > + *pEDIDidx = data; > + } > } > > return true; > diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h > index e82ab8628..911f9f414 100644 > --- a/drivers/gpu/drm/ast/ast_drv.h > +++ b/drivers/gpu/drm/ast/ast_drv.h > @@ -150,6 +150,7 @@ struct ast_private { > > void __iomem *regs; > void __iomem *ioregs; > + void __iomem *dp501_fw_buf; > > enum ast_chip chip; > bool vga2_clone; > @@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); > #define AST_MM_ALIGN_SHIFT 4 > #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) > > +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) > +#define AST_DP501_FW_VERSION_1 BIT(4) > +#define AST_DP501_PNP_CONNECTED BIT(1) > + > +#define AST_DP501_DEFAULT_DCLK 65 > + > +#define AST_DP501_GBL_VERSION 0xf000 > +#define AST_DP501_PNPMONITOR 0xf010 > +#define AST_DP501_LINKRATE 0xf014 > +#define AST_DP501_EDID_DATA 0xf020 > + > int ast_mm_init(struct ast_private *ast); > > /* ast post */ > diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c > index 0ac3c2039..3976a2587 100644 > --- a/drivers/gpu/drm/ast/ast_main.c > +++ b/drivers/gpu/drm/ast/ast_main.c > @@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) > if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { > /* Double check it's actually working */ > data = ast_read32(ast, 0xf004); > - if (data != 0xFFFFFFFF) { > + if ((data != 0xFFFFFFFF) && (data != 0x00)) { > /* P2A works, grab silicon revision */ > ast->config_mode = ast_use_p2a; > > @@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, > return ast; > dev = &ast->base; > > + dev->pdev = pdev; > pci_set_drvdata(pdev, dev); > > ast->regs = pci_iomap(pdev, 1, 0); > @@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, > if (ret) > return ERR_PTR(ret); > > + /* map reserved buffer */ > + ast->dp501_fw_buf = NULL; > + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { > + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); > + if (!ast->dp501_fw_buf) > + drm_info(dev, "failed to map reserved buffer!\n"); > + } > + > ret = ast_mode_config_init(ast); > if (ret) > return ERR_PTR(ret); > -- > 2.18.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel >
-----Original Message----- From: Thomas Zimmermann [mailto:tzimmermann@suse.de] Sent: Tuesday, April 27, 2021 7:02 PM To: Kuo-Hsiang Chou <kuohsiang_chou@aspeedtech.com>; dri-devel@lists.freedesktop.org; linux-kernel@vger.kernel.org Subject: Re: [PATCH v5] drm/ast: Fixed CVE for DP501 Hi Thomas, Hi Am 21.04.21 um 10:58 schrieb KuoHsiang Chou: > [Bug][DP501] > If ASPEED P2A (PCI to AHB) bridge is disabled and disallowed for > CVE_2019_6260 item3, and then the monitor's EDID is unable read > through Parade DP501. > The reason is the DP501's FW is mapped to BMC addressing space rather > than Host addressing space. > The resolution is that using "pci_iomap_range()" maps to DP501's FW > that stored on the end of FB (Frame Buffer). > In this case, FrameBuffer reserves the last 2MB used for the image of > DP501. > Your patches are missing a short changelog, so that reviewers can see what changed between versions. Anyway, I merged your patch into drm-misc-next now. Thanks for the fix. Thanks, I must keep this rule about the short changelog. More generally speaking, the DP501 code needs a major refactoring. It's currently bolted onto the regular VGA connector code. It should rather be a separate connector or a DRM bridge. I always wanted to work on this, but don't have a device for testing. If I'd provide patches, would you be in a position to test them? NO, I can't. The patch was verified on AST2500+DP501 before, so the correctness of this patch is promised. But customer always requested to send the platform back after bug fixed. Now, no DP501 platform on my hand, but I try to convince custom to get the someone platform. Best Regards, Kuo-Hsiang Chou Best regards Thomas > Signed-off-by: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> > Reported-by: kernel test robot <lkp@intel.com> > --- > drivers/gpu/drm/ast/ast_dp501.c | 139 +++++++++++++++++++++++--------- > drivers/gpu/drm/ast/ast_drv.h | 12 +++ > drivers/gpu/drm/ast/ast_main.c | 11 ++- > 3 files changed, 125 insertions(+), 37 deletions(-) > > diff --git a/drivers/gpu/drm/ast/ast_dp501.c > b/drivers/gpu/drm/ast/ast_dp501.c index 88121c0e0..cd93c44f2 100644 > --- a/drivers/gpu/drm/ast/ast_dp501.c > +++ b/drivers/gpu/drm/ast/ast_dp501.c > @@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) > u32 i, data; > u32 boot_address; > > + if (ast->config_mode != ast_use_p2a) > + return false; > + > data = ast_mindwm(ast, 0x1e6e2100) & 0x01; > if (data) { > boot_address = get_fw_base(ast); > @@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev) > u8 *fw_addr = NULL; > u8 jreg; > > + if (ast->config_mode != ast_use_p2a) > + return false; > + > data = ast_mindwm(ast, 0x1e6e2100) & 0x01; > if (!data) { > > @@ -271,25 +277,55 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) > struct ast_private *ast = to_ast_private(dev); > u32 boot_address, offset, data; > u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; > + u32 *plinkcap; > > - boot_address = get_fw_base(ast); > - > - /* validate FW version */ > - offset = 0xf000; > - data = ast_mindwm(ast, boot_address + offset); > - if ((data & 0xf0) != 0x10) /* version: 1x */ > - return maxclk; > - > - /* Read Link Capability */ > - offset = 0xf014; > - *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); > - if (linkcap[2] == 0) { > - linkrate = linkcap[0]; > - linklanes = linkcap[1]; > - data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > - if (data > 0xff) > - data = 0xff; > - maxclk = (u8)data; > + if (ast->config_mode == ast_use_p2a) { > + boot_address = get_fw_base(ast); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = ast_mindwm(ast, boot_address + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ > + return maxclk; > + > + /* Read Link Capability */ > + offset = AST_DP501_LINKRATE; > + plinkcap = (u32 *)linkcap; > + *plinkcap = ast_mindwm(ast, boot_address + offset); > + if (linkcap[2] == 0) { > + linkrate = linkcap[0]; > + linklanes = linkcap[1]; > + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > + if (data > 0xff) > + data = 0xff; > + maxclk = (u8)data; > + } > + } else { > + if (!ast->dp501_fw_buf) > + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ > + > + /* dummy read */ > + offset = 0x0000; > + data = readl(ast->dp501_fw_buf + offset); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = readl(ast->dp501_fw_buf + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ > + return maxclk; > + > + /* Read Link Capability */ > + offset = AST_DP501_LINKRATE; > + plinkcap = (u32 *)linkcap; > + *plinkcap = readl(ast->dp501_fw_buf + offset); > + if (linkcap[2] == 0) { > + linkrate = linkcap[0]; > + linklanes = linkcap[1]; > + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); > + if (data > 0xff) > + data = 0xff; > + maxclk = (u8)data; > + } > } > return maxclk; > } > @@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) > { > struct ast_private *ast = to_ast_private(dev); > u32 i, boot_address, offset, data; > + u32 *pEDIDidx; > > - boot_address = get_fw_base(ast); > - > - /* validate FW version */ > - offset = 0xf000; > - data = ast_mindwm(ast, boot_address + offset); > - if ((data & 0xf0) != 0x10) > - return false; > - > - /* validate PnP Monitor */ > - offset = 0xf010; > - data = ast_mindwm(ast, boot_address + offset); > - if (!(data & 0x01)) > - return false; > + if (ast->config_mode == ast_use_p2a) { > + boot_address = get_fw_base(ast); > > - /* Read EDID */ > - offset = 0xf020; > - for (i = 0; i < 128; i += 4) { > - data = ast_mindwm(ast, boot_address + offset + i); > - *(u32 *)(ediddata + i) = data; > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = ast_mindwm(ast, boot_address + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) > + return false; > + > + /* validate PnP Monitor */ > + offset = AST_DP501_PNPMONITOR; > + data = ast_mindwm(ast, boot_address + offset); > + if (!(data & AST_DP501_PNP_CONNECTED)) > + return false; > + > + /* Read EDID */ > + offset = AST_DP501_EDID_DATA; > + for (i = 0; i < 128; i += 4) { > + data = ast_mindwm(ast, boot_address + offset + i); > + pEDIDidx = (u32 *)(ediddata + i); > + *pEDIDidx = data; > + } > + } else { > + if (!ast->dp501_fw_buf) > + return false; > + > + /* dummy read */ > + offset = 0x0000; > + data = readl(ast->dp501_fw_buf + offset); > + > + /* validate FW version */ > + offset = AST_DP501_GBL_VERSION; > + data = readl(ast->dp501_fw_buf + offset); > + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) > + return false; > + > + /* validate PnP Monitor */ > + offset = AST_DP501_PNPMONITOR; > + data = readl(ast->dp501_fw_buf + offset); > + if (!(data & AST_DP501_PNP_CONNECTED)) > + return false; > + > + /* Read EDID */ > + offset = AST_DP501_EDID_DATA; > + for (i = 0; i < 128; i += 4) { > + data = readl(ast->dp501_fw_buf + offset + i); > + pEDIDidx = (u32 *)(ediddata + i); > + *pEDIDidx = data; > + } > } > > return true; > diff --git a/drivers/gpu/drm/ast/ast_drv.h > b/drivers/gpu/drm/ast/ast_drv.h index e82ab8628..911f9f414 100644 > --- a/drivers/gpu/drm/ast/ast_drv.h > +++ b/drivers/gpu/drm/ast/ast_drv.h > @@ -150,6 +150,7 @@ struct ast_private { > > void __iomem *regs; > void __iomem *ioregs; > + void __iomem *dp501_fw_buf; > > enum ast_chip chip; > bool vga2_clone; > @@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); > #define AST_MM_ALIGN_SHIFT 4 > #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) > > +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) > +#define AST_DP501_FW_VERSION_1 BIT(4) > +#define AST_DP501_PNP_CONNECTED BIT(1) > + > +#define AST_DP501_DEFAULT_DCLK 65 > + > +#define AST_DP501_GBL_VERSION 0xf000 > +#define AST_DP501_PNPMONITOR 0xf010 > +#define AST_DP501_LINKRATE 0xf014 > +#define AST_DP501_EDID_DATA 0xf020 > + > int ast_mm_init(struct ast_private *ast); > > /* ast post */ > diff --git a/drivers/gpu/drm/ast/ast_main.c > b/drivers/gpu/drm/ast/ast_main.c index 0ac3c2039..3976a2587 100644 > --- a/drivers/gpu/drm/ast/ast_main.c > +++ b/drivers/gpu/drm/ast/ast_main.c > @@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) > if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { > /* Double check it's actually working */ > data = ast_read32(ast, 0xf004); > - if (data != 0xFFFFFFFF) { > + if ((data != 0xFFFFFFFF) && (data != 0x00)) { > /* P2A works, grab silicon revision */ > ast->config_mode = ast_use_p2a; > > @@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, > return ast; > dev = &ast->base; > > + dev->pdev = pdev; > pci_set_drvdata(pdev, dev); > > ast->regs = pci_iomap(pdev, 1, 0); > @@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const > struct drm_driver *drv, > if (ret) > return ERR_PTR(ret); > > + /* map reserved buffer */ > + ast->dp501_fw_buf = NULL; > + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { > + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); > + if (!ast->dp501_fw_buf) > + drm_info(dev, "failed to map reserved buffer!\n"); > + } > + > ret = ast_mode_config_init(ast); > if (ret) > return ERR_PTR(ret); > -- > 2.18.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel > -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
Hi Am 29.04.21 um 11:21 schrieb Kuo-Hsiang Chou: > More generally speaking, the DP501 code needs a major refactoring. It's currently bolted onto the regular VGA connector code. It should rather be a separate connector or a DRM bridge. I always wanted to work on this, but don't have a device for testing. If I'd provide patches, would you be in a position to test them? > > NO, I can't. The patch was verified on AST2500+DP501 before, so the correctness of this patch is promised. But customer always requested to send the platform back after bug fixed. Now, no DP501 platform on my hand, but I try to convince custom to get the someone platform. What's the hardware platform that your customer provides to you? I'd like to do more development for the DP501 code, but the hardware is hard to find. Best regards Thomas > > Best Regards, > Kuo-Hsiang Chou > > Best regards > Thomas > > >> Signed-off-by: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> >> Reported-by: kernel test robot <lkp@intel.com> >> --- >> drivers/gpu/drm/ast/ast_dp501.c | 139 +++++++++++++++++++++++--------- >> drivers/gpu/drm/ast/ast_drv.h | 12 +++ >> drivers/gpu/drm/ast/ast_main.c | 11 ++- >> 3 files changed, 125 insertions(+), 37 deletions(-) >> >> diff --git a/drivers/gpu/drm/ast/ast_dp501.c >> b/drivers/gpu/drm/ast/ast_dp501.c index 88121c0e0..cd93c44f2 100644 >> --- a/drivers/gpu/drm/ast/ast_dp501.c >> +++ b/drivers/gpu/drm/ast/ast_dp501.c >> @@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) >> u32 i, data; >> u32 boot_address; >> >> + if (ast->config_mode != ast_use_p2a) >> + return false; >> + >> data = ast_mindwm(ast, 0x1e6e2100) & 0x01; >> if (data) { >> boot_address = get_fw_base(ast); >> @@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev) >> u8 *fw_addr = NULL; >> u8 jreg; >> >> + if (ast->config_mode != ast_use_p2a) >> + return false; >> + >> data = ast_mindwm(ast, 0x1e6e2100) & 0x01; >> if (!data) { >> >> @@ -271,25 +277,55 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) >> struct ast_private *ast = to_ast_private(dev); >> u32 boot_address, offset, data; >> u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; >> + u32 *plinkcap; >> >> - boot_address = get_fw_base(ast); >> - >> - /* validate FW version */ >> - offset = 0xf000; >> - data = ast_mindwm(ast, boot_address + offset); >> - if ((data & 0xf0) != 0x10) /* version: 1x */ >> - return maxclk; >> - >> - /* Read Link Capability */ >> - offset = 0xf014; >> - *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); >> - if (linkcap[2] == 0) { >> - linkrate = linkcap[0]; >> - linklanes = linkcap[1]; >> - data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> - if (data > 0xff) >> - data = 0xff; >> - maxclk = (u8)data; >> + if (ast->config_mode == ast_use_p2a) { >> + boot_address = get_fw_base(ast); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = ast_mindwm(ast, boot_address + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ >> + return maxclk; >> + >> + /* Read Link Capability */ >> + offset = AST_DP501_LINKRATE; >> + plinkcap = (u32 *)linkcap; >> + *plinkcap = ast_mindwm(ast, boot_address + offset); >> + if (linkcap[2] == 0) { >> + linkrate = linkcap[0]; >> + linklanes = linkcap[1]; >> + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> + if (data > 0xff) >> + data = 0xff; >> + maxclk = (u8)data; >> + } >> + } else { >> + if (!ast->dp501_fw_buf) >> + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ >> + >> + /* dummy read */ >> + offset = 0x0000; >> + data = readl(ast->dp501_fw_buf + offset); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = readl(ast->dp501_fw_buf + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ >> + return maxclk; >> + >> + /* Read Link Capability */ >> + offset = AST_DP501_LINKRATE; >> + plinkcap = (u32 *)linkcap; >> + *plinkcap = readl(ast->dp501_fw_buf + offset); >> + if (linkcap[2] == 0) { >> + linkrate = linkcap[0]; >> + linklanes = linkcap[1]; >> + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> + if (data > 0xff) >> + data = 0xff; >> + maxclk = (u8)data; >> + } >> } >> return maxclk; >> } >> @@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) >> { >> struct ast_private *ast = to_ast_private(dev); >> u32 i, boot_address, offset, data; >> + u32 *pEDIDidx; >> >> - boot_address = get_fw_base(ast); >> - >> - /* validate FW version */ >> - offset = 0xf000; >> - data = ast_mindwm(ast, boot_address + offset); >> - if ((data & 0xf0) != 0x10) >> - return false; >> - >> - /* validate PnP Monitor */ >> - offset = 0xf010; >> - data = ast_mindwm(ast, boot_address + offset); >> - if (!(data & 0x01)) >> - return false; >> + if (ast->config_mode == ast_use_p2a) { >> + boot_address = get_fw_base(ast); >> >> - /* Read EDID */ >> - offset = 0xf020; >> - for (i = 0; i < 128; i += 4) { >> - data = ast_mindwm(ast, boot_address + offset + i); >> - *(u32 *)(ediddata + i) = data; >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = ast_mindwm(ast, boot_address + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) >> + return false; >> + >> + /* validate PnP Monitor */ >> + offset = AST_DP501_PNPMONITOR; >> + data = ast_mindwm(ast, boot_address + offset); >> + if (!(data & AST_DP501_PNP_CONNECTED)) >> + return false; >> + >> + /* Read EDID */ >> + offset = AST_DP501_EDID_DATA; >> + for (i = 0; i < 128; i += 4) { >> + data = ast_mindwm(ast, boot_address + offset + i); >> + pEDIDidx = (u32 *)(ediddata + i); >> + *pEDIDidx = data; >> + } >> + } else { >> + if (!ast->dp501_fw_buf) >> + return false; >> + >> + /* dummy read */ >> + offset = 0x0000; >> + data = readl(ast->dp501_fw_buf + offset); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = readl(ast->dp501_fw_buf + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) >> + return false; >> + >> + /* validate PnP Monitor */ >> + offset = AST_DP501_PNPMONITOR; >> + data = readl(ast->dp501_fw_buf + offset); >> + if (!(data & AST_DP501_PNP_CONNECTED)) >> + return false; >> + >> + /* Read EDID */ >> + offset = AST_DP501_EDID_DATA; >> + for (i = 0; i < 128; i += 4) { >> + data = readl(ast->dp501_fw_buf + offset + i); >> + pEDIDidx = (u32 *)(ediddata + i); >> + *pEDIDidx = data; >> + } >> } >> >> return true; >> diff --git a/drivers/gpu/drm/ast/ast_drv.h >> b/drivers/gpu/drm/ast/ast_drv.h index e82ab8628..911f9f414 100644 >> --- a/drivers/gpu/drm/ast/ast_drv.h >> +++ b/drivers/gpu/drm/ast/ast_drv.h >> @@ -150,6 +150,7 @@ struct ast_private { >> >> void __iomem *regs; >> void __iomem *ioregs; >> + void __iomem *dp501_fw_buf; >> >> enum ast_chip chip; >> bool vga2_clone; >> @@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); >> #define AST_MM_ALIGN_SHIFT 4 >> #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) >> >> +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) >> +#define AST_DP501_FW_VERSION_1 BIT(4) >> +#define AST_DP501_PNP_CONNECTED BIT(1) >> + >> +#define AST_DP501_DEFAULT_DCLK 65 >> + >> +#define AST_DP501_GBL_VERSION 0xf000 >> +#define AST_DP501_PNPMONITOR 0xf010 >> +#define AST_DP501_LINKRATE 0xf014 >> +#define AST_DP501_EDID_DATA 0xf020 >> + >> int ast_mm_init(struct ast_private *ast); >> >> /* ast post */ >> diff --git a/drivers/gpu/drm/ast/ast_main.c >> b/drivers/gpu/drm/ast/ast_main.c index 0ac3c2039..3976a2587 100644 >> --- a/drivers/gpu/drm/ast/ast_main.c >> +++ b/drivers/gpu/drm/ast/ast_main.c >> @@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) >> if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { >> /* Double check it's actually working */ >> data = ast_read32(ast, 0xf004); >> - if (data != 0xFFFFFFFF) { >> + if ((data != 0xFFFFFFFF) && (data != 0x00)) { >> /* P2A works, grab silicon revision */ >> ast->config_mode = ast_use_p2a; >> >> @@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, >> return ast; >> dev = &ast->base; >> >> + dev->pdev = pdev; >> pci_set_drvdata(pdev, dev); >> >> ast->regs = pci_iomap(pdev, 1, 0); >> @@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const >> struct > drm_driver *drv, >> if (ret) >> return ERR_PTR(ret); >> >> + /* map reserved buffer */ >> + ast->dp501_fw_buf = NULL; >> + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { >> + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); >> + if (!ast->dp501_fw_buf) >> + drm_info(dev, "failed to map reserved buffer!\n"); >> + } >> + >> ret = ast_mode_config_init(ast); >> if (ret) >> return ERR_PTR(ret); >> -- >> 2.18.4 >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/dri-devel >> > > -- > Thomas Zimmermann > Graphics Driver Developer > SUSE Software Solutions Germany GmbH > Maxfeldstr. 5, 90409 Nürnberg, Germany > (HRB 36809, AG Nürnberg) > Geschäftsführer: Felix Imendörffer > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel >
-----Original Message----- From: Thomas Zimmermann [mailto:tzimmermann@suse.de] Sent: Tuesday, August 03, 2021 4:58 PM To: Kuo-Hsiang Chou <kuohsiang_chou@aspeedtech.com>; dri-devel@lists.freedesktop.org; linux-kernel@vger.kernel.org Subject: Re: [PATCH v5] drm/ast: Fixed CVE for DP501 Hi Am 29.04.21 um 11:21 schrieb Kuo-Hsiang Chou: > More generally speaking, the DP501 code needs a major refactoring. It's currently bolted onto the regular VGA connector code. It should rather be a separate connector or a DRM bridge. I always wanted to work on this, but don't have a device for testing. If I'd provide patches, would you be in a position to test them? > > NO, I can't. The patch was verified on AST2500+DP501 before, so the correctness of this patch is promised. But customer always requested to send the platform back after bug fixed. Now, no DP501 platform on my hand, but I try to convince custom to get the someone platform. What's the hardware platform that your customer provides to you? I'd like to do more development for the DP501 code, but the hardware is hard to find. Hi Tomas The platform was a whole server platform borrowed from Lenovo, but Lenovo had get it back after issue fixed. The reason that DP501 hardware hard to find is the IC vendor, parade, isn't support it anymore. But ASPEED needs to maintain DP501 for some of customers who use AST2500 and DP501, though, IC vendor doesn't support it anymore. Please understand the condition that I can be the position to test DP501. Thanks very much! Regards, Kuo-Hsiang Chou Best regards Thomas > > Best Regards, > Kuo-Hsiang Chou > > Best regards > Thomas > > >> Signed-off-by: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> >> Reported-by: kernel test robot <lkp@intel.com> >> --- >> drivers/gpu/drm/ast/ast_dp501.c | 139 +++++++++++++++++++++++--------- >> drivers/gpu/drm/ast/ast_drv.h | 12 +++ >> drivers/gpu/drm/ast/ast_main.c | 11 ++- >> 3 files changed, 125 insertions(+), 37 deletions(-) >> >> diff --git a/drivers/gpu/drm/ast/ast_dp501.c >> b/drivers/gpu/drm/ast/ast_dp501.c index 88121c0e0..cd93c44f2 100644 >> --- a/drivers/gpu/drm/ast/ast_dp501.c >> +++ b/drivers/gpu/drm/ast/ast_dp501.c >> @@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) >> u32 i, data; >> u32 boot_address; >> >> + if (ast->config_mode != ast_use_p2a) >> + return false; >> + >> data = ast_mindwm(ast, 0x1e6e2100) & 0x01; >> if (data) { >> boot_address = get_fw_base(ast); @@ -207,6 +210,9 @@ static bool >> ast_launch_m68k(struct drm_device *dev) >> u8 *fw_addr = NULL; >> u8 jreg; >> >> + if (ast->config_mode != ast_use_p2a) >> + return false; >> + >> data = ast_mindwm(ast, 0x1e6e2100) & 0x01; >> if (!data) { >> >> @@ -271,25 +277,55 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) >> struct ast_private *ast = to_ast_private(dev); >> u32 boot_address, offset, data; >> u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; >> + u32 *plinkcap; >> >> - boot_address = get_fw_base(ast); >> - >> - /* validate FW version */ >> - offset = 0xf000; >> - data = ast_mindwm(ast, boot_address + offset); >> - if ((data & 0xf0) != 0x10) /* version: 1x */ >> - return maxclk; >> - >> - /* Read Link Capability */ >> - offset = 0xf014; >> - *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); >> - if (linkcap[2] == 0) { >> - linkrate = linkcap[0]; >> - linklanes = linkcap[1]; >> - data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> - if (data > 0xff) >> - data = 0xff; >> - maxclk = (u8)data; >> + if (ast->config_mode == ast_use_p2a) { >> + boot_address = get_fw_base(ast); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = ast_mindwm(ast, boot_address + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ >> + return maxclk; >> + >> + /* Read Link Capability */ >> + offset = AST_DP501_LINKRATE; >> + plinkcap = (u32 *)linkcap; >> + *plinkcap = ast_mindwm(ast, boot_address + offset); >> + if (linkcap[2] == 0) { >> + linkrate = linkcap[0]; >> + linklanes = linkcap[1]; >> + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> + if (data > 0xff) >> + data = 0xff; >> + maxclk = (u8)data; >> + } >> + } else { >> + if (!ast->dp501_fw_buf) >> + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ >> + >> + /* dummy read */ >> + offset = 0x0000; >> + data = readl(ast->dp501_fw_buf + offset); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = readl(ast->dp501_fw_buf + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ >> + return maxclk; >> + >> + /* Read Link Capability */ >> + offset = AST_DP501_LINKRATE; >> + plinkcap = (u32 *)linkcap; >> + *plinkcap = readl(ast->dp501_fw_buf + offset); >> + if (linkcap[2] == 0) { >> + linkrate = linkcap[0]; >> + linklanes = linkcap[1]; >> + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); >> + if (data > 0xff) >> + data = 0xff; >> + maxclk = (u8)data; >> + } >> } >> return maxclk; >> } >> @@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) >> { >> struct ast_private *ast = to_ast_private(dev); >> u32 i, boot_address, offset, data; >> + u32 *pEDIDidx; >> >> - boot_address = get_fw_base(ast); >> - >> - /* validate FW version */ >> - offset = 0xf000; >> - data = ast_mindwm(ast, boot_address + offset); >> - if ((data & 0xf0) != 0x10) >> - return false; >> - >> - /* validate PnP Monitor */ >> - offset = 0xf010; >> - data = ast_mindwm(ast, boot_address + offset); >> - if (!(data & 0x01)) >> - return false; >> + if (ast->config_mode == ast_use_p2a) { >> + boot_address = get_fw_base(ast); >> >> - /* Read EDID */ >> - offset = 0xf020; >> - for (i = 0; i < 128; i += 4) { >> - data = ast_mindwm(ast, boot_address + offset + i); >> - *(u32 *)(ediddata + i) = data; >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = ast_mindwm(ast, boot_address + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) >> + return false; >> + >> + /* validate PnP Monitor */ >> + offset = AST_DP501_PNPMONITOR; >> + data = ast_mindwm(ast, boot_address + offset); >> + if (!(data & AST_DP501_PNP_CONNECTED)) >> + return false; >> + >> + /* Read EDID */ >> + offset = AST_DP501_EDID_DATA; >> + for (i = 0; i < 128; i += 4) { >> + data = ast_mindwm(ast, boot_address + offset + i); >> + pEDIDidx = (u32 *)(ediddata + i); >> + *pEDIDidx = data; >> + } >> + } else { >> + if (!ast->dp501_fw_buf) >> + return false; >> + >> + /* dummy read */ >> + offset = 0x0000; >> + data = readl(ast->dp501_fw_buf + offset); >> + >> + /* validate FW version */ >> + offset = AST_DP501_GBL_VERSION; >> + data = readl(ast->dp501_fw_buf + offset); >> + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) >> + return false; >> + >> + /* validate PnP Monitor */ >> + offset = AST_DP501_PNPMONITOR; >> + data = readl(ast->dp501_fw_buf + offset); >> + if (!(data & AST_DP501_PNP_CONNECTED)) >> + return false; >> + >> + /* Read EDID */ >> + offset = AST_DP501_EDID_DATA; >> + for (i = 0; i < 128; i += 4) { >> + data = readl(ast->dp501_fw_buf + offset + i); >> + pEDIDidx = (u32 *)(ediddata + i); >> + *pEDIDidx = data; >> + } >> } >> >> return true; >> diff --git a/drivers/gpu/drm/ast/ast_drv.h >> b/drivers/gpu/drm/ast/ast_drv.h index e82ab8628..911f9f414 100644 >> --- a/drivers/gpu/drm/ast/ast_drv.h >> +++ b/drivers/gpu/drm/ast/ast_drv.h >> @@ -150,6 +150,7 @@ struct ast_private { >> >> void __iomem *regs; >> void __iomem *ioregs; >> + void __iomem *dp501_fw_buf; >> >> enum ast_chip chip; >> bool vga2_clone; >> @@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); >> #define AST_MM_ALIGN_SHIFT 4 >> #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) >> >> +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) >> +#define AST_DP501_FW_VERSION_1 BIT(4) >> +#define AST_DP501_PNP_CONNECTED BIT(1) >> + >> +#define AST_DP501_DEFAULT_DCLK 65 >> + >> +#define AST_DP501_GBL_VERSION 0xf000 >> +#define AST_DP501_PNPMONITOR 0xf010 >> +#define AST_DP501_LINKRATE 0xf014 >> +#define AST_DP501_EDID_DATA 0xf020 >> + >> int ast_mm_init(struct ast_private *ast); >> >> /* ast post */ >> diff --git a/drivers/gpu/drm/ast/ast_main.c >> b/drivers/gpu/drm/ast/ast_main.c index 0ac3c2039..3976a2587 100644 >> --- a/drivers/gpu/drm/ast/ast_main.c >> +++ b/drivers/gpu/drm/ast/ast_main.c >> @@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) >> if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { >> /* Double check it's actually working */ >> data = ast_read32(ast, 0xf004); >> - if (data != 0xFFFFFFFF) { >> + if ((data != 0xFFFFFFFF) && (data != 0x00)) { >> /* P2A works, grab silicon revision */ >> ast->config_mode = ast_use_p2a; >> >> @@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, >> return ast; >> dev = &ast->base; >> >> + dev->pdev = pdev; >> pci_set_drvdata(pdev, dev); >> >> ast->regs = pci_iomap(pdev, 1, 0); @@ -450,6 +451,14 @@ struct >> ast_private *ast_device_create(const struct > drm_driver *drv, >> if (ret) >> return ERR_PTR(ret); >> >> + /* map reserved buffer */ >> + ast->dp501_fw_buf = NULL; >> + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { >> + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); >> + if (!ast->dp501_fw_buf) >> + drm_info(dev, "failed to map reserved buffer!\n"); >> + } >> + >> ret = ast_mode_config_init(ast); >> if (ret) >> return ERR_PTR(ret); >> -- >> 2.18.4 >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/dri-devel >> > > -- > Thomas Zimmermann > Graphics Driver Developer > SUSE Software Solutions Germany GmbH > Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) > Geschäftsführer: Felix Imendörffer > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel > -- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 88121c0e0..cd93c44f2 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) u32 i, data; u32 boot_address; + if (ast->config_mode != ast_use_p2a) + return false; + data = ast_mindwm(ast, 0x1e6e2100) & 0x01; if (data) { boot_address = get_fw_base(ast); @@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev) u8 *fw_addr = NULL; u8 jreg; + if (ast->config_mode != ast_use_p2a) + return false; + data = ast_mindwm(ast, 0x1e6e2100) & 0x01; if (!data) { @@ -271,25 +277,55 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) struct ast_private *ast = to_ast_private(dev); u32 boot_address, offset, data; u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; + u32 *plinkcap; - boot_address = get_fw_base(ast); - - /* validate FW version */ - offset = 0xf000; - data = ast_mindwm(ast, boot_address + offset); - if ((data & 0xf0) != 0x10) /* version: 1x */ - return maxclk; - - /* Read Link Capability */ - offset = 0xf014; - *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); - if (linkcap[2] == 0) { - linkrate = linkcap[0]; - linklanes = linkcap[1]; - data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); - if (data > 0xff) - data = 0xff; - maxclk = (u8)data; + if (ast->config_mode == ast_use_p2a) { + boot_address = get_fw_base(ast); + + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = ast_mindwm(ast, boot_address + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ + return maxclk; + + /* Read Link Capability */ + offset = AST_DP501_LINKRATE; + plinkcap = (u32 *)linkcap; + *plinkcap = ast_mindwm(ast, boot_address + offset); + if (linkcap[2] == 0) { + linkrate = linkcap[0]; + linklanes = linkcap[1]; + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); + if (data > 0xff) + data = 0xff; + maxclk = (u8)data; + } + } else { + if (!ast->dp501_fw_buf) + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ + + /* dummy read */ + offset = 0x0000; + data = readl(ast->dp501_fw_buf + offset); + + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = readl(ast->dp501_fw_buf + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) /* version: 1x */ + return maxclk; + + /* Read Link Capability */ + offset = AST_DP501_LINKRATE; + plinkcap = (u32 *)linkcap; + *plinkcap = readl(ast->dp501_fw_buf + offset); + if (linkcap[2] == 0) { + linkrate = linkcap[0]; + linklanes = linkcap[1]; + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); + if (data > 0xff) + data = 0xff; + maxclk = (u8)data; + } } return maxclk; } @@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) { struct ast_private *ast = to_ast_private(dev); u32 i, boot_address, offset, data; + u32 *pEDIDidx; - boot_address = get_fw_base(ast); - - /* validate FW version */ - offset = 0xf000; - data = ast_mindwm(ast, boot_address + offset); - if ((data & 0xf0) != 0x10) - return false; - - /* validate PnP Monitor */ - offset = 0xf010; - data = ast_mindwm(ast, boot_address + offset); - if (!(data & 0x01)) - return false; + if (ast->config_mode == ast_use_p2a) { + boot_address = get_fw_base(ast); - /* Read EDID */ - offset = 0xf020; - for (i = 0; i < 128; i += 4) { - data = ast_mindwm(ast, boot_address + offset + i); - *(u32 *)(ediddata + i) = data; + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = ast_mindwm(ast, boot_address + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) + return false; + + /* validate PnP Monitor */ + offset = AST_DP501_PNPMONITOR; + data = ast_mindwm(ast, boot_address + offset); + if (!(data & AST_DP501_PNP_CONNECTED)) + return false; + + /* Read EDID */ + offset = AST_DP501_EDID_DATA; + for (i = 0; i < 128; i += 4) { + data = ast_mindwm(ast, boot_address + offset + i); + pEDIDidx = (u32 *)(ediddata + i); + *pEDIDidx = data; + } + } else { + if (!ast->dp501_fw_buf) + return false; + + /* dummy read */ + offset = 0x0000; + data = readl(ast->dp501_fw_buf + offset); + + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = readl(ast->dp501_fw_buf + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) + return false; + + /* validate PnP Monitor */ + offset = AST_DP501_PNPMONITOR; + data = readl(ast->dp501_fw_buf + offset); + if (!(data & AST_DP501_PNP_CONNECTED)) + return false; + + /* Read EDID */ + offset = AST_DP501_EDID_DATA; + for (i = 0; i < 128; i += 4) { + data = readl(ast->dp501_fw_buf + offset + i); + pEDIDidx = (u32 *)(ediddata + i); + *pEDIDidx = data; + } } return true; diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index e82ab8628..911f9f414 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -150,6 +150,7 @@ struct ast_private { void __iomem *regs; void __iomem *ioregs; + void __iomem *dp501_fw_buf; enum ast_chip chip; bool vga2_clone; @@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); #define AST_MM_ALIGN_SHIFT 4 #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) +#define AST_DP501_FW_VERSION_1 BIT(4) +#define AST_DP501_PNP_CONNECTED BIT(1) + +#define AST_DP501_DEFAULT_DCLK 65 + +#define AST_DP501_GBL_VERSION 0xf000 +#define AST_DP501_PNPMONITOR 0xf010 +#define AST_DP501_LINKRATE 0xf014 +#define AST_DP501_EDID_DATA 0xf020 + int ast_mm_init(struct ast_private *ast); /* ast post */ diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 0ac3c2039..3976a2587 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { /* Double check it's actually working */ data = ast_read32(ast, 0xf004); - if (data != 0xFFFFFFFF) { + if ((data != 0xFFFFFFFF) && (data != 0x00)) { /* P2A works, grab silicon revision */ ast->config_mode = ast_use_p2a; @@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, return ast; dev = &ast->base; + dev->pdev = pdev; pci_set_drvdata(pdev, dev); ast->regs = pci_iomap(pdev, 1, 0); @@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, if (ret) return ERR_PTR(ret); + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + ret = ast_mode_config_init(ast); if (ret) return ERR_PTR(ret);
[Bug][DP501] If ASPEED P2A (PCI to AHB) bridge is disabled and disallowed for CVE_2019_6260 item3, and then the monitor's EDID is unable read through Parade DP501. The reason is the DP501's FW is mapped to BMC addressing space rather than Host addressing space. The resolution is that using "pci_iomap_range()" maps to DP501's FW that stored on the end of FB (Frame Buffer). In this case, FrameBuffer reserves the last 2MB used for the image of DP501. Signed-off-by: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> Reported-by: kernel test robot <lkp@intel.com> --- drivers/gpu/drm/ast/ast_dp501.c | 139 +++++++++++++++++++++++--------- drivers/gpu/drm/ast/ast_drv.h | 12 +++ drivers/gpu/drm/ast/ast_main.c | 11 ++- 3 files changed, 125 insertions(+), 37 deletions(-) -- 2.18.4