Subject: vga: add sr_vbe register set
From: Gerd Hoffmann kraxel@redhat.com Tue May 17 10:54:54 2016 +0200
Date: Mon May 23 14:28:25 2016 +0200:
Git: 94ef4f337fb614f18b765a8e0e878a4c23cdedcd

Commit "fd3c136 vga: make sure vga register setup for vbe stays intact
(CVE-2016-3712)." causes a regression.  The win7 installer is unhappy
because it can't freely modify vga registers any more while in vbe mode.

This patch introduces a new sr_vbe register set.  The vbe_update_vgaregs
will fill sr_vbe[] instead of sr[].  Normal vga register reads and
writes go to sr[].  Any sr register read access happens through a new
sr() helper function which will read from sr_vbe[] with vbe active and
from sr[] otherwise.

This way we can allow guests update sr[] registers as they want, without
allowing them disrupt vbe video modes that way.

Cc: qemu-stable@nongnu.org
Reported-by: Thomas Lamprecht <thomas@lamprecht.org>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 1463475294-14119-1-git-send-email-kraxel@redhat.com

Index: xen-4.2.5-testing/tools/qemu-xen-dir-remote/hw/vga.c
===================================================================
--- xen-4.2.5-testing.orig/tools/qemu-xen-dir-remote/hw/vga.c
+++ xen-4.2.5-testing/tools/qemu-xen-dir-remote/hw/vga.c
@@ -159,6 +159,11 @@ static inline bool vbe_enabled(VGACommon
     return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED;
 }
 
+static inline uint8_t sr(VGACommonState *s, int idx)
+{
+    return vbe_enabled(s) ? s->sr_vbe[idx] : s->sr[idx];
+}
+
 static void vga_update_memory_access(VGACommonState *s)
 {
     MemoryRegion *region, *old_region = s->chain4_alias;
@@ -166,7 +171,7 @@ static void vga_update_memory_access(VGA
 
     s->chain4_alias = NULL;
 
-    if ((s->sr[0x02] & 0xf) == 0xf && s->sr[0x04] & 0x08) {
+    if ((sr(s, 0x02) & 0x0F) == 0x0F && sr(s, 0x04) & 0x08) {
         offset = 0;
         switch ((s->gr[6] >> 2) & 3) {
         case 0:
@@ -245,7 +250,7 @@ static void vga_precise_update_retrace_i
 
 
 
-    clocking_mode = (s->sr[0x01] >> 3) & 1;
+    clocking_mode = (sr(s, 0x01) >> 3) & 1;
     clock_sel = (s->msr >> 2) & 3;
     dots = (s->msr & 1) ? 8 : 9;
 
@@ -496,7 +501,6 @@ void vga_ioport_write(void *opaque, uint
         printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
 #endif
         s->sr[s->sr_index] = val & sr_mask[s->sr_index];
-        vbe_update_vgaregs(s);
         if (s->sr_index == 1) s->update_retrace_info(s);
         vga_update_memory_access(s);
         break;
@@ -600,13 +604,13 @@ static void vbe_update_vgaregs(VGACommon
 
     if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
         shift_control = 0;
-        s->sr[0x01] &= ~8; /* no double line */
+        s->sr_vbe[0x01] &= ~8; /* no double line */
     } else {
         shift_control = 2;
         /* set chain 4 mode */
-        s->sr[0x04] |= 0x08;
+        s->sr_vbe[0x04] |= 0x08;
         /* activate all planes */
-        s->sr[0x02] |= 0x0F;
+        s->sr_vbe[0x02] |= 0x0F;
     }
     s->gr[0x05] = (s->gr[0x05] & ~0x60) |
         (shift_control << 5);
@@ -809,7 +813,7 @@ uint32_t vga_mem_readb(VGACommonState *s
         break;
     }
 
-    if (s->sr[4] & 0x08) {
+    if (sr(s, 0x04) & 0x08) {
         /* chain 4 mode : simplest access */
         assert(addr < s->vram_size);
         ret = s->vram_ptr[addr];
@@ -876,7 +880,7 @@ void vga_mem_writeb(VGACommonState *s, t
         break;
     }
 
-    if (s->sr[4] & 0x08) {
+    if (sr(s, 0x04) & 0x08) {
         /* chain 4 mode : simplest access */
         plane = addr & 3;
         mask = (1 << plane);
@@ -893,7 +897,7 @@ void vga_mem_writeb(VGACommonState *s, t
         /* odd/even mode (aka text mode mapping) */
         plane = (s->gr[4] & 2) | (addr & 1);
         mask = (1 << plane);
-        if (s->sr[2] & mask) {
+        if (sr(s, 0x02) & mask) {
             addr = ((addr & ~1) << 1) | plane;
             if (addr >= s->vram_size) {
                 return;
@@ -967,7 +971,7 @@ void vga_mem_writeb(VGACommonState *s, t
 
     do_write:
         /* mask data according to sr[2] */
-        mask = s->sr[2];
+        mask = sr(s, 0x02);
         s->plane_updated |= mask; /* only used to detect font change */
         write_mask = mask16[mask];
         if (addr * sizeof(uint32_t) >= s->vram_size) {
@@ -1257,9 +1261,9 @@ static void vga_get_text_resolution(VGAC
     /* total width & height */
     cheight = (s->cr[9] & 0x1f) + 1;
     cwidth = 8;
-    if (!(s->sr[1] & 0x01))
+    if (!(sr(s, 0x01) & 0x01))
         cwidth = 9;
-    if (s->sr[1] & 0x08)
+    if (sr(s, 0x01) & 0x08)
         cwidth = 16; /* NOTE: no 18 pixel wide */
     width = (s->cr[0x01] + 1);
     if (s->cr[0x06] == 100) {
@@ -1312,7 +1316,7 @@ static void vga_draw_text(VGACommonState
     vga_draw_glyph9_func *vga_draw_glyph9;
 
     /* compute font data address (in plane 2) */
-    v = s->sr[3];
+    v = sr(s, 0x03);
     offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
     if (offset != s->font_offsets[0]) {
         s->font_offsets[0] = offset;
@@ -1659,11 +1663,11 @@ static void vga_draw_graphic(VGACommonSt
     }
 
     if (shift_control == 0) {
-        if (s->sr[0x01] & 8) {
+        if (sr(s, 0x01) & 8) {
             disp_width <<= 1;
         }
     } else if (shift_control == 1) {
-        if (s->sr[0x01] & 8) {
+        if (sr(s, 0x01) & 8) {
             disp_width <<= 1;
         }
     }
@@ -1707,7 +1711,7 @@ static void vga_draw_graphic(VGACommonSt
 
     if (shift_control == 0) {
         full_update |= update_palette16(s);
-        if (s->sr[0x01] & 8) {
+        if (sr(s, 0x01) & 8) {
             v = VGA_DRAW_LINE4D2;
         } else {
             v = VGA_DRAW_LINE4;
@@ -1715,7 +1719,7 @@ static void vga_draw_graphic(VGACommonSt
         bits = 4;
     } else if (shift_control == 1) {
         full_update |= update_palette16(s);
-        if (s->sr[0x01] & 8) {
+        if (sr(s, 0x01) & 8) {
             v = VGA_DRAW_LINE2D2;
         } else {
             v = VGA_DRAW_LINE2;
@@ -1760,7 +1764,7 @@ static void vga_draw_graphic(VGACommonSt
     line_offset = s->line_offset;
 #if 0
     printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
-           width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]);
+           width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, sr(s, 0x01));
 #endif
     addr1 = (s->start_addr * 4);
     bwidth = (width * bits + 7) / 8;
@@ -1921,6 +1925,7 @@ void vga_common_reset(VGACommonState *s)
 {
     s->sr_index = 0;
     memset(s->sr, '\0', sizeof(s->sr));
+    memset(s->sr_vbe, '\0', sizeof(s->sr_vbe));
     s->gr_index = 0;
     memset(s->gr, '\0', sizeof(s->gr));
     s->ar_index = 0;
@@ -2024,9 +2029,9 @@ static void vga_update_text(void *opaque
         /* total width & height */
         cheight = (s->cr[9] & 0x1f) + 1;
         cw = 8;
-        if (!(s->sr[1] & 0x01))
+        if (!(sr(s, 0x01) & 0x01))
             cw = 9;
-        if (s->sr[1] & 0x08)
+        if (sr(s, 0x01) & 0x08)
             cw = 16; /* NOTE: no 18 pixel wide */
         width = (s->cr[0x01] + 1);
         if (s->cr[0x06] == 100) {
@@ -2184,6 +2189,7 @@ static int vga_common_post_load(void *op
 
     /* force refresh */
     s->graphic_mode = -1;
+    vbe_update_vgaregs(s);
     return 0;
 }
 
Index: xen-4.2.5-testing/tools/qemu-xen-dir-remote/hw/vga_int.h
===================================================================
--- xen-4.2.5-testing.orig/tools/qemu-xen-dir-remote/hw/vga_int.h
+++ xen-4.2.5-testing/tools/qemu-xen-dir-remote/hw/vga_int.h
@@ -113,6 +113,7 @@ typedef struct VGACommonState {
     MemoryRegion *chain4_alias;
     uint8_t sr_index;
     uint8_t sr[256];
+    uint8_t sr_vbe[256];
     uint8_t gr_index;
     uint8_t gr[256];
     uint8_t ar_index;
