From 84ec3f940289dfba9b6de531c9aac7f089fc6c8f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 01/13] sm501: Fix bounds checks We don't need to add width to pitch when calculating last point, that would reject valid ops within the card's local_mem. Fixes: b15a22bbcbe6a78dc3d88fe3134985e4cdd87de4 Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: ddb5781d12913bb9d6dbfd9e5b1e2b893e2b3e2d.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index a7fc08c52b..5ceee4166f 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -723,8 +723,8 @@ static void sm501_2d_operation(SM501State *s) dst_y -= height - 1; } - if (dst_base >= get_local_mem_size(s) || dst_base + - (dst_x + width + (dst_y + height) * (dst_pitch + width)) * + if (dst_base >= get_local_mem_size(s) || + dst_base + (dst_x + width + (dst_y + height) * dst_pitch) * (1 << format) >= get_local_mem_size(s)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op dest is outside vram.\n"); return; @@ -749,8 +749,8 @@ static void sm501_2d_operation(SM501State *s) src_y -= height - 1; } - if (src_base >= get_local_mem_size(s) || src_base + - (src_x + width + (src_y + height) * (src_pitch + width)) * + if (src_base >= get_local_mem_size(s) || + src_base + (src_x + width + (src_y + height) * src_pitch) * (1 << format) >= get_local_mem_size(s)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op src is outside vram.\n"); From 4decaad9d295c8598bbcba09c40d3fd4a115f1e8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 02/13] sm501: Drop unneded variable We don't need a separate variable to keep track if we allocated memory that needs to be freed as we can test the pointer itself. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: ff9136c3151a15cdfa1d9b7a68acf11cffb8efa4.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 5ceee4166f..5d2e019575 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -796,13 +796,12 @@ static void sm501_2d_operation(SM501State *s) de = db + width + height * (width + dst_pitch); if (rtl && ((db >= sb && db <= se) || (de >= sb && de <= se))) { /* regions may overlap: copy via temporary */ - int free_buf = 0, llb = width * (1 << format); + int llb = width * (1 << format); int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = tmp_buf; if (tmp_stride * sizeof(uint32_t) * height > sizeof(tmp_buf)) { tmp = g_malloc(tmp_stride * sizeof(uint32_t) * height); - free_buf = 1; } pixman_blt((uint32_t *)&s->local_mem[src_base], tmp, src_pitch * (1 << format) / sizeof(uint32_t), @@ -813,7 +812,7 @@ static void sm501_2d_operation(SM501State *s) dst_pitch * (1 << format) / sizeof(uint32_t), 8 * (1 << format), 8 * (1 << format), 0, 0, dst_x, dst_y, width, height); - if (free_buf) { + if (tmp != tmp_buf) { g_free(tmp); } } else { From 1cb62e3666b48ac4c6a22340165e21439919908f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 03/13] sm501: Ignore no-op blits Some guests seem to try source copy blits with same source and dest which are no-op so avoid calling pixman for these. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: a2a8214dd37344dfb65f1c343ace4cff2e94f3bb.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 5d2e019575..ad5a62bfab 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -788,6 +788,11 @@ static void sm501_2d_operation(SM501State *s) (rop2_source_is_pattern ? " with pattern source" : "")); } + /* Ignore no-op blits, some guests seem to do this */ + if (src_base == dst_base && src_pitch == dst_pitch && + src_x == dst_x && src_y == dst_y) { + break; + } /* Check for overlaps, this could be made more exact */ uint32_t sb, se, db, de; sb = src_base + src_x + src_y * (width + src_pitch); From 299778d5af207b298224d2c610324941b8561006 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 04/13] sm501: Introduce variable for commonly used value for better readability The bytes per pixel value can be calculated from format but it's used freqently enough (and will be used more in subseqent patches) so store it in a variable for better readabilty. Also drop some unneded 0x prefix around where new variable is defined. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: b9ea5ef2d68583db9f3fb73a2b859abbd7c044a8.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index ad5a62bfab..3ced2c42db 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -684,10 +684,11 @@ static void sm501_2d_operation(SM501State *s) { int cmd = (s->twoD_control >> 16) & 0x1F; int rtl = s->twoD_control & BIT(27); - int format = (s->twoD_stretch >> 20) & 0x3; - int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ + int format = (s->twoD_stretch >> 20) & 3; + int bypp = 1 << format; /* bytes per pixel */ + int rop_mode = (s->twoD_control >> 15) & 1; /* 1 for rop2, else rop3 */ /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ - int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; + int rop2_source_is_pattern = (s->twoD_control >> 14) & 1; int rop = s->twoD_control & 0xFF; unsigned int dst_x = (s->twoD_destination >> 16) & 0x01FFF; unsigned int dst_y = s->twoD_destination & 0xFFFF; @@ -724,8 +725,8 @@ static void sm501_2d_operation(SM501State *s) } if (dst_base >= get_local_mem_size(s) || - dst_base + (dst_x + width + (dst_y + height) * dst_pitch) * - (1 << format) >= get_local_mem_size(s)) { + dst_base + (dst_x + width + (dst_y + height) * dst_pitch) * bypp >= + get_local_mem_size(s)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op dest is outside vram.\n"); return; } @@ -750,8 +751,8 @@ static void sm501_2d_operation(SM501State *s) } if (src_base >= get_local_mem_size(s) || - src_base + (src_x + width + (src_y + height) * src_pitch) * - (1 << format) >= get_local_mem_size(s)) { + src_base + (src_x + width + (src_y + height) * src_pitch) * bypp >= + get_local_mem_size(s)) { qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op src is outside vram.\n"); return; @@ -763,8 +764,8 @@ static void sm501_2d_operation(SM501State *s) uint8_t *d = s->local_mem + dst_base; for (y = 0; y < height; y++) { - i = (dst_x + (dst_y + y) * dst_pitch) * (1 << format); - for (x = 0; x < width; x++, i += (1 << format)) { + i = (dst_x + (dst_y + y) * dst_pitch) * bypp; + for (x = 0; x < width; x++, i += bypp) { switch (format) { case 0: d[i] = ~d[i]; @@ -801,7 +802,7 @@ static void sm501_2d_operation(SM501State *s) de = db + width + height * (width + dst_pitch); if (rtl && ((db >= sb && db <= se) || (de >= sb && de <= se))) { /* regions may overlap: copy via temporary */ - int llb = width * (1 << format); + int llb = width * bypp; int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); uint32_t *tmp = tmp_buf; @@ -809,13 +810,13 @@ static void sm501_2d_operation(SM501State *s) tmp = g_malloc(tmp_stride * sizeof(uint32_t) * height); } pixman_blt((uint32_t *)&s->local_mem[src_base], tmp, - src_pitch * (1 << format) / sizeof(uint32_t), - tmp_stride, 8 * (1 << format), 8 * (1 << format), + src_pitch * bypp / sizeof(uint32_t), + tmp_stride, 8 * bypp, 8 * bypp, src_x, src_y, 0, 0, width, height); pixman_blt(tmp, (uint32_t *)&s->local_mem[dst_base], tmp_stride, - dst_pitch * (1 << format) / sizeof(uint32_t), - 8 * (1 << format), 8 * (1 << format), + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, 0, 0, dst_x, dst_y, width, height); if (tmp != tmp_buf) { g_free(tmp); @@ -823,9 +824,9 @@ static void sm501_2d_operation(SM501State *s) } else { pixman_blt((uint32_t *)&s->local_mem[src_base], (uint32_t *)&s->local_mem[dst_base], - src_pitch * (1 << format) / sizeof(uint32_t), - dst_pitch * (1 << format) / sizeof(uint32_t), - 8 * (1 << format), 8 * (1 << format), + src_pitch * bypp / sizeof(uint32_t), + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, 8 * bypp, src_x, src_y, dst_x, dst_y, width, height); } } @@ -842,8 +843,8 @@ static void sm501_2d_operation(SM501State *s) } pixman_fill((uint32_t *)&s->local_mem[dst_base], - dst_pitch * (1 << format) / sizeof(uint32_t), - 8 * (1 << format), dst_x, dst_y, width, height, color); + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, dst_x, dst_y, width, height, color); break; } default: @@ -855,7 +856,7 @@ static void sm501_2d_operation(SM501State *s) if (dst_base >= get_fb_addr(s, crt) && dst_base <= get_fb_addr(s, crt) + fb_len) { int dst_len = MIN(fb_len, ((dst_y + height - 1) * dst_pitch + - dst_x + width) * (1 << format)); + dst_x + width) * bypp); if (dst_len) { memory_region_set_dirty(&s->local_mem_region, dst_base, dst_len); } From c208085a3e979e1da23a59c397ad6ab56a29d7bf Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 05/13] sm501: Optimise 1 pixel 2d ops Some guests do 1x1 blits which is faster to do directly than calling a function for it so avoid overhead in this case. Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: 7cccc302d7b4c5c313bad7681ac4686417143c3e.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 3ced2c42db..2098e69810 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -794,6 +794,14 @@ static void sm501_2d_operation(SM501State *s) src_x == dst_x && src_y == dst_y) { break; } + /* Some clients also do 1 pixel blits, avoid overhead for these */ + if (width == 1 && height == 1) { + unsigned int si = (src_x + src_y * src_pitch) * bypp; + unsigned int di = (dst_x + dst_y * dst_pitch) * bypp; + stn_he_p(&s->local_mem[dst_base + di], bypp, + ldn_he_p(&s->local_mem[src_base + si], bypp)); + break; + } /* Check for overlaps, this could be made more exact */ uint32_t sb, se, db, de; sb = src_base + src_x + src_y * (width + src_pitch); @@ -842,9 +850,14 @@ static void sm501_2d_operation(SM501State *s) color = cpu_to_le16(color); } - pixman_fill((uint32_t *)&s->local_mem[dst_base], - dst_pitch * bypp / sizeof(uint32_t), - 8 * bypp, dst_x, dst_y, width, height, color); + if (width == 1 && height == 1) { + unsigned int i = (dst_x + dst_y * dst_pitch) * bypp; + stn_he_p(&s->local_mem[dst_base + i], bypp, color); + } else { + pixman_fill((uint32_t *)&s->local_mem[dst_base], + dst_pitch * bypp / sizeof(uint32_t), + 8 * bypp, dst_x, dst_y, width, height, color); + } break; } default: From ba27110fab0b7ba26ff6a36a7311481181dd83f8 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 06/13] sm501: Use stn_he_p/ldn_he_p instead of switch/case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of open coding op with different sizes using a switch and type casting it can be written more compactly using stn_he_p/ldn_he_p. Suggested-by: Peter Maydell Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-id: e2f649cb286f0735a10ec87c1b36a7ae081acb61.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 2098e69810..6349f31e64 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -766,17 +766,7 @@ static void sm501_2d_operation(SM501State *s) for (y = 0; y < height; y++) { i = (dst_x + (dst_y + y) * dst_pitch) * bypp; for (x = 0; x < width; x++, i += bypp) { - switch (format) { - case 0: - d[i] = ~d[i]; - break; - case 1: - *(uint16_t *)&d[i] = ~*(uint16_t *)&d[i]; - break; - case 2: - *(uint32_t *)&d[i] = ~*(uint32_t *)&d[i]; - break; - } + stn_he_p(&d[i], bypp, ~ldn_he_p(&d[i], bypp)); } } } else { From f018edc358669d42553f4a636b7611d05ab2198f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 07/13] sm501: Do not allow guest to set invalid format Prevent guest setting invalid format value that might trip checks in sm501_2d_operation(). Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: 26d4fa9b8ce81e2723e98d592ccba7550042752c.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 6349f31e64..7e4c042d52 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1503,6 +1503,9 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, s->twoD_background = value; break; case SM501_2D_STRETCH: + if (((value >> 20) & 3) == 3) { + value &= ~BIT(20); + } s->twoD_stretch = value; break; case SM501_2D_COLOR_COMPARE: From d8327a68694e49d7d125b5dbe4eeaaf9695cbb73 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sat, 20 Jun 2020 22:56:28 +0200 Subject: [PATCH 08/13] sm501: Convert debug printfs to traces Signed-off-by: BALATON Zoltan Reviewed-by: Peter Maydell Message-id: caf97bf0c84a440896ddf020e84c312fa5c15076.1592686588.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 50 +++++++++++------------------------------ hw/display/trace-events | 12 ++++++++++ 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 7e4c042d52..2db347dcbc 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -39,15 +39,7 @@ #include "qemu/range.h" #include "ui/pixel_ops.h" #include "qemu/bswap.h" - -/*#define DEBUG_SM501*/ -/*#define DEBUG_BITBLT*/ - -#ifdef DEBUG_SM501 -#define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define SM501_DPRINTF(fmt, ...) do {} while (0) -#endif +#include "trace.h" #define MMIO_BASE_OFFSET 0x3e00000 #define MMIO_SIZE 0x200000 @@ -871,7 +863,6 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, { SM501State *s = (SM501State *)opaque; uint32_t ret = 0; - SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); switch (addr) { case SM501_SYSTEM_CONTROL: @@ -923,7 +914,7 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, qemu_log_mask(LOG_UNIMP, "sm501: not implemented system config" "register read. addr=%" HWADDR_PRIx "\n", addr); } - + trace_sm501_system_config_read(addr, ret); return ret; } @@ -931,9 +922,8 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - (uint32_t)addr, (uint32_t)value); + trace_sm501_system_config_write((uint32_t)addr, (uint32_t)value); switch (addr) { case SM501_SYSTEM_CONTROL: s->system_control &= 0x10DB0000; @@ -1019,9 +1009,7 @@ static uint64_t sm501_i2c_read(void *opaque, hwaddr addr, unsigned size) qemu_log_mask(LOG_UNIMP, "sm501 i2c : not implemented register read." " addr=0x%" HWADDR_PRIx "\n", addr); } - - SM501_DPRINTF("sm501 i2c regs : read addr=%" HWADDR_PRIx " val=%x\n", - addr, ret); + trace_sm501_i2c_read((uint32_t)addr, ret); return ret; } @@ -1029,9 +1017,8 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 i2c regs : write addr=%" HWADDR_PRIx - " val=%" PRIx64 "\n", addr, value); + trace_sm501_i2c_write((uint32_t)addr, (uint32_t)value); switch (addr) { case SM501_I2C_BYTE_COUNT: s->i2c_byte_count = value & 0xf; @@ -1045,25 +1032,19 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); if (!res) { int i; - SM501_DPRINTF("sm501 i2c : transferring %d bytes to 0x%x\n", - s->i2c_byte_count + 1, s->i2c_addr >> 1); for (i = 0; i <= s->i2c_byte_count; i++) { res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i], !(s->i2c_addr & 1)); if (res) { - SM501_DPRINTF("sm501 i2c : transfer failed" - " i=%d, res=%d\n", i, res); s->i2c_status |= SM501_I2C_STATUS_ERROR; return; } } if (i) { - SM501_DPRINTF("sm501 i2c : transferred %d bytes\n", i); s->i2c_status = SM501_I2C_STATUS_COMPLETE; } } } else { - SM501_DPRINTF("sm501 i2c : end transfer\n"); i2c_end_transfer(s->i2c_bus); s->i2c_status &= ~SM501_I2C_STATUS_ERROR; } @@ -1103,7 +1084,8 @@ static const MemoryRegionOps sm501_i2c_ops = { static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); + + trace_sm501_palette_read((uint32_t)addr); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ @@ -1116,8 +1098,8 @@ static void sm501_palette_write(void *opaque, hwaddr addr, uint32_t value) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", - (int)addr, value); + + trace_sm501_palette_write((uint32_t)addr, value); /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ @@ -1132,7 +1114,6 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, { SM501State *s = (SM501State *)opaque; uint32_t ret = 0; - SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); switch (addr) { @@ -1237,7 +1218,7 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " "read. addr=%" HWADDR_PRIx "\n", addr); } - + trace_sm501_disp_ctrl_read((uint32_t)addr, ret); return ret; } @@ -1245,9 +1226,8 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); + trace_sm501_disp_ctrl_write((uint32_t)addr, (uint32_t)value); switch (addr) { case SM501_DC_PANEL_CONTROL: s->dc_panel_control = value & 0x0FFF73FF; @@ -1392,7 +1372,6 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, { SM501State *s = (SM501State *)opaque; uint32_t ret = 0; - SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); switch (addr) { case SM501_2D_SOURCE: @@ -1462,7 +1441,7 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " "read. addr=%" HWADDR_PRIx "\n", addr); } - + trace_sm501_2d_engine_read((uint32_t)addr, ret); return ret; } @@ -1470,9 +1449,8 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { SM501State *s = (SM501State *)opaque; - SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); + trace_sm501_2d_engine_write((uint32_t)addr, (uint32_t)value); switch (addr) { case SM501_2D_SOURCE: s->twoD_source = value; @@ -1830,8 +1808,6 @@ static void sm501_init(SM501State *s, DeviceState *dev, uint32_t local_mem_bytes) { s->local_mem_size_index = get_local_mem_size_index(local_mem_bytes); - SM501_DPRINTF("sm501 local mem size=%x. index=%d\n", get_local_mem_size(s), - s->local_mem_size_index); /* local memory */ memory_region_init_ram(&s->local_mem_region, OBJECT(dev), "sm501.local", diff --git a/hw/display/trace-events b/hw/display/trace-events index 72d4c9812c..970d6bac5d 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -161,3 +161,15 @@ cg3_write(uint32_t addr, uint32_t val, unsigned size) "write addr:0x%06"PRIx32" # dpcd.c dpcd_read(uint32_t addr, uint8_t val) "read addr:0x%"PRIx32" val:0x%02x" dpcd_write(uint32_t addr, uint8_t val) "write addr:0x%"PRIx32" val:0x%02x" + +# sm501.c +sm501_system_config_read(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_system_config_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_i2c_read(uint32_t addr, uint8_t val) "addr=0x%x, val=0x%x" +sm501_i2c_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_palette_read(uint32_t addr) "addr=0x%x" +sm501_palette_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_disp_ctrl_read(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_disp_ctrl_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_2d_engine_read(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" +sm501_2d_engine_write(uint32_t addr, uint32_t val) "addr=0x%x, val=0x%x" From 9982c605a71bffd4c52c111b5c79e2060953a762 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 24 Jun 2020 18:42:18 +0200 Subject: [PATCH 09/13] sm501: Fix and optimize overlap check When doing reverse blit we need to check if source and dest overlap but it is not trivial due to possible different base and pitch of source and dest. Do rectangle overlap if base and pitch match, otherwise just check if memory area containing the rects overlaps so rects could possibly overlap. Signed-off-by: BALATON Zoltan Message-Id: <20200624164737.A941374633D@zero.eik.bme.hu> Reviewed-by: Peter Maydell Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 2db347dcbc..9cccc68c35 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -690,6 +690,7 @@ static void sm501_2d_operation(SM501State *s) unsigned int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); + bool overlap = false; if ((s->twoD_stretch >> 16) & 0xF) { qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); @@ -784,16 +785,21 @@ static void sm501_2d_operation(SM501State *s) ldn_he_p(&s->local_mem[src_base + si], bypp)); break; } - /* Check for overlaps, this could be made more exact */ - uint32_t sb, se, db, de; - sb = src_base + src_x + src_y * (width + src_pitch); - se = sb + width + height * (width + src_pitch); - db = dst_base + dst_x + dst_y * (width + dst_pitch); - de = db + width + height * (width + dst_pitch); - if (rtl && ((db >= sb && db <= se) || (de >= sb && de <= se))) { - /* regions may overlap: copy via temporary */ - int llb = width * bypp; - int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); + /* If reverse blit do simple check for overlaps */ + if (rtl && src_base == dst_base && src_pitch == dst_pitch) { + overlap = (src_x < dst_x + width && src_x + width > dst_x && + src_y < dst_y + height && src_y + height > dst_y); + } else if (rtl) { + unsigned int sb, se, db, de; + sb = src_base + (src_x + src_y * src_pitch) * bypp; + se = sb + (width + (height - 1) * src_pitch) * bypp; + db = dst_base + (dst_x + dst_y * dst_pitch) * bypp; + de = db + (width + (height - 1) * dst_pitch) * bypp; + overlap = (db < se && sb < de); + } + if (overlap) { + /* pixman can't do reverse blit: copy via temporary */ + int tmp_stride = DIV_ROUND_UP(width * bypp, sizeof(uint32_t)); uint32_t *tmp = tmp_buf; if (tmp_stride * sizeof(uint32_t) * height > sizeof(tmp_buf)) { From d634c883ca07c28da2cb84c019659694f05d8b3a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 21 Jun 2020 13:12:38 +0200 Subject: [PATCH 10/13] ati-vga: Support unaligned access to hardware cursor registers This fixes horizontal mouse movement and pointer color with MacOS that writes these registers with access size less than 4 so previously only the last portion of access was effective overwriting previous partial writes. Signed-off-by: BALATON Zoltan Message-id: ba1d5ba97f246e8807f86f1243c2bdc6497dc8f2.1592737958.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/ati.c | 85 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 7216f7e08f..245130d52f 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -389,22 +389,28 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case 0xf00 ... 0xfff: val = pci_default_read_config(&s->dev, addr - 0xf00, size); break; - case CUR_OFFSET: - val = s->regs.cur_offset; + case CUR_OFFSET ... CUR_OFFSET + 3: + val = ati_reg_read_offs(s->regs.cur_offset, addr - CUR_OFFSET, size); break; - case CUR_HORZ_VERT_POSN: - val = s->regs.cur_hv_pos; - val |= s->regs.cur_offset & BIT(31); + case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3: + val = ati_reg_read_offs(s->regs.cur_hv_pos, + addr - CUR_HORZ_VERT_POSN, size); + if (addr + size > CUR_HORZ_VERT_POSN + 3) { + val |= (s->regs.cur_offset & BIT(31)) >> (4 - size); + } break; - case CUR_HORZ_VERT_OFF: - val = s->regs.cur_hv_offs; - val |= s->regs.cur_offset & BIT(31); + case CUR_HORZ_VERT_OFF ... CUR_HORZ_VERT_OFF + 3: + val = ati_reg_read_offs(s->regs.cur_hv_offs, + addr - CUR_HORZ_VERT_OFF, size); + if (addr + size > CUR_HORZ_VERT_OFF + 3) { + val |= (s->regs.cur_offset & BIT(31)) >> (4 - size); + } break; - case CUR_CLR0: - val = s->regs.cur_color0; + case CUR_CLR0 ... CUR_CLR0 + 3: + val = ati_reg_read_offs(s->regs.cur_color0, addr - CUR_CLR0, size); break; - case CUR_CLR1: - val = s->regs.cur_color1; + case CUR_CLR1 ... CUR_CLR1 + 3: + val = ati_reg_read_offs(s->regs.cur_color1, addr - CUR_CLR1, size); break; case DST_OFFSET: val = s->regs.dst_offset; @@ -679,48 +685,71 @@ static void ati_mm_write(void *opaque, hwaddr addr, case 0xf00 ... 0xfff: /* read-only copy of PCI config space so ignore writes */ break; - case CUR_OFFSET: - if (s->regs.cur_offset != (data & 0x87fffff0)) { - s->regs.cur_offset = data & 0x87fffff0; + case CUR_OFFSET ... CUR_OFFSET + 3: + { + uint32_t t = s->regs.cur_offset; + + ati_reg_write_offs(&t, addr - CUR_OFFSET, data, size); + t &= 0x87fffff0; + if (s->regs.cur_offset != t) { + s->regs.cur_offset = t; ati_cursor_define(s); } break; - case CUR_HORZ_VERT_POSN: - s->regs.cur_hv_pos = data & 0x3fff0fff; - if (data & BIT(31)) { - s->regs.cur_offset |= data & BIT(31); + } + case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3: + { + uint32_t t = s->regs.cur_hv_pos | (s->regs.cur_offset & BIT(31)); + + ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_POSN, data, size); + s->regs.cur_hv_pos = t & 0x3fff0fff; + if (t & BIT(31)) { + s->regs.cur_offset |= t & BIT(31); } else if (s->regs.cur_offset & BIT(31)) { s->regs.cur_offset &= ~BIT(31); ati_cursor_define(s); } if (!s->cursor_guest_mode && - (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(data & BIT(31))) { + (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) { dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, s->regs.cur_hv_pos & 0xffff, 1); } break; + } case CUR_HORZ_VERT_OFF: - s->regs.cur_hv_offs = data & 0x3f003f; - if (data & BIT(31)) { - s->regs.cur_offset |= data & BIT(31); + { + uint32_t t = s->regs.cur_hv_offs | (s->regs.cur_offset & BIT(31)); + + ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_OFF, data, size); + s->regs.cur_hv_offs = t & 0x3f003f; + if (t & BIT(31)) { + s->regs.cur_offset |= t & BIT(31); } else if (s->regs.cur_offset & BIT(31)) { s->regs.cur_offset &= ~BIT(31); ati_cursor_define(s); } break; - case CUR_CLR0: - if (s->regs.cur_color0 != (data & 0xffffff)) { - s->regs.cur_color0 = data & 0xffffff; + } + case CUR_CLR0 ... CUR_CLR0 + 3: + { + uint32_t t = s->regs.cur_color0; + + ati_reg_write_offs(&t, addr - CUR_CLR0, data, size); + t &= 0xffffff; + if (s->regs.cur_color0 != t) { + s->regs.cur_color0 = t; ati_cursor_define(s); } break; - case CUR_CLR1: + } + case CUR_CLR1 ... CUR_CLR1 + 3: /* * Update cursor unconditionally here because some clients set up * other registers before actually writing cursor data to memory at * offset so we would miss cursor change unless always updating here */ - s->regs.cur_color1 = data & 0xffffff; + ati_reg_write_offs(&s->regs.cur_color1, addr - CUR_CLR1, data, size); + s->regs.cur_color1 &= 0xffffff; ati_cursor_define(s); break; case DST_OFFSET: From 41977c65c04a85f177603778cb60e06847efd3af Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 21 Jun 2020 13:12:38 +0200 Subject: [PATCH 11/13] ati-vga: Do not assert on error Do not abort on unsupported value just print log and continue. While display will likely be broken this prevents malicious guest to crash QEMU causing denial of service. Signed-off-by: BALATON Zoltan Message-id: 0c13dab5d8e3b7e7479c3edbf53aeac8c09de6de.1592737958.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/ati.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 245130d52f..95fc443cac 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -86,8 +86,8 @@ static void ati_vga_switch_mode(ATIVGAState *s) break; default: qemu_log_mask(LOG_UNIMP, "Unsupported bpp value\n"); + return; } - assert(bpp != 0); DPRINTF("Switching to %dx%d %d %d @ %x\n", h, v, stride, bpp, offs); vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); From 2bbcaa7cd67c30fc90d643f2fb490787b9d9627c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 21 Jun 2020 13:12:38 +0200 Subject: [PATCH 12/13] ati-vga: Add dummy MEM_SDRAM_MODE_REG Radeon chips have an SDRAM mode reg that is accessed by some drivers. We don't emulate the memory controller but provide some default value to prevent drivers getting unexpected 0. Signed-off-by: BALATON Zoltan Message-id: cc1324b9ef06beb8ae233ddc77dedd8bab9b8624.1592737958.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/ati.c | 5 +++++ hw/display/ati_dbg.c | 1 + hw/display/ati_regs.h | 1 + 3 files changed, 7 insertions(+) diff --git a/hw/display/ati.c b/hw/display/ati.c index 95fc443cac..4c3ad8f47b 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -361,6 +361,11 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case MC_STATUS: val = 5; break; + case MEM_SDRAM_MODE_REG: + if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF) { + val = BIT(28) | BIT(20); + } + break; case RBBM_STATUS: case GUI_STAT: val = 64; /* free CMDFIFO entries */ diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c index 0ebbd36f14..bd0ecd48c7 100644 --- a/hw/display/ati_dbg.c +++ b/hw/display/ati_dbg.c @@ -42,6 +42,7 @@ static struct ati_regdesc ati_reg_names[] = { {"MC_FB_LOCATION", 0x0148}, {"MC_AGP_LOCATION", 0x014C}, {"MC_STATUS", 0x0150}, + {"MEM_SDRAM_MODE_REG", 0x0158}, {"MEM_POWER_MISC", 0x015c}, {"AGP_BASE", 0x0170}, {"AGP_CNTL", 0x0174}, diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h index ebd37ee30d..d6282b2ef2 100644 --- a/hw/display/ati_regs.h +++ b/hw/display/ati_regs.h @@ -60,6 +60,7 @@ #define MC_FB_LOCATION 0x0148 #define MC_AGP_LOCATION 0x014C #define MC_STATUS 0x0150 +#define MEM_SDRAM_MODE_REG 0x0158 #define MEM_POWER_MISC 0x015c #define AGP_BASE 0x0170 #define AGP_CNTL 0x0174 From 8db2a4fd8abf6550479f7a8caa8f655c34238d6a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 22 Jun 2020 15:12:40 +0200 Subject: [PATCH 13/13] configure: vgabios cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 91b8eba9ec3f ("vgabios: remove submodule and build rules.") removed the vgabios submodule, but left some traces in the configure script. Remove them. Reported-by: BALATON Zoltan Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-id: 20200622131240.9624-1-kraxel@redhat.com --- configure | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 4a22dcd563..8a65240d4a 100755 --- a/configure +++ b/configure @@ -8486,14 +8486,14 @@ DIRS="tests tests/tcg tests/tcg/lm32 tests/qapi-schema tests/qtest/libqos" DIRS="$DIRS tests/qtest tests/qemu-iotests tests/vm tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw" -DIRS="$DIRS roms/seabios roms/vgabios" +DIRS="$DIRS roms/seabios" LINKS="Makefile" LINKS="$LINKS tests/tcg/lm32/Makefile po/Makefile" LINKS="$LINKS tests/tcg/Makefile.target tests/fp/Makefile" LINKS="$LINKS tests/plugin/Makefile" LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps" LINKS="$LINKS pc-bios/s390-ccw/Makefile" -LINKS="$LINKS roms/seabios/Makefile roms/vgabios/Makefile" +LINKS="$LINKS roms/seabios/Makefile" LINKS="$LINKS pc-bios/qemu-icon.bmp" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS tests/acceptance tests/data" @@ -8526,7 +8526,7 @@ export target_list source_path use_containers $source_path/tests/tcg/configure.sh) # temporary config to build submodules -for rom in seabios vgabios ; do +for rom in seabios; do config_mak=roms/$rom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak