gma: Check that framebuffer fits stolen memory and aperture

Change-Id: I92bd42e4a838832999a6d6fa795a8015d9896a12
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/20605
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index f9cbb10..2dcaf1c 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -479,6 +479,96 @@
 
    ----------------------------------------------------------------------------
 
+   use type HW.Word16;
+   subtype Stolen_Size_Range is Int64 range 0 .. 2 ** 33;
+
+   function GGMS_Gen4 (GGC : Word16) return Natural is
+     (Natural (Shift_Right (GGC, 8) and 16#07#));
+   function GTT_Size_Gen4 (GGC : Word16) return Natural is
+     (if GGMS_Gen4 (GGC) in 1 .. 3 then
+         (GGMS_Gen4 (GGC) + 1) * 2 ** 19 else 0);
+
+   function GMS_Gen4 (GGC : Word16) return Natural is
+     (Natural (Shift_Right (GGC, 4) and 16#0f#));
+   Valid_Stolen_Size_Gen4 : constant
+      array (Natural range 1 .. 13) of Stolen_Size_Range :=
+     (1, 4, 8, 16, 32, 48, 64, 128, 256, 96, 160, 224, 352);
+   function Stolen_Size_Gen4 (GGC : Word16) return Stolen_Size_Range is
+     (if GMS_Gen4 (GGC) in Valid_Stolen_Size_Gen4'Range then
+         Valid_Stolen_Size_Gen4 (GMS_Gen4 (GGC)) else 0);
+
+   function GTT_Size_Gen6 (GGC : Word16) return Natural is
+     (Natural (Shift_Right (GGC, 8) and 16#03#) * 2 ** 20);
+
+   function Stolen_Size_Gen6 (GGC : Word16) return Stolen_Size_Range is
+     (Stolen_Size_Range (Shift_Right (GGC, 3) and 16#1f#) * 32 * 2 ** 20);
+
+   function GTT_Size_Gen8 (GGC : Word16) return Natural is
+     (Natural (Shift_Right (GGC, 6) and 16#03#) * 2 ** 20);
+
+   function GMS_Gen8 (GGC : Word16) return Stolen_Size_Range is
+     (Stolen_Size_Range (Shift_Right (GGC, 8) and 16#ff#));
+   function Stolen_Size_Gen8 (GGC : Word16) return Stolen_Size_Range is
+     (GMS_Gen8 (GGC) * 32 * 2 ** 20);
+
+   function Stolen_Size_Gen9 (GGC : Word16) return Stolen_Size_Range is
+     (if GMS_Gen8 (GGC) < 16#f0# then
+         Stolen_Size_Gen8 (GGC)
+      else
+         (GMS_Gen8 (GGC) - 16#f0# + 1) * 4 * 2 ** 20);
+
+   procedure Decode_Stolen
+     (GTT_Size    : out Natural;
+      Stolen_Size : out Stolen_Size_Range)
+   with
+      Pre => Is_Initialized
+   is
+      GGC_Reg : constant :=
+        (case Config.CPU is
+            when Ironlake                 => 16#52#,
+            when Sandybridge .. Skylake   => 16#50#);
+      GGC : Word16;
+   begin
+      Dev.Read16 (GGC, GGC_Reg);
+      case Config.CPU is
+         when Ironlake =>
+            GTT_Size    := GTT_Size_Gen4 (GGC);
+            Stolen_Size := Stolen_Size_Gen4 (GGC);
+         when Sandybridge .. Haswell =>
+            GTT_Size    := GTT_Size_Gen6 (GGC);
+            Stolen_Size := Stolen_Size_Gen6 (GGC);
+         when Broadwell =>
+            GTT_Size    := GTT_Size_Gen8 (GGC);
+            Stolen_Size := Stolen_Size_Gen8 (GGC);
+         when Broxton .. Skylake =>
+            GTT_Size    := GTT_Size_Gen8 (GGC);
+            Stolen_Size := Stolen_Size_Gen9 (GGC);
+      end case;
+   end Decode_Stolen;
+
+   -- Additional runtime validation that FB fits stolen memory and aperture.
+   procedure Validate_FB (FB : Framebuffer_Type; Valid : out Boolean)
+   with
+      Pre => Is_Initialized,
+      Post => (if Valid then Valid_FB (FB))
+   is
+      GTT_Size, Aperture_Size : Natural;
+      Stolen_Size : Stolen_Size_Range;
+   begin
+      Valid := Valid_FB (FB);
+
+      if Valid then
+         Decode_Stolen (GTT_Size, Stolen_Size);
+         Dev.Resource_Size (Aperture_Size, PCI.Res2);
+         Valid :=
+            FB_Last_Page (FB) < GTT_Size / Config.GTT_PTE_Size and
+            FB_Last_Page (FB) < Natural (Stolen_Size / GTT_Page_Size) and
+            FB_Last_Page (FB) < Aperture_Size / GTT_Page_Size;
+         pragma Debug (not Valid, Debug.Put
+           ("Stolen memory too small to hold framebuffer."));
+      end if;
+   end Validate_FB;
+
    procedure Setup_Default_FB
      (FB       : in     Framebuffer_Type;
       Clear    : in     Boolean := True;
@@ -489,7 +579,7 @@
 
       Phys_Base : Word32;
    begin
-      Success := Valid_FB (FB);
+      Validate_FB (FB, Success);
 
       if Success then
          Dev.Read32 (Phys_Base, GMA_Phys_Base);