gma: Allow offsets /= 0 in Setup_Default_GTT()

Honor the `Offset` field of the given framebuffer and add preconditions
that ensure we won't overflow the GTT or its target address space.

Change-Id: I6577e98e154610228734baee7674ee54b9a922e8
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/20602
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index a17a1ac..dae8d27 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -437,19 +437,17 @@
       Registers.Write_GTT (GTT_Page, Device_Address, Valid);
    end Write_GTT;
 
-   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
+   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
    is
-      FB_Size : constant Pos32 :=
-         FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
-      Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
+      Phys_Addr : GTT_Address_Type :=
+         GTT_Address_Type (Phys_Base) + GTT_Address_Type (FB.Offset);
    begin
-      for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
-      loop
+      for Idx in FB_First_Page (FB) .. FB_Last_Page (FB) loop
          Registers.Write_GTT
            (GTT_Page       => Idx,
             Device_Address => Phys_Addr,
             Valid          => True);
-         Phys_Addr := Phys_Addr + 4096;
+         Phys_Addr := Phys_Addr + GTT_Page_Size;
       end loop;
    end Setup_Default_GTT;
 
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index 75dde9e..a91c83b 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -1,5 +1,6 @@
 --
 -- Copyright (C) 2015-2017 secunet Security Networks AG
+-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -12,6 +13,7 @@
 -- GNU General Public License for more details.
 --
 
+with HW.Config;
 with HW.Time;
 with HW.Port_IO;
 
@@ -84,14 +86,40 @@
                     Reason => "It's only used for debugging");
    procedure Dump_Configs (Configs : Pipe_Configs);
 
+   GTT_Page_Size : constant := 4096;
    type GTT_Address_Type is mod 2 ** 39;
-   type GTT_Range is range 0 .. 16#8_0000# - 1;
+   subtype GTT_Range is Natural range 0 .. 16#8_0000# - 1;
    procedure Write_GTT
      (GTT_Page       : GTT_Range;
       Device_Address : GTT_Address_Type;
       Valid          : Boolean);
 
-   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32);
+   ----------------------------------------------------------------------------
+
+   function FB_First_Page (FB : Framebuffer_Type) return Natural is
+     (Natural (FB.Offset / GTT_Page_Size));
+   function FB_Pages (FB : Framebuffer_Type) return Natural is
+     (Natural (Div_Round_Up (FB_Size (FB), GTT_Page_Size)));
+   function FB_Last_Page (FB : Framebuffer_Type) return Natural is
+     (FB_First_Page (FB) + FB_Pages (FB) - 1);
+
+   -- Check basics and that it fits in GTT
+   function Valid_FB (FB : Framebuffer_Type) return Boolean is
+     (FB.Width <= FB.Stride and FB_Last_Page (FB) <= GTT_Range'Last);
+
+   -- Also check that we don't overflow the GTT's 39-bit space
+   -- (always true with a 32-bit base)
+   function Valid_Phys_FB (FB : Framebuffer_Type; Phys_Base : Word32)
+      return Boolean is
+     (Valid_FB (FB) and
+      Int64 (Phys_Base) + Int64 (FB.Offset) + Int64 (FB_Size (FB)) <=
+         Int64 (GTT_Address_Type'Last))
+   with
+      Ghost;
+
+   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
+   with
+      Pre => Is_Initialized and Valid_Phys_FB (FB, Phys_Base);
 
 private
 
diff --git a/common/hw-gfx.ads b/common/hw-gfx.ads
index 051b326..daf648d 100644
--- a/common/hw-gfx.ads
+++ b/common/hw-gfx.ads
@@ -1,5 +1,6 @@
 --
 -- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
 --
 -- This program is free software; you can redistribute it and/or modify
 -- it under the terms of the GNU General Public License as published by
@@ -35,6 +36,9 @@
       Offset : Word32;
    end record;
 
+   function FB_Size (FB : Framebuffer_Type) return Pos32 is
+     (FB.Stride * FB.Height * Pos32 (FB.BPC) / (8 / 4));
+
    Default_FB : constant Framebuffer_Type := Framebuffer_Type'
       (Width  => 1,
        Height => 1,