Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9779714c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
parents 89a6c8cb 65b5ac14
Loading
Loading
Loading
Loading
+100 −8
Original line number Diff line number Diff line
@@ -199,10 +199,33 @@
   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>
@@ -212,7 +235,7 @@
   <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.
@@ -240,6 +263,9 @@
   </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.
@@ -255,6 +281,12 @@
   <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"
@@ -755,10 +791,7 @@ Task Addr Pid Parent [*] cpu State Thread Command
  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 callback 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>
  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">
+6 −3
Original line number Diff line number Diff line
@@ -1148,6 +1148,9 @@ and is between 256 and 4096 characters. It is defined in the file
			 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.
+78 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@
#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
+75 −1
Original line number Diff line number Diff line
@@ -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 !!! */
+101 −1
Original line number Diff line number Diff line
@@ -975,6 +975,9 @@ void
intel_wait_for_vblank(struct drm_device *dev)
{
	/* Wait for 20ms, i.e. one cycle at 50hz. */
	if (in_dbg_master())
		mdelay(20); /* The kernel debugger cannot call msleep() */
	else
		msleep(20);
}

@@ -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,
Loading