Merge branch 'kms-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Aug 2010 23:00:44 +0000 (16:00 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 5 Aug 2010 23:00:44 +0000 (16:00 -0700)
* 'kms-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
  kgdb,docs: Update the kgdb docs to include kms
  drm_fb_helper: Preserve capability to use atomic kms
  i915: when kgdb is active display compression should be off
  drm/i915: use new fb debug hooks
  drm: add KGDB/KDB support
  fb: add hooks to handle KDB enter/exit
  kgdboc: Add call backs to allow kernel mode switching
  vt,console,kdb: automatically set kdb LINES variable
  vt,console,kdb: implement atomic console enter/leave functions

15 files changed:
Documentation/DocBook/kgdb.tmpl
Documentation/kernel-parameters.txt
drivers/char/vt.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_fb.c
drivers/serial/kgdboc.c
drivers/video/console/fbcon.c
drivers/video/console/fbcon.h
include/drm/drm_crtc_helper.h
include/drm/drm_fb_helper.h
include/linux/console.h
include/linux/fb.h
include/linux/kdb.h
kernel/debug/kdb/kdb_private.h

index 55f12ac37acdf105f65bd3981b8e8c1e6cf720b7..490d862c5f0ddffc748ebb56de4255ef0769c115 100644 (file)
    may be configured as a kernel built-in or a kernel loadable module.
    You can only make use of <constant>kgdbwait</constant> and early
    debugging if you build kgdboc into the kernel as a built-in.
+   <para>Optionally you can elect to activate kms (Kernel Mode
+   Setting) integration.  When you use kms with kgdboc and you have a
+   video driver that has atomic mode setting hooks, it is possible to
+   enter the debugger on the graphics console.  When the kernel
+   execution is resumed, the previous graphics mode will be restored.
+   This integration can serve as a useful tool to aid in diagnosing
+   crashes or doing analysis of memory with kdb while allowing the
+   full graphics console applications to run.
+   </para>
    </para>
    <sect2 id="kgdbocArgs">
    <title>kgdboc arguments</title>
-   <para>Usage: <constant>kgdboc=[kbd][[,]serial_device][,baud]</constant></para>
+   <para>Usage: <constant>kgdboc=[kms][[,]kbd][[,]serial_device][,baud]</constant></para>
+   <para>The order listed above must be observed if you use any of the
+   optional configurations together.
+   </para>
+   <para>Abbreviations:
+   <itemizedlist>
+   <listitem><para>kms = Kernel Mode Setting</para></listitem>
+   <listitem><para>kbd = Keyboard</para></listitem>
+   </itemizedlist>
+   </para>
+   <para>You can configure kgdboc to use the keyboard, and or a serial
+   device depending on if you are using kdb and or kgdb, in one of the
+   following scenarios.  The order listed above must be observed if
+   you use any of the optional configurations together.  Using kms +
+   only gdb is generally not a useful combination.</para>
    <sect3 id="kgdbocArgs1">
    <title>Using loadable module or built-in</title>
    <para>
    <listitem>
    <para>As a kernel loadable module:</para>
    <para>Use the command: <constant>modprobe kgdboc kgdboc=&lt;tty-device&gt;,[baud]</constant></para>
-   <para>Here are two examples of how you might formate the kgdboc
+   <para>Here are two examples of how you might format the kgdboc
    string. The first is for an x86 target using the first serial port.
    The second example is for the ARM Versatile AB using the second
    serial port.
    </sect3>
    <sect3 id="kgdbocArgs3">
    <title>More examples</title>
+   <para>You can configure kgdboc to use the keyboard, and or a serial
+   device depending on if you are using kdb and or kgdb, in one of the
+   following scenarios.</para>
    <para>You can configure kgdboc to use the keyboard, and or a serial device
    depending on if you are using kdb and or kgdb, in one of the
    following scenarios.
    <listitem><para>kdb with a keyboard</para>
    <para><constant>kgdboc=kbd</constant></para>
    </listitem>
+   <listitem><para>kdb with kernel mode setting</para>
+   <para><constant>kgdboc=kms,kbd</constant></para>
+   </listitem>
+   <listitem><para>kdb with kernel mode setting and kgdb over a serial port</para>
+   <para><constant>kgdboc=kms,kbd,ttyS0,115200</constant></para>
+   </listitem>
    </orderedlist>
    </para>
    </sect3>
