Commit ba4e0339 authored by KuoHsiang Chou's avatar KuoHsiang Chou Committed by Thomas Zimmermann

drm/ast: Fixed CVE for DP501

[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: default avatarKuoHsiang Chou <kuohsiang_chou@aspeedtech.com>
Reported-by: default avatarkernel test robot <lkp@intel.com>
Signed-off-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210421085859.17761-1-kuohsiang_chou@aspeedtech.com
parent 749da85a
...@@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) ...@@ -189,6 +189,9 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
u32 i, data; u32 i, data;
u32 boot_address; u32 boot_address;
if (ast->config_mode != ast_use_p2a)
return false;
data = ast_mindwm(ast, 0x1e6e2100) & 0x01; data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
if (data) { if (data) {
boot_address = get_fw_base(ast); boot_address = get_fw_base(ast);
...@@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev) ...@@ -207,6 +210,9 @@ static bool ast_launch_m68k(struct drm_device *dev)
u8 *fw_addr = NULL; u8 *fw_addr = NULL;
u8 jreg; u8 jreg;
if (ast->config_mode != ast_use_p2a)
return false;
data = ast_mindwm(ast, 0x1e6e2100) & 0x01; data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
if (!data) { if (!data) {
...@@ -271,18 +277,47 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) ...@@ -271,18 +277,47 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev)
struct ast_private *ast = to_ast_private(dev); struct ast_private *ast = to_ast_private(dev);
u32 boot_address, offset, data; u32 boot_address, offset, data;
u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; u8 linkcap[4], linkrate, linklanes, maxclk = 0xff;
u32 *plinkcap;
if (ast->config_mode == ast_use_p2a) {
boot_address = get_fw_base(ast); boot_address = get_fw_base(ast);
/* validate FW version */ /* validate FW version */
offset = 0xf000; offset = AST_DP501_GBL_VERSION;
data = ast_mindwm(ast, boot_address + offset); data = ast_mindwm(ast, boot_address + offset);
if ((data & 0xf0) != 0x10) /* version: 1x */ 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; return maxclk;
/* Read Link Capability */ /* Read Link Capability */
offset = 0xf014; offset = AST_DP501_LINKRATE;
*(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); plinkcap = (u32 *)linkcap;
*plinkcap = readl(ast->dp501_fw_buf + offset);
if (linkcap[2] == 0) { if (linkcap[2] == 0) {
linkrate = linkcap[0]; linkrate = linkcap[0];
linklanes = linkcap[1]; linklanes = linkcap[1];
...@@ -291,6 +326,7 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev) ...@@ -291,6 +326,7 @@ u8 ast_get_dp501_max_clk(struct drm_device *dev)
data = 0xff; data = 0xff;
maxclk = (u8)data; maxclk = (u8)data;
} }
}
return maxclk; return maxclk;
} }
...@@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) ...@@ -298,26 +334,57 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
{ {
struct ast_private *ast = to_ast_private(dev); struct ast_private *ast = to_ast_private(dev);
u32 i, boot_address, offset, data; u32 i, boot_address, offset, data;
u32 *pEDIDidx;
if (ast->config_mode == ast_use_p2a) {
boot_address = get_fw_base(ast); boot_address = get_fw_base(ast);
/* validate FW version */ /* validate FW version */
offset = 0xf000; offset = AST_DP501_GBL_VERSION;
data = ast_mindwm(ast, boot_address + offset); data = ast_mindwm(ast, boot_address + offset);
if ((data & 0xf0) != 0x10) if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1)
return false; return false;
/* validate PnP Monitor */ /* validate PnP Monitor */
offset = 0xf010; offset = AST_DP501_PNPMONITOR;
data = ast_mindwm(ast, boot_address + offset); data = ast_mindwm(ast, boot_address + offset);
if (!(data & 0x01)) if (!(data & AST_DP501_PNP_CONNECTED))
return false; return false;
/* Read EDID */ /* Read EDID */
offset = 0xf020; offset = AST_DP501_EDID_DATA;
for (i = 0; i < 128; i += 4) { for (i = 0; i < 128; i += 4) {
data = ast_mindwm(ast, boot_address + offset + i); data = ast_mindwm(ast, boot_address + offset + i);
*(u32 *)(ediddata + i) = data; 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; return true;
......
...@@ -150,6 +150,7 @@ struct ast_private { ...@@ -150,6 +150,7 @@ struct ast_private {
void __iomem *regs; void __iomem *regs;
void __iomem *ioregs; void __iomem *ioregs;
void __iomem *dp501_fw_buf;
enum ast_chip chip; enum ast_chip chip;
bool vga2_clone; bool vga2_clone;
...@@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast); ...@@ -325,6 +326,17 @@ int ast_mode_config_init(struct ast_private *ast);
#define AST_MM_ALIGN_SHIFT 4 #define AST_MM_ALIGN_SHIFT 4
#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) #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); int ast_mm_init(struct ast_private *ast);
/* ast post */ /* ast post */
......
...@@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) ...@@ -99,7 +99,7 @@ static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev)
if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) {
/* Double check it's actually working */ /* Double check it's actually working */
data = ast_read32(ast, 0xf004); data = ast_read32(ast, 0xf004);
if (data != 0xFFFFFFFF) { if ((data != 0xFFFFFFFF) && (data != 0x00)) {
/* P2A works, grab silicon revision */ /* P2A works, grab silicon revision */
ast->config_mode = ast_use_p2a; ast->config_mode = ast_use_p2a;
...@@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, ...@@ -411,6 +411,7 @@ struct ast_private *ast_device_create(const struct drm_driver *drv,
return ast; return ast;
dev = &ast->base; dev = &ast->base;
dev->pdev = pdev;
pci_set_drvdata(pdev, dev); pci_set_drvdata(pdev, dev);
ast->regs = pcim_iomap(pdev, 1, 0); ast->regs = pcim_iomap(pdev, 1, 0);
...@@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const struct drm_driver *drv, ...@@ -450,6 +451,14 @@ struct ast_private *ast_device_create(const struct drm_driver *drv,
if (ret) if (ret)
return ERR_PTR(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); ret = ast_mode_config_init(ast);
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment