From 2d18434c1ca66d68f80954be6828a3770176dab4 Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Fri, 10 Jul 2020 11:19:41 +0200 Subject: [PATCH 1/7] hw/net/xgmac: Fix buffer overflow in xgmac_enet_send() A buffer overflow issue was reported by Mr. Ziming Zhang, CC'd here. It occurs while sending an Ethernet frame due to missing break statements and improper checking of the buffer size. Reported-by: Ziming Zhang Signed-off-by: Mauro Matteo Cascella Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/xgmac.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index f49df95b07..f496f7ed4c 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -217,21 +217,31 @@ static void xgmac_enet_send(XgmacState *s) } len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff); + /* + * FIXME: these cases of malformed tx descriptors (bad sizes) + * should probably be reported back to the guest somehow + * rather than simply silently stopping processing, but we + * don't know what the hardware does in this situation. + * This will only happen for buggy guests anyway. + */ if ((bd.buffer1_size & 0xfff) > 2048) { DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " "xgmac buffer 1 len on send > 2048 (0x%x)\n", __func__, bd.buffer1_size & 0xfff); + break; } if ((bd.buffer2_size & 0xfff) != 0) { DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " "xgmac buffer 2 len on send != 0 (0x%x)\n", __func__, bd.buffer2_size & 0xfff); + break; } - if (len >= sizeof(frame)) { + if (frame_size + len >= sizeof(frame)) { DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu " - "buffer\n" , __func__, len, sizeof(frame)); + "buffer\n" , __func__, frame_size + len, sizeof(frame)); DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n", __func__, bd.buffer1_size, bd.buffer2_size); + break; } cpu_physical_memory_read(bd.buffer1_addr, ptr, len); -- Gitee From 596e7e8908b742f727d02ec9ab747116573f67e0 Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Sat, 1 Aug 2020 18:42:38 +0200 Subject: [PATCH 2/7] hw/net/net_tx_pkt: fix assertion failure in net_tx_pkt_add_raw_fragment() An assertion failure issue was found in the code that processes network packets while adding data fragments into the packet context. It could be abused by a malicious guest to abort the QEMU process on the host. This patch replaces the affected assert() with a conditional statement, returning false if the current data fragment exceeds max_raw_frags. Reported-by: Alexander Bulekov Reported-by: Ziming Zhang Reviewed-by: Dmitry Fleytman Signed-off-by: Mauro Matteo Cascella Signed-off-by: Jason Wang --- hw/net/net_tx_pkt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index 162f802dd7..54d4c3bbd0 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -379,7 +379,10 @@ bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, hwaddr mapped_len = 0; struct iovec *ventry; assert(pkt); - assert(pkt->max_raw_frags > pkt->raw_frags); + + if (pkt->raw_frags >= pkt->max_raw_frags) { + return false; + } if (!len) { return true; -- Gitee From 428e3a78ddf1de3dfb914043d6a8668f73ef8bb3 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 21 May 2020 21:39:44 +0200 Subject: [PATCH 3/7] sm501: Convert printf + abort to qemu_log_mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some places already use qemu_log_mask() to log unimplemented features or errors but some others have printf() then abort(). Convert these to qemu_log_mask() and avoid aborting to prevent guests to easily cause denial of service. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-id: 305af87f59d81e92f2aaff09eb8a3603b8baa322.1590089984.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 57 ++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 5918f59b2b..aa4b202a48 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -727,8 +727,8 @@ static void sm501_2d_operation(SM501State *s) int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); if (addressing != 0x0) { - printf("%s: only XY addressing is supported.\n", __func__); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); + return; } if (rop_mode == 0) { @@ -754,8 +754,8 @@ static void sm501_2d_operation(SM501State *s) if ((s->twoD_source_base & 0x08000000) || (s->twoD_destination_base & 0x08000000)) { - printf("%s: only local memory is supported.\n", __func__); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); + return; } switch (operation) { @@ -823,9 +823,9 @@ static void sm501_2d_operation(SM501State *s) break; default: - printf("non-implemented SM501 2D operation. %d\n", operation); - abort(); - break; + qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", + operation); + return; } if (dst_base >= get_fb_addr(s, crt) && @@ -892,9 +892,8 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, break; default: - printf("sm501 system config : not implemented register read." - " addr=%x\n", (int)addr); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented system config" + "register read. addr=%" HWADDR_PRIx "\n", addr); } return ret; @@ -948,15 +947,15 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, break; case SM501_ENDIAN_CONTROL: if (value & 0x00000001) { - printf("sm501 system config : big endian mode not implemented.\n"); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: system config big endian mode not" + " implemented.\n"); } break; default: - printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented system config" + "register write. addr=%" HWADDR_PRIx + ", val=%" PRIx64 "\n", addr, value); } } @@ -1207,9 +1206,8 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, break; default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " + "read. addr=%" HWADDR_PRIx "\n", addr); } return ret; @@ -1345,9 +1343,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, break; default: - printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " + "write. addr=%" HWADDR_PRIx + ", val=%" PRIx64 "\n", addr, value); } } @@ -1433,9 +1431,8 @@ static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, ret = 0; /* Should return interrupt status */ break; default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented disp ctrl register " + "read. addr=%" HWADDR_PRIx "\n", addr); } return ret; @@ -1520,9 +1517,9 @@ static void sm501_2d_engine_write(void *opaque, hwaddr addr, /* ignored, writing 0 should clear interrupt status */ break; default: - printf("sm501 2d engine : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); + qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2d engine register " + "write. addr=%" HWADDR_PRIx + ", val=%" PRIx64 "\n", addr, value); } } @@ -1670,9 +1667,9 @@ static void sm501_update_display(void *opaque) draw_line = draw_line32_funcs[dst_depth_index]; break; default: - printf("sm501 update display : invalid control register value.\n"); - abort(); - break; + qemu_log_mask(LOG_GUEST_ERROR, "sm501: update display" + "invalid control register value.\n"); + return; } /* set up to draw hardware cursor */ -- Gitee From bc472e56b985db1de73a7ddab5ea8568d6e7f327 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 21 May 2020 21:39:44 +0200 Subject: [PATCH 4/7] sm501: Shorten long variable names in sm501_2d_operation This increases readability and cleans up some confusing naming. Signed-off-by: BALATON Zoltan Message-id: b9b67b94c46e945252a73c77dfd117132c63c4fb.1590089984.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index aa4b202a48..51e7ccc39d 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -700,17 +700,16 @@ static inline void hwc_invalidate(SM501State *s, int crt) static void sm501_2d_operation(SM501State *s) { /* obtain operation parameters */ - int operation = (s->twoD_control >> 16) & 0x1f; + int cmd = (s->twoD_control >> 16) & 0x1F; int rtl = s->twoD_control & 0x8000000; int src_x = (s->twoD_source >> 16) & 0x01FFF; int src_y = s->twoD_source & 0xFFFF; int dst_x = (s->twoD_destination >> 16) & 0x01FFF; int dst_y = s->twoD_destination & 0xFFFF; - int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; - int operation_height = s->twoD_dimension & 0xFFFF; + int width = (s->twoD_dimension >> 16) & 0x1FFF; + int height = s->twoD_dimension & 0xFFFF; uint32_t color = s->twoD_foreground; - int format_flags = (s->twoD_stretch >> 20) & 0x3; - int addressing = (s->twoD_stretch >> 16) & 0xF; + int format = (s->twoD_stretch >> 20) & 0x3; int rop_mode = (s->twoD_control >> 15) & 0x1; /* 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; @@ -721,12 +720,12 @@ static void sm501_2d_operation(SM501State *s) /* get frame buffer info */ uint8_t *src = s->local_mem + src_base; uint8_t *dst = s->local_mem + dst_base; - int src_width = s->twoD_pitch & 0x1FFF; - int dst_width = (s->twoD_pitch >> 16) & 0x1FFF; + int src_pitch = s->twoD_pitch & 0x1FFF; + 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); - if (addressing != 0x0) { + if ((s->twoD_stretch >> 16) & 0xF) { qemu_log_mask(LOG_UNIMP, "sm501: only XY addressing is supported.\n"); return; } @@ -758,20 +757,20 @@ static void sm501_2d_operation(SM501State *s) return; } - switch (operation) { + switch (cmd) { case 0x00: /* copy area */ #define COPY_AREA(_bpp, _pixel_type, rtl) { \ int y, x, index_d, index_s; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ + for (y = 0; y < height; y++) { \ + for (x = 0; x < width; x++) { \ _pixel_type val; \ \ if (rtl) { \ - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + index_s = ((src_y - y) * src_pitch + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_pitch + dst_x - x) * _bpp; \ } else { \ - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + index_s = ((src_y + y) * src_pitch + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ } \ if (rop_mode == 1 && rop == 5) { \ /* Invert dest */ \ @@ -783,7 +782,7 @@ static void sm501_2d_operation(SM501State *s) } \ } \ } - switch (format_flags) { + switch (format) { case 0: COPY_AREA(1, uint8_t, rtl); break; @@ -799,15 +798,15 @@ static void sm501_2d_operation(SM501State *s) case 0x01: /* fill rectangle */ #define FILL_RECT(_bpp, _pixel_type) { \ int y, x; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + for (y = 0; y < height; y++) { \ + for (x = 0; x < width; x++) { \ + int index = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ *(_pixel_type *)&dst[index] = (_pixel_type)color; \ } \ } \ } - switch (format_flags) { + switch (format) { case 0: FILL_RECT(1, uint8_t); break; @@ -824,14 +823,14 @@ static void sm501_2d_operation(SM501State *s) default: qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", - operation); + cmd); return; } 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 + operation_height - 1) * dst_width + - dst_x + operation_width) * (1 << format_flags)); + int dst_len = MIN(fb_len, ((dst_y + height - 1) * dst_pitch + + dst_x + width) * (1 << format)); if (dst_len) { memory_region_set_dirty(&s->local_mem_region, dst_base, dst_len); } -- Gitee From 9f1e9012047639121eb275a4f8f5693d340e91f6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 21 May 2020 21:39:44 +0200 Subject: [PATCH 5/7] sm501: Use BIT(x) macro to shorten constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-id: 124bf5de8d7cf503b32b377d0445029a76bfbd49.1590089984.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 51e7ccc39d..f3d11d0b23 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -701,7 +701,7 @@ static void sm501_2d_operation(SM501State *s) { /* obtain operation parameters */ int cmd = (s->twoD_control >> 16) & 0x1F; - int rtl = s->twoD_control & 0x8000000; + int rtl = s->twoD_control & BIT(27); int src_x = (s->twoD_source >> 16) & 0x01FFF; int src_y = s->twoD_source & 0xFFFF; int dst_x = (s->twoD_destination >> 16) & 0x01FFF; @@ -751,8 +751,7 @@ static void sm501_2d_operation(SM501State *s) } } - if ((s->twoD_source_base & 0x08000000) || - (s->twoD_destination_base & 0x08000000)) { + if (s->twoD_source_base & BIT(27) || s->twoD_destination_base & BIT(27)) { qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); return; } -- Gitee From 6186d3de416825e3a737dd3da31da475f50d66d0 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 21 May 2020 21:39:44 +0200 Subject: [PATCH 6/7] sm501: Clean up local variables in sm501_2d_operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make variables local to the block they are used in to make it clearer which operation they are needed for. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-id: ae59f8138afe7f6a5a4a82539d0f61496a906b06.1590089984.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index f3d11d0b23..98b3b97f7b 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -699,28 +699,19 @@ static inline void hwc_invalidate(SM501State *s, int crt) static void sm501_2d_operation(SM501State *s) { - /* obtain operation parameters */ int cmd = (s->twoD_control >> 16) & 0x1F; int rtl = s->twoD_control & BIT(27); - int src_x = (s->twoD_source >> 16) & 0x01FFF; - int src_y = s->twoD_source & 0xFFFF; - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; - int dst_y = s->twoD_destination & 0xFFFF; - int width = (s->twoD_dimension >> 16) & 0x1FFF; - int height = s->twoD_dimension & 0xFFFF; - uint32_t color = s->twoD_foreground; int format = (s->twoD_stretch >> 20) & 0x3; int rop_mode = (s->twoD_control >> 15) & 0x1; /* 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 rop = s->twoD_control & 0xFF; - uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; + int dst_x = (s->twoD_destination >> 16) & 0x01FFF; + int dst_y = s->twoD_destination & 0xFFFF; + int width = (s->twoD_dimension >> 16) & 0x1FFF; + int height = s->twoD_dimension & 0xFFFF; uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; - - /* get frame buffer info */ - uint8_t *src = s->local_mem + src_base; uint8_t *dst = s->local_mem + dst_base; - int src_pitch = s->twoD_pitch & 0x1FFF; 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); @@ -758,6 +749,13 @@ static void sm501_2d_operation(SM501State *s) switch (cmd) { case 0x00: /* copy area */ + { + int src_x = (s->twoD_source >> 16) & 0x01FFF; + int src_y = s->twoD_source & 0xFFFF; + uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; + uint8_t *src = s->local_mem + src_base; + int src_pitch = s->twoD_pitch & 0x1FFF; + #define COPY_AREA(_bpp, _pixel_type, rtl) { \ int y, x, index_d, index_s; \ for (y = 0; y < height; y++) { \ @@ -793,8 +791,11 @@ static void sm501_2d_operation(SM501State *s) break; } break; - + } case 0x01: /* fill rectangle */ + { + uint32_t color = s->twoD_foreground; + #define FILL_RECT(_bpp, _pixel_type) { \ int y, x; \ for (y = 0; y < height; y++) { \ @@ -819,7 +820,7 @@ static void sm501_2d_operation(SM501State *s) break; } break; - + } default: qemu_log_mask(LOG_UNIMP, "sm501: not implemented 2D operation: %d\n", cmd); -- Gitee From bbbf2c2f4201eb84a5bcd07a92399fe166d682e9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 21 May 2020 21:39:44 +0200 Subject: [PATCH 7/7] sm501: Replace hand written implementation with pixman where possible Besides being faster this should also prevent malicious guests to abuse 2D engine to overwrite data or cause a crash. Signed-off-by: BALATON Zoltan Message-id: 58666389b6cae256e4e972a32c05cf8aa51bffc0.1590089984.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/sm501.c | 207 ++++++++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 88 deletions(-) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 98b3b97f7b..7dc4bb18b7 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -706,13 +706,12 @@ static void sm501_2d_operation(SM501State *s) /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; int rop = s->twoD_control & 0xFF; - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; - int dst_y = s->twoD_destination & 0xFFFF; - int width = (s->twoD_dimension >> 16) & 0x1FFF; - int height = s->twoD_dimension & 0xFFFF; + unsigned int dst_x = (s->twoD_destination >> 16) & 0x01FFF; + unsigned int dst_y = s->twoD_destination & 0xFFFF; + unsigned int width = (s->twoD_dimension >> 16) & 0x1FFF; + unsigned int height = s->twoD_dimension & 0xFFFF; uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; - uint8_t *dst = s->local_mem + dst_base; - int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF; + 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); @@ -721,104 +720,136 @@ static void sm501_2d_operation(SM501State *s) return; } - if (rop_mode == 0) { - if (rop != 0xcc) { - /* Anything other than plain copies are not supported */ - qemu_log_mask(LOG_UNIMP, "sm501: rop3 mode with rop %x is not " - "supported.\n", rop); - } - } else { - if (rop2_source_is_pattern && rop != 0x5) { - /* For pattern source, we support only inverse dest */ - qemu_log_mask(LOG_UNIMP, "sm501: rop2 source being the pattern and " - "rop %x is not supported.\n", rop); - } else { - if (rop != 0x5 && rop != 0xc) { - /* Anything other than plain copies or inverse dest is not - * supported */ - qemu_log_mask(LOG_UNIMP, "sm501: rop mode %x is not " - "supported.\n", rop); - } - } - } - if (s->twoD_source_base & BIT(27) || s->twoD_destination_base & BIT(27)) { qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n"); return; } + if (!dst_pitch) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero dest pitch.\n"); + return; + } + + if (!width || !height) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero size 2D op.\n"); + return; + } + + if (rtl) { + dst_x -= width - 1; + dst_y -= height - 1; + } + + if (dst_base >= get_local_mem_size(s) || dst_base + + (dst_x + width + (dst_y + height) * (dst_pitch + width)) * + (1 << format) >= get_local_mem_size(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: 2D op dest is outside vram.\n"); + return; + } + switch (cmd) { - case 0x00: /* copy area */ + case 0: /* BitBlt */ { - int src_x = (s->twoD_source >> 16) & 0x01FFF; - int src_y = s->twoD_source & 0xFFFF; + unsigned int src_x = (s->twoD_source >> 16) & 0x01FFF; + unsigned int src_y = s->twoD_source & 0xFFFF; uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; - uint8_t *src = s->local_mem + src_base; - int src_pitch = s->twoD_pitch & 0x1FFF; - -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ - int y, x, index_d, index_s; \ - for (y = 0; y < height; y++) { \ - for (x = 0; x < width; x++) { \ - _pixel_type val; \ - \ - if (rtl) { \ - index_s = ((src_y - y) * src_pitch + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_pitch + dst_x - x) * _bpp; \ - } else { \ - index_s = ((src_y + y) * src_pitch + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ - } \ - if (rop_mode == 1 && rop == 5) { \ - /* Invert dest */ \ - val = ~*(_pixel_type *)&dst[index_d]; \ - } else { \ - val = *(_pixel_type *)&src[index_s]; \ - } \ - *(_pixel_type *)&dst[index_d] = val; \ - } \ - } \ - } - switch (format) { - case 0: - COPY_AREA(1, uint8_t, rtl); - break; - case 1: - COPY_AREA(2, uint16_t, rtl); - break; - case 2: - COPY_AREA(4, uint32_t, rtl); - break; + unsigned int src_pitch = s->twoD_pitch & 0x1FFF; + + if (!src_pitch) { + qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero src pitch.\n"); + return; + } + + if (rtl) { + src_x -= width - 1; + src_y -= height - 1; + } + + if (src_base >= get_local_mem_size(s) || src_base + + (src_x + width + (src_y + height) * (src_pitch + width)) * + (1 << format) >= get_local_mem_size(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "sm501: 2D op src is outside vram.\n"); + return; + } + + if ((rop_mode && rop == 0x5) || (!rop_mode && rop == 0x55)) { + /* Invert dest, is there a way to do this with pixman? */ + unsigned int x, y, i; + 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)) { + 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; + } + } + } + } else { + /* Do copy src for unimplemented ops, better than unpainted area */ + if ((rop_mode && (rop != 0xc || rop2_source_is_pattern)) || + (!rop_mode && rop != 0xcc)) { + qemu_log_mask(LOG_UNIMP, + "sm501: rop%d op %x%s not implemented\n", + (rop_mode ? 2 : 3), rop, + (rop2_source_is_pattern ? + " with pattern source" : "")); + } + /* 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 * (1 << format); + int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t)); + uint32_t *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_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), + 0, 0, dst_x, dst_y, width, height); + g_free(tmp); + } 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_x, src_y, dst_x, dst_y, width, height); + } } break; } - case 0x01: /* fill rectangle */ + case 1: /* Rectangle Fill */ { uint32_t color = s->twoD_foreground; -#define FILL_RECT(_bpp, _pixel_type) { \ - int y, x; \ - for (y = 0; y < height; y++) { \ - for (x = 0; x < width; x++) { \ - int index = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \ - *(_pixel_type *)&dst[index] = (_pixel_type)color; \ - } \ - } \ - } - - switch (format) { - case 0: - FILL_RECT(1, uint8_t); - break; - case 1: - color = cpu_to_le16(color); - FILL_RECT(2, uint16_t); - break; - case 2: + if (format == 2) { color = cpu_to_le32(color); - FILL_RECT(4, uint32_t); - break; + } else if (format == 1) { + color = cpu_to_le16(color); } + + 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); break; } default: -- Gitee