@@ -637,6 +669,8 @@ Task Addr       Pid   Parent [*] cpu State Thread     Command
       <listitem><para>The logic to perform safe memory reads and writes to memory while using the debugger</para></listitem>
       <listitem><para>A full implementation for software breakpoints unless overridden by the arch</para></listitem>
       <listitem><para>The API to invoke either the kdb or kgdb frontend to the debug core.</para></listitem>
+      <listitem><para>The structures and callback API for atomic kernel mode setting.</para>
+      <para>NOTE: kgdboc is where the kms callbacks are invoked.</para></listitem>
       </itemizedlist>
       </para>
       </listitem>
@@ -747,6 +781,8 @@ Task Addr       Pid   Parent [*] cpu State Thread     Command
   </sect1>
   <sect1 id="kgdbocDesign">
   <title>kgdboc internals</title>
+  <sect2>
+  <title>kgdboc and uarts</title>
   <para>
   The kgdboc driver is actually a very thin driver that relies on the
   underlying low level to the hardware driver having "polling hooks"
@@ -754,11 +790,8 @@ Task Addr       Pid   Parent [*] cpu State Thread     Command
   implementation of kgdboc it the serial_core was changed to expose a
   low level UART hook for doing polled mode reading and writing of a
   single character while in an atomic context.  When kgdb makes an I/O
-  request to the debugger, kgdboc invokes a call back in the serial
-  core which in turn uses the call back in the UART driver.  It is
-  certainly possible to extend kgdboc to work with non-UART based
-  consoles in the future.
-  </para>
+  request to the debugger, kgdboc invokes a callback in the serial
+  core which in turn uses the callback in the UART driver.</para>
   <para>
   When using kgdboc with a UART, the UART driver must implement two callbacks in the <constant>struct uart_ops</constant>. Example from drivers/8250.c:<programlisting>
 #ifdef CONFIG_CONSOLE_POLL
@@ -772,9 +805,68 @@ Task Addr       Pid   Parent [*] cpu State Thread     Command
   that they can be called from an atomic context and have to restore
   the state of the UART chip on return such that the system can return
   to normal when the debugger detaches.  You need to be very careful
-  with any kind of lock you consider, because failing here is most
+  with any kind of lock you consider, because failing here is most likely
   going to mean pressing the reset button.
   </para>
+  </sect2>
+  <sect2 id="kgdbocKbd">
+  <title>kgdboc and keyboards</title>
+  <para>The kgdboc driver contains logic to configure communications
+  with an attached keyboard.  The keyboard infrastructure is only
+  compiled into the kernel when CONFIG_KDB_KEYBOARD=y is set in the
+  kernel configuration.</para>
+  <para>The core polled keyboard driver driver for PS/2 type keyboards
+  is in drivers/char/kdb_keyboard.c.  This driver is hooked into the
+  debug core when kgdboc populates the callback in the array
+  called <constant>kdb_poll_funcs[]</constant>.  The
+  kdb_get_kbd_char() is the top-level function which polls hardware
+  for single character input.
+  </para>
+  </sect2>
+  <sect2 id="kgdbocKms">
+  <title>kgdboc and kms</title>
+  <para>The kgdboc driver contains logic to request the graphics
+  display to switch to a text context when you are using
+  "kgdboc=kms,kbd", provided that you have a video driver which has a
+  frame buffer console and atomic kernel mode setting support.</para>
+  <para>
+  Every time the kernel
+  debugger is entered it calls kgdboc_pre_exp_handler() which in turn
+  calls con_debug_enter() in the virtual console layer.  On resuming kernel
+  execution, the kernel debugger calls kgdboc_post_exp_handler() which
+  in turn calls con_debug_leave().</para>
+  <para>Any video driver that wants to be compatible with the kernel
+  debugger and the atomic kms callbacks must implement the
+  mode_set_base_atomic, fb_debug_enter and fb_debug_leave operations.
+  For the fb_debug_enter and fb_debug_leave the option exists to use
+  the generic drm fb helper functions or implement something custom for
+  the hardware.  The following example shows the initialization of the
+  .mode_set_base_atomic operation in
+  drivers/gpu/drm/i915/intel_display.c:
+  <informalexample>
+  <programlisting>
+static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+[...]
+        .mode_set_base_atomic = intel_pipe_set_base_atomic,
+[...]
+};
+  </programlisting>
+  </informalexample>
+  </para>
+  <para>Here is an example of how the i915 driver initializes the fb_debug_enter and fb_debug_leave functions to use the generic drm helpers in
+  drivers/gpu/drm/i915/intel_fb.c:
+  <informalexample>
+  <programlisting>
+static struct fb_ops intelfb_ops = {
+[...]
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
+[...]
+};
+  </programlisting>
+  </informalexample>
+  </para>
+  </sect2>
   </sect1>
   </chapter>
   <chapter id="credits">
index ef8b11cec6e94c365df8bd3bddbc12026e2cd7ea..f72ba727441faa8dd6f06ba78931052aeb62a2b5 100644 (file)
@@ -1145,9 +1145,12 @@ and is between 256 and 4096 characters. It is defined in the file
        kgdboc=         [KGDB,HW] kgdb over consoles.
                        Requires a tty driver that supports console polling,
                        or a supported polling keyboard driver (non-usb).
-                       Serial only format: <serial_device>[,baud]
-                       keyboard only format: kbd
-                       keyboard and serial format: kbd,<serial_device>[,baud]
+                        Serial only format: <serial_device>[,baud]
+                        keyboard only format: kbd
+                        keyboard and serial format: kbd,<serial_device>[,baud]
+                       Optional Kernel mode setting:
+                        kms, kbd format: kms,kbd
+                        kms, kbd and serial format: kms,kbd,<ser_dev>[,baud]
 
        kgdbwait        [KGDB] Stop kernel execution and enter the
                        kernel debugger at the earliest opportunity.
index 7cdb6ee569cd7ca592bedfea458f0b676ec937af..4a9eb3044e52c068641d1a3ddccd3682fdf228c3 100644 (file)
 #include <linux/io.h>
 #include <asm/system.h>
 #include <linux/uaccess.h>
+#include <linux/kdb.h>
 
 #define MAX_NR_CON_DRIVER 16
 
@@ -187,10 +188,15 @@ static DECLARE_WORK(console_work, console_callback);
  * fg_console is the current virtual console,
  * last_console is the last used one,
  * want_console is the console we want to switch to,
+ * saved_* variants are for save/restore around kernel debugger enter/leave
  */
 int fg_console;
 int last_console;
 int want_console = -1;
+int saved_fg_console;
+int saved_last_console;
+int saved_want_console;
+int saved_vc_mode;
 
 /*
  * For each existing display, we have a pointer to console currently visible
@@ -3413,6 +3419,78 @@ int con_is_bound(const struct consw *csw)
 }
 EXPORT_SYMBOL(con_is_bound);
 
+/**
+ * con_debug_enter - prepare the console for the kernel debugger
+ * @sw: console driver
+ *
+ * Called when the console is taken over by the kernel debugger, this
+ * function needs to save the current console state, then put the console
+ * into a state suitable for the kernel debugger.
+ *
+ * RETURNS:
+ * Zero on success, nonzero if a failure occurred when trying to prepare
+ * the console for the debugger.
+ */
+int con_debug_enter(struct vc_data *vc)
+{
+       int ret = 0;
+
+       saved_fg_console = fg_console;
+       saved_last_console = last_console;
+       saved_want_console = want_console;
+       saved_vc_mode = vc->vc_mode;
+       vc->vc_mode = KD_TEXT;
+       console_blanked = 0;
+       if (vc->vc_sw->con_debug_enter)
+               ret = vc->vc_sw->con_debug_enter(vc);
+#ifdef CONFIG_KGDB_KDB
+       /* Set the initial LINES variable if it is not already set */
+       if (vc->vc_rows < 999) {
+               int linecount;
+               char lns[4];
+               const char *setargs[3] = {
+                       "set",
+                       "LINES",
+                       lns,
+               };
+               if (kdbgetintenv(setargs[0], &linecount)) {
+                       snprintf(lns, 4, "%i", vc->vc_rows);
+                       kdb_set(2, setargs);
+               }
+       }
+#endif /* CONFIG_KGDB_KDB */
+       return ret;
+}
+EXPORT_SYMBOL_GPL(con_debug_enter);
+
+/**
+ * con_debug_leave - restore console state
+ * @sw: console driver
+ *
+ * Restore the console state to what it was before the kernel debugger
+ * was invoked.
+ *
+ * RETURNS:
+ * Zero on success, nonzero if a failure occurred when trying to restore
+ * the console.
+ */
+int con_debug_leave(void)
+{
+       struct vc_data *vc;
+       int ret = 0;
+
+       fg_console = saved_fg_console;
+       last_console = saved_last_console;
+       want_console = saved_want_console;
+       vc_cons[fg_console].d->vc_mode = saved_vc_mode;
+
+       vc = vc_cons[fg_console].d;
+       if (vc->vc_sw->con_debug_leave)
+               ret = vc->vc_sw->con_debug_leave(vc);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(con_debug_leave);
+
 /**
  * register_con_driver - register console driver to console layer
  * @csw: console driver
index 719662034bbfee9d9d1f2be26bfe1199c985d0a0..de82e201d6826d8ac0e422b22a2d0a93f8312d82 100644 (file)
@@ -241,6 +241,80 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
        return 0;
 }
 
+int drm_fb_helper_debug_enter(struct fb_info *info)
+{
+       struct drm_fb_helper *helper = info->par;
+       struct drm_crtc_helper_funcs *funcs;
+       int i;
+
+       if (list_empty(&kernel_fb_helper_list))
+               return false;
+
+       list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
+               for (i = 0; i < helper->crtc_count; i++) {
+                       struct drm_mode_set *mode_set =
+                               &helper->crtc_info[i].mode_set;
+
+                       if (!mode_set->crtc->enabled)
+                               continue;
+
+                       funcs = mode_set->crtc->helper_private;
+                       funcs->mode_set_base_atomic(mode_set->crtc,
+                                                   mode_set->fb,
+                                                   mode_set->x,
+                                                   mode_set->y);
+
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_debug_enter);
+
+/* Find the real fb for a given fb helper CRTC */
+static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_crtc *c;
+
+       list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+               if (crtc->base.id == c->base.id)
+                       return c->fb;
+       }
+
+       return NULL;
+}
+
+int drm_fb_helper_debug_leave(struct fb_info *info)
+{
+       struct drm_fb_helper *helper = info->par;
+       struct drm_crtc *crtc;
+       struct drm_crtc_helper_funcs *funcs;
+       struct drm_framebuffer *fb;
+       int i;
+
+       for (i = 0; i < helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
+               crtc = mode_set->crtc;
+               funcs = crtc->helper_private;
+               fb = drm_mode_config_fb(crtc);
+
+               if (!crtc->enabled)
+                       continue;
+
+               if (!fb) {
+                       DRM_ERROR("no fb to restore??\n");
+                       continue;
+               }
+
+               funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
+                                           crtc->y);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_debug_leave);
+
 bool drm_fb_helper_force_kernel_mode(void)
 {
        int i = 0;
@@ -611,7 +685,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
        struct drm_framebuffer *fb = fb_helper->fb;
        int depth;
 
-       if (var->pixclock != 0)
+       if (var->pixclock != 0 || in_dbg_master())
                return -EINVAL;
 
        /* Need to resize the fb object !!! */
index 5e21b31198249349dc5e69265dbe20c8b0a23d02..714bf539918b2a495f7f66b27d886d6881582691 100644 (file)
@@ -975,7 +975,10 @@ void
 intel_wait_for_vblank(struct drm_device *dev)
 {
        /* Wait for 20ms, i.e. one cycle at 50hz. */
-       msleep(20);
+       if (in_dbg_master())
+               mdelay(20); /* The kernel debugger cannot call msleep() */
+       else
+               msleep(20);
 }
 
 /* Parameters have changed, update FBC info */
@@ -1248,6 +1251,10 @@ static void intel_update_fbc(struct drm_crtc *crtc,
                goto out_disable;
        }
 
+       /* If the kernel debugger is active, always disable compression */
+       if (in_dbg_master())
+               goto out_disable;
+
        if (intel_fbc_enabled(dev)) {
                /* We can re-enable it in this case, but need to update pitch */
                if ((fb->pitch > dev_priv->cfb_pitch) ||
@@ -1314,6 +1321,98 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
        return 0;
 }
 
+/* Assume fb object is pinned & idle & fenced and just update base pointers */
+static int
+intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+                          int x, int y)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_framebuffer *intel_fb;
+       struct drm_i915_gem_object *obj_priv;
+       struct drm_gem_object *obj;
+       int plane = intel_crtc->plane;
+       unsigned long Start, Offset;
+       int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR);
+       int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF);
+       int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
+       int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+       u32 dspcntr;
+
+       switch (plane) {
+       case 0:
+       case 1:
+               break;
+       default:
+               DRM_ERROR("Can't update plane %d in SAREA\n", plane);
+               return -EINVAL;
+       }
+
+       intel_fb = to_intel_framebuffer(fb);
+       obj = intel_fb->obj;
+       obj_priv = to_intel_bo(obj);
+
+       dspcntr = I915_READ(dspcntr_reg);
+       /* Mask out pixel format bits in case we change it */
+       dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+       switch (fb->bits_per_pixel) {
+       case 8:
+               dspcntr |= DISPPLANE_8BPP;
+               break;
+       case 16:
+               if (fb->depth == 15)
+                       dspcntr |= DISPPLANE_15_16BPP;
+               else
+                       dspcntr |= DISPPLANE_16BPP;
+               break;
+       case 24:
+       case 32:
+               dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+               break;
+       default:
+               DRM_ERROR("Unknown color depth\n");
+               return -EINVAL;
+       }
+       if (IS_I965G(dev)) {
+               if (obj_priv->tiling_mode != I915_TILING_NONE)
+                       dspcntr |= DISPPLANE_TILED;
+               else
+                       dspcntr &= ~DISPPLANE_TILED;
+       }
+
+       if (IS_IRONLAKE(dev))
+               /* must disable */
+               dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
+       I915_WRITE(dspcntr_reg, dspcntr);
+
+       Start = obj_priv->gtt_offset;
+       Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
+
+       DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+       I915_WRITE(dspstride, fb->pitch);
+       if (IS_I965G(dev)) {
+               I915_WRITE(dspbase, Offset);
+               I915_READ(dspbase);
+               I915_WRITE(dspsurf, Start);
+               I915_READ(dspsurf);
+               I915_WRITE(dsptileoff, (y << 16) | x);
+       } else {
+               I915_WRITE(dspbase, Start + Offset);
+               I915_READ(dspbase);
+       }
+
+       if ((IS_I965G(dev) || plane == 0))
+               intel_update_fbc(crtc, &crtc->mode);
+
+       intel_wait_for_vblank(dev);
+       intel_increase_pllclock(crtc, true);
+
+       return 0;
+}
+
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                    struct drm_framebuffer *old_fb)
@@ -4814,6 +4913,7 @@ static const struct drm_crtc_helper_funcs intel_helper_funcs = {
        .mode_fixup = intel_crtc_mode_fixup,
        .mode_set = intel_crtc_mode_set,
        .mode_set_base = intel_pipe_set_base,
+       .mode_set_base_atomic = intel_pipe_set_base_atomic,
        .prepare = intel_crtc_prepare,
        .commit = intel_crtc_commit,
        .load_lut = intel_crtc_load_lut,
index 3e18c9e7729b6a475ce270d3329acf3d54288f1c..54acd8b534df4bd092c71f4e8ce0998b7cd9dc09 100644 (file)
@@ -61,6 +61,8 @@ static struct fb_ops intelfb_ops = {
        .fb_pan_display = drm_fb_helper_pan_display,
        .fb_blank = drm_fb_helper_blank,
        .fb_setcmap = drm_fb_helper_setcmap,
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
 };
 
 static int intelfb_create(struct intel_fbdev *ifbdev,
index a9a94ae7234951f3b30b25829c67d1ee13a3a8f2..39f9a1adaa756b408a18ce02e6dfe662350f2246 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kdb.h>
 #include <linux/tty.h>
 #include <linux/console.h>
+#include <linux/vt_kern.h>
 
 #define MAX_CONFIG_LEN         40
 
@@ -31,6 +32,7 @@ static struct kparam_string kps = {
        .maxlen                 = MAX_CONFIG_LEN,
 };
 
+static int kgdboc_use_kms;  /* 1 if we use kernel mode switching */
 static struct tty_driver       *kgdb_tty_driver;
 static int                     kgdb_tty_line;
 
@@ -104,6 +106,12 @@ static int configure_kgdboc(void)
        kgdboc_io_ops.is_console = 0;
        kgdb_tty_driver = NULL;
 
+       kgdboc_use_kms = 0;
+       if (strncmp(cptr, "kms,", 4) == 0) {
+               cptr += 4;
+               kgdboc_use_kms = 1;
+       }
+
        if (kgdboc_register_kbd(&cptr))
                goto do_register;
 
@@ -201,8 +209,14 @@ static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
        return configure_kgdboc();
 }
 
+static int dbg_restore_graphics;
+
 static void kgdboc_pre_exp_handler(void)
 {
+       if (!dbg_restore_graphics && kgdboc_use_kms) {
+               dbg_restore_graphics = 1;
+               con_debug_enter(vc_cons[fg_console].d);
+       }
        /* Increment the module count when the debugger is active */
        if (!kgdb_connected)
                try_module_get(THIS_MODULE);
@@ -213,6 +227,10 @@ static void kgdboc_post_exp_handler(void)
        /* decrement the module count when the debugger detaches */
        if (!kgdb_connected)
                module_put(THIS_MODULE);
+       if (kgdboc_use_kms && dbg_restore_graphics) {
+               dbg_restore_graphics = 0;
+               con_debug_leave();
+       }
 }
 
 static struct kgdb_io kgdboc_io_ops = {
index b0a3fa00706d2e25896fc862aa1bcc116010a6c7..3b3f5749af925c83d055fa41df0a1c6baed92e5a 100644 (file)
@@ -2342,6 +2342,30 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
        return 0;
 }
 
+static int fbcon_debug_enter(struct vc_data *vc)
+{
+       struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+       struct fbcon_ops *ops = info->fbcon_par;
+
+       ops->save_graphics = ops->graphics;
+       ops->graphics = 0;
+       if (info->fbops->fb_debug_enter)
+               info->fbops->fb_debug_enter(info);
+       fbcon_set_palette(vc, color_table);
+       return 0;
+}
+
+static int fbcon_debug_leave(struct vc_data *vc)
+{
+       struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
+       struct fbcon_ops *ops = info->fbcon_par;
+
+       ops->graphics = ops->save_graphics;
+       if (info->fbops->fb_debug_leave)
+               info->fbops->fb_debug_leave(info);
+       return 0;
+}
+
 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
 {
        u8 *fontdata = vc->vc_font.data;
@@ -3276,6 +3300,8 @@ static const struct consw fb_con = {
        .con_screen_pos         = fbcon_screen_pos,
        .con_getxy              = fbcon_getxy,
        .con_resize             = fbcon_resize,
+       .con_debug_enter        = fbcon_debug_enter,
+       .con_debug_leave        = fbcon_debug_leave,
 };
 
 static struct notifier_block fbcon_event_notifier = {
index 89a346880ec014897f051168013d4af7fc5cd091..6bd2e0c7f209d402cbbf75bb3b088a7c58f3ed24 100644 (file)
@@ -74,6 +74,7 @@ struct fbcon_ops {
        int    cursor_reset;
        int    blank_state;
        int    graphics;
+       int    save_graphics; /* for debug enter/leave */
        int    flags;
        int    rotate;
        int    cur_rotate;
index 1121f7799c6ff527838ec50052d51bc10b10e2e7..10f7d03e58a93477327a3be699dc4c802c5cd687 100644 (file)
@@ -60,6 +60,8 @@ struct drm_crtc_helper_funcs {
        /* Move the crtc on the current fb to the given position *optional* */
        int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
                             struct drm_framebuffer *old_fb);
+       int (*mode_set_base_atomic)(struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb, int x, int y);
 
        /* reload the current crtc LUT */
        void (*load_lut)(struct drm_crtc *crtc);
index f0a6afc47e761a9aba1507d16a7d80feab95e741..f22e7fe4b6dbf25815580ad3625fd09f5aed3946 100644 (file)
@@ -32,6 +32,8 @@
 
 struct drm_fb_helper;
 
+#include <linux/kgdb.h>
+
 struct drm_fb_helper_crtc {
        uint32_t crtc_id;
        struct drm_mode_set mode_set;
@@ -78,6 +80,7 @@ struct drm_fb_helper_connector {
 
 struct drm_fb_helper {
        struct drm_framebuffer *fb;
+       struct drm_framebuffer *saved_fb;
        struct drm_device *dev;
        struct drm_display_mode *mode;
        int crtc_count;
@@ -126,5 +129,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
+int drm_fb_helper_debug_enter(struct fb_info *info);
+int drm_fb_helper_debug_leave(struct fb_info *info);
 
 #endif
index dcca5339ceb3c65711d9fce57d27bec70153029f..f76fc297322dcb0395dad79c04cdd32aac80815f 100644 (file)
@@ -55,6 +55,16 @@ struct consw {
        void    (*con_invert_region)(struct vc_data *, u16 *, int);
        u16    *(*con_screen_pos)(struct vc_data *, int);
        unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
+       /*
+        * Prepare the console for the debugger.  This includes, but is not
+        * limited to, unblanking the console, loading an appropriate
+        * palette, and allowing debugger generated output.
+        */
+       int     (*con_debug_enter)(struct vc_data *);
+       /*
+        * Restore the console to its pre-debug state as closely as possible.
+        */
+       int     (*con_debug_leave)(struct vc_data *);
 };
 
 extern const struct consw *conswitchp;
@@ -69,6 +79,9 @@ int register_con_driver(const struct consw *csw, int first, int last);
 int unregister_con_driver(const struct consw *csw);
 int take_over_console(const struct consw *sw, int first, int last, int deflt);
 void give_up_console(const struct consw *sw);
+int con_debug_enter(struct vc_data *vc);
+int con_debug_leave(void);
+
 /* scroll */
 #define SM_UP       (1)
 #define SM_DOWN     (2)
index e7445df44d6c9324fd32a85ee1145415ecbd7960..0c5659c41b01c502160ca089bd758a36963f7918 100644 (file)
@@ -3,6 +3,9 @@
 
 #include <linux/types.h>
 #include <linux/i2c.h>
+#ifdef __KERNEL__
+#include <linux/kgdb.h>
+#endif /* __KERNEL__ */
 
 /* Definitions of frame buffers                                                */
 
@@ -607,6 +610,12 @@ struct fb_deferred_io {
  * LOCKING NOTE: those functions must _ALL_ be called with the console
  * semaphore held, this is the only suitable locking mechanism we have
  * in 2.6. Some may be called at interrupt time at this point though.
+ *
+ * The exception to this is the debug related hooks.  Putting the fb
+ * into a debug state (e.g. flipping to the kernel console) and restoring
+ * it must be done in a lock-free manner, so low level drivers should
+ * keep track of the initial console (if applicable) and may need to
+ * perform direct, unlocked hardware writes in these hooks.
  */
 
 struct fb_ops {
@@ -676,6 +685,10 @@ struct fb_ops {
 
        /* teardown any resources to do with this framebuffer */
        void (*fb_destroy)(struct fb_info *info);
+
+       /* called at KDB enter and leave time to prepare the console */
+       int (*fb_debug_enter)(struct fb_info *info);
+       int (*fb_debug_leave)(struct fb_info *info);
 };
 
 #ifdef CONFIG_FB_TILEBLITTING
index ccb2b3ec0fe899769c161a844b13204d3894fd77..ea6e5244ed3f0b77d8ff644ee975d21945494a17 100644 (file)
@@ -114,4 +114,8 @@ enum {
        KDB_INIT_EARLY,
        KDB_INIT_FULL,
 };
+
+extern int kdbgetintenv(const char *, int *);
+extern int kdb_set(int, const char **);
+
 #endif /* !_KDB_H */
index 97d3ba69775df3b66120f0e7579fc8fba0d896da..c438f545a3217eca47dcae2ff98618467971a9f7 100644 (file)
@@ -144,9 +144,7 @@ extern int kdb_getword(unsigned long *, unsigned long, size_t);
 extern int kdb_putword(unsigned long, unsigned long, size_t);
 
 extern int kdbgetularg(const char *, unsigned long *);
-extern int kdb_set(int, const char **);
 extern char *kdbgetenv(const char *);
-extern int kdbgetintenv(const char *, int *);
 extern int kdbgetaddrarg(int, const char **, int*, unsigned long *,
                         long *, char **);
 extern int kdbgetsymval(const char *, kdb_symtab_t *);