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 <balaton@eik.bme.hu> Message-id: 58666389b6cae256e4e972a32c05cf8aa51bffc0.1590089984.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
3d0b096298
commit
b15a22bbcb
|
@ -706,13 +706,12 @@ static void sm501_2d_operation(SM501State *s)
|
||||||
/* 1 if rop2 source is the pattern, otherwise the source is the bitmap */
|
/* 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) & 0x1;
|
||||||
int rop = s->twoD_control & 0xFF;
|
int rop = s->twoD_control & 0xFF;
|
||||||
int dst_x = (s->twoD_destination >> 16) & 0x01FFF;
|
unsigned int dst_x = (s->twoD_destination >> 16) & 0x01FFF;
|
||||||
int dst_y = s->twoD_destination & 0xFFFF;
|
unsigned int dst_y = s->twoD_destination & 0xFFFF;
|
||||||
int width = (s->twoD_dimension >> 16) & 0x1FFF;
|
unsigned int width = (s->twoD_dimension >> 16) & 0x1FFF;
|
||||||
int height = s->twoD_dimension & 0xFFFF;
|
unsigned int height = s->twoD_dimension & 0xFFFF;
|
||||||
uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF;
|
uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF;
|
||||||
uint8_t *dst = s->local_mem + dst_base;
|
unsigned int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF;
|
||||||
int dst_pitch = (s->twoD_pitch >> 16) & 0x1FFF;
|
|
||||||
int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0;
|
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);
|
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;
|
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)) {
|
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");
|
qemu_log_mask(LOG_UNIMP, "sm501: only local memory is supported.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cmd) {
|
if (!dst_pitch) {
|
||||||
case 0x00: /* copy area */
|
qemu_log_mask(LOG_GUEST_ERROR, "sm501: Zero dest pitch.\n");
|
||||||
{
|
return;
|
||||||
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++) { \
|
|
||||||
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; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 0: /* BitBlt */
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
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) {
|
switch (format) {
|
||||||
case 0:
|
case 0:
|
||||||
COPY_AREA(1, uint8_t, rtl);
|
d[i] = ~d[i];
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
COPY_AREA(2, uint16_t, rtl);
|
*(uint16_t *)&d[i] = ~*(uint16_t *)&d[i];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
COPY_AREA(4, uint32_t, rtl);
|
*(uint32_t *)&d[i] = ~*(uint32_t *)&d[i];
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 0x01: /* fill rectangle */
|
case 1: /* Rectangle Fill */
|
||||||
{
|
{
|
||||||
uint32_t color = s->twoD_foreground;
|
uint32_t color = s->twoD_foreground;
|
||||||
|
|
||||||
#define FILL_RECT(_bpp, _pixel_type) { \
|
if (format == 2) {
|
||||||
int y, x; \
|
color = cpu_to_le32(color);
|
||||||
for (y = 0; y < height; y++) { \
|
} else if (format == 1) {
|
||||||
for (x = 0; x < width; x++) { \
|
color = cpu_to_le16(color);
|
||||||
int index = ((dst_y + y) * dst_pitch + dst_x + x) * _bpp; \
|
|
||||||
*(_pixel_type *)&dst[index] = (_pixel_type)color; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (format) {
|
pixman_fill((uint32_t *)&s->local_mem[dst_base],
|
||||||
case 0:
|
dst_pitch * (1 << format) / sizeof(uint32_t),
|
||||||
FILL_RECT(1, uint8_t);
|
8 * (1 << format), dst_x, dst_y, width, height, color);
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
color = cpu_to_le16(color);
|
|
||||||
FILL_RECT(2, uint16_t);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
color = cpu_to_le32(color);
|
|
||||||
FILL_RECT(4, uint32_t);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue