From 3b654a0991f1a499a754dd638f57f6d75c89b78e Mon Sep 17 00:00:00 2001 From: Nico Huber Date: Sat, 15 Jul 2017 22:27:14 +0200 Subject: [PATCH] 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 Reviewed-on: https://review.coreboot.org/20604 Reviewed-by: Arthur Heymans --- README.md | 35 ++++++------ gfxtest/gfx_test.sh | 63 ++++++++++++++++++++++ gfxtest/hw-gfx-gma-gfx_test.adb | 96 ++++++++++++++++++++++++++++++++- 3 files changed, 177 insertions(+), 17 deletions(-) create mode 100755 gfxtest/gfx_test.sh diff --git a/README.md b/README.md index 7bfb504..5c54f0a 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,15 @@ in `configs/`, e.g.: or overwrite the config filename by specifying `cnf=` 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 @@ The makefile knows an additional target `gfx_test` to build a small 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 @@ The resulting binary is `build/gfx_test`. 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. - -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/ +`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). -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.Unchecked_Conversion; 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 @@ is 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 @@ is 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 @@ is 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 @@ is (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 & " "); + Debug.New_Line; + end Print_Usage; + procedure Main is use type HW.GFX.GMA.Port_Type; @@ -141,12 +207,21 @@ is 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 @@ is 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 @@ is 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;