gfx_test: Set our own framebuffers up, update README
Use GMA.Setup_Default_FB() to allocate framebuffers in the stolen
memory. To help with setups where we can't unload the i915 driver,
back up and restore the current GTT setup and framebuffer contents.
Also add a wrapper script and update the README.
Change-Id: I10790d35d38b7b211f41b2452f6d2baf17372e31
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/20604
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/README.md b/README.md
index 7bfb504..5c54f0a 100644
--- a/README.md
+++ b/README.md
@@ -45,11 +45,15 @@
or overwrite the config filename by specifying `cnf=<configfile>` on
the make command line.
+By default most debug messages won't be compiled into the binary. To
+include them into the build, set `DEBUG=1` on the command line or in
+your `.config`.
+
Let's install *libhwbase*. We'll need `configs/linux` to build regular
Linux executables:
$ cd libhwbase
- $ make cnf=configs/linux install
+ $ make DEBUG=1 cnf=configs/linux install
By default this installs into a new subdirectory `dest`. You can however
overwrite this decision by specifying `DESTDIR=`.
@@ -65,7 +69,7 @@
Linux test application:
$ cd ../libgfxinit
- $ make cnf=configs/sandybridge gfx_test
+ $ make DEBUG=1 cnf=configs/sandybridge gfx_test
The resulting binary is `build/gfx_test`.
@@ -73,20 +77,19 @@
Testing libgfxinit on Linux
===========================
-In its current state `gfx_test` doesn't know how to set up a frame-
-buffer. It just assumes that enough memory is mapped. This is known
-to work well, after running the VBIOS but before the Linux driver
-*i915* took over (e.g. when booting with `nomodeset` in the kernel
-command line or with *i915* blacklisted). After running *i915* it
-only works by chance.
+`gfx_test` sets up its own framebuffer in the *stolen memory*. It
+backs any current framebuffer mapping and contents up first and re-
+stores it before exiting. This works somehow even while the *i915*
+driver is running. A wrapper script `gfxtest/gfx_test.sh` is pro-
+vided to help with the setup. It switches to a text console first
+and tries to unload the *i915* driver. But ignores failures to do
+so (it won't work if you still have any application running that
+uses the gfx driver, e.g. an X server).
-When running `gfx_test` (as root), it will access the graphics hard-
-ware through the sysfs PCI interface. The path is
-
- /sys/devices/pci0000:00/0000:00:02.0/
-
-for all supported platforms.
+ # gfxtest/gfx_test.sh
If you chose the right config above, you should be presented with a
-nice test image. However, `gfx_test` is one-way only: The graphics
-hardware will stay in this state, until another driver takes over.
+nice test image. But please be prepared that your console might be
+stuck in that state afterwards. You can try to run it with *i915*
+deactivated then (e.g. when booting with `nomodeset` in the kernel
+command line or with *i915* blacklisted) and loading it afterwards.
diff --git a/gfxtest/gfx_test.sh b/gfxtest/gfx_test.sh
new file mode 100755
index 0000000..9df0005
--- /dev/null
+++ b/gfxtest/gfx_test.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+set -e
+
+usage="Usage: $0 [seconds]\n"
+
+err_msgs=
+command -v chvt >/dev/null || err_msgs="${err_msgs}"'Need `chvt`\n'
+command -v openvt >/dev/null || err_msgs="${err_msgs}"'Need `openvt`\n'
+command -v fgconsole >/dev/null || err_msgs="${err_msgs}"'Need `fgconsole`\n'
+[ -n "$err_msgs" ] && err_msgs="${err_msgs}"'e.g. install the `kbd` package\n'
+
+[ -x build/gfx_test ] || \
+ err_msgs="${err_msgs}"'Please run from *libgfxinit* source dir and build `gfx_test` first.\n'
+
+[ "$#" -gt 1 ] && err_msgs="${err_msgs}${usage}"
+
+if [ -n "$err_msgs" ]; then
+ printf "$err_msgs"
+ exit 1
+fi
+
+if [ "$#" -lt 1 ]; then
+ # default duration of 5s
+ set 5
+fi
+
+reload_i915=0
+prepare_vt() {
+ # switch VT, we might be in X
+ orig_vt=`fgconsole`
+ openvt -s -- true
+
+ # poll until the VT switch is done
+ while [ `fgconsole` -eq $orig_vt ]; do :; done
+
+ # take i915 out of charge
+ echo 0 >/sys/devices/virtual/vtconsole/vtcon1/bind
+
+ # try unloading it
+ if lsmod | grep -q i915 && modprobe -r i915 >/dev/null 2>&1; then
+ reload_i915=1
+ fi
+}
+
+restore_vt() {
+ # reload i915
+ if [ $reload_i915 -eq 1 ]; then
+ modprobe i915
+ fi
+
+ # put i915 back in charge
+ echo 1 >/sys/devices/virtual/vtconsole/vtcon1/bind
+
+ # return to original VT
+ chvt $orig_vt
+}
+
+prepare_vt
+
+build/gfx_test "$@" || true
+
+restore_vt
diff --git a/gfxtest/hw-gfx-gma-gfx_test.adb b/gfxtest/hw-gfx-gma-gfx_test.adb
index ad396fd..d870401 100644
--- a/gfxtest/hw-gfx-gma-gfx_test.adb
+++ b/gfxtest/hw-gfx-gma-gfx_test.adb
@@ -2,10 +2,12 @@
with Ada.Command_Line;
with Interfaces.C;
+with HW.Time;
with HW.Debug;
with HW.PCI.Dev;
with HW.MMIO_Range;
with HW.GFX.GMA;
+with HW.GFX.GMA.Config;
with HW.GFX.GMA.Display_Probing;
package body HW.GFX.GMA.GFX_Test
@@ -14,6 +16,32 @@
package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
+ type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
+ type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
+ package GTT is new MMIO_Range
+ (Base_Addr => 0,
+ Element_T => GTT_PTE_Type,
+ Index_T => GTT_Range,
+ Array_T => GTT_Registers_Type);
+
+ GTT_Backup : GTT_Registers_Type;
+
+ procedure Backup_GTT
+ is
+ begin
+ for Idx in GTT_Range loop
+ GTT.Read (GTT_Backup (Idx), Idx);
+ end loop;
+ end Backup_GTT;
+
+ procedure Restore_GTT
+ is
+ begin
+ for Idx in GTT_Range loop
+ GTT.Write (Idx, GTT_Backup (Idx));
+ end loop;
+ end Restore_GTT;
+
type Pixel_Type is record
Red : Byte;
Green : Byte;
@@ -46,7 +74,27 @@
package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
- Pipes : GMA.Pipe_Configs;
+ Screen_Backup : Screen_Type;
+
+ procedure Backup_Screen (FB : Framebuffer_Type)
+ is
+ First : constant Screen_Index := Natural (FB.Offset) / 4;
+ Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+ begin
+ for Idx in Screen_Index range First .. Last loop
+ Screen.Read (Screen_Backup (Idx), Idx);
+ end loop;
+ end Backup_Screen;
+
+ procedure Restore_Screen (FB : Framebuffer_Type)
+ is
+ First : constant Screen_Index := Natural (FB.Offset) / 4;
+ Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+ begin
+ for Idx in Screen_Index range First .. Last loop
+ Screen.Write (Idx, Screen_Backup (Idx));
+ end loop;
+ end Restore_Screen;
function Fill
(X, Y : Natural;
@@ -113,11 +161,14 @@
Offset := Offset + Word32 (FB.Stride * FB.Height * 4);
end Calc_Framebuffer;
+ Pipes : GMA.Pipe_Configs;
+
procedure Prepare_Configs
is
use type HW.GFX.GMA.Port_Type;
Offset : Word32 := 0;
+ Success : Boolean;
begin
GMA.Display_Probing.Scan_Ports (Pipes);
@@ -127,12 +178,27 @@
(FB => Pipes (Pipe).Framebuffer,
Mode => Pipes (Pipe).Mode,
Offset => Offset);
+ GMA.Setup_Default_FB
+ (FB => Pipes (Pipe).Framebuffer,
+ Clear => False,
+ Success => Success);
+ if not Success then
+ Pipes (Pipe).Port := GMA.Disabled;
+ end if;
end if;
end loop;
GMA.Dump_Configs (Pipes);
end Prepare_Configs;
+ procedure Print_Usage
+ is
+ begin
+ Debug.Put_Line
+ ("Usage: " & Ada.Command_Line.Command_Name & " <delay seconds>");
+ Debug.New_Line;
+ end Print_Usage;
+
procedure Main
is
use type HW.GFX.GMA.Port_Type;
@@ -141,12 +207,21 @@
Res_Addr : Word64;
+ Delay_S : Natural;
+
Dev_Init,
Initialized : Boolean;
function iopl (level : Interfaces.C.int) return Interfaces.C.int;
pragma Import (C, iopl, "iopl");
begin
+ if Ada.Command_Line.Argument_Count /= 1 then
+ Print_Usage;
+ return;
+ end if;
+
+ Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
+
if iopl (3) /= 0 then
Debug.Put_Line ("Failed to change i/o privilege level.");
return;
@@ -158,6 +233,13 @@
return;
end if;
+ Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
+ if Res_Addr = 0 then
+ Debug.Put_Line ("Failed to map PCI resource0.");
+ return;
+ end if;
+ GTT.Set_Base_Address (Res_Addr);
+
Dev.Map (Res_Addr, PCI.Res2, WC => True);
if Res_Addr = 0 then
Debug.Put_Line ("Failed to map PCI resource2.");
@@ -170,17 +252,29 @@
Success => Initialized);
if Initialized then
+ Backup_GTT;
+
Prepare_Configs;
GMA.Update_Outputs (Pipes);
for Pipe in GMA.Pipe_Index loop
if Pipes (Pipe).Port /= GMA.Disabled then
+ Backup_Screen (Pipes (Pipe).Framebuffer);
Test_Screen
(Framebuffer => Pipes (Pipe).Framebuffer,
Pipe => Pipe);
end if;
end loop;
+
+ Time.M_Delay (Delay_S * 1_000);
+
+ for Pipe in GMA.Pipe_Index loop
+ if Pipes (Pipe).Port /= GMA.Disabled then
+ Restore_Screen (Pipes (Pipe).Framebuffer);
+ end if;
+ end loop;
+ Restore_GTT;
end if;
end Main;