I don't know if anyone else would find this useful or not. This patch
adds support to log all console IO to a buffer. The dmesg command can
then display the buffer utilizing more-style paging or dump it to a
remote host via TCP.

I used this to debug my PCI-e InfiniBand sanboot setup. Vintage serial
ports weren't available on the computer so this was the easiest option.
(Although trying out QEMU's pci-e pass-through was a close second.)

The entire code is modularized using gPXE's awesome linker table
methodology and this patch sets it to disabled in the main config by
default. 

Matthew Lowe

---------------------

>From f5102645798f3f6422fc79b0919523e6c6707bcd Mon Sep 17 00:00:00 2001
From: Matthew Lowe <[email protected]>
Date: Sat, 3 Jul 2010 16:24:58 +0000
Subject: [PATCH] Added command "dmesg" to provide a means of logging and
 replaying console output. Dmesg can display output via pages
 directly through the command line interface, or it can dump
 the entire buffer to a remote host via a TCP connection.

There are three components to dmesg:
 - dmesg memory buffer (core/dmesg.c)
 - dmesg console driver (core/dmesg_console.c)
 - dmesg userspace command (hci/commandline/dmesg_cmd.c)

Signed-off-by: Matthew Lowe <[email protected]>
---
 src/config/config.c          |    5 +
 src/config/general.h         |    4 +
 src/core/dmesg.c             |  170 ++++++++++++++++
 src/core/dmesg_console.c     |   56 ++++++
 src/hci/commands/dmesg_cmd.c |  439
++++++++++++++++++++++++++++++++++++++++++
 src/include/gpxe/dmesg.h     |   57 ++++++
 src/include/gpxe/errfile.h   |    2 +
 7 files changed, 733 insertions(+), 0 deletions(-)
 create mode 100644 src/core/dmesg.c
 create mode 100644 src/core/dmesg_console.c
 create mode 100644 src/hci/commands/dmesg_cmd.c
 create mode 100644 src/include/gpxe/dmesg.h

diff --git a/src/config/config.c b/src/config/config.c
index a6e7622..7b44564 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -252,6 +252,11 @@ REQUIRE_OBJECT ( gdbidt );
 REQUIRE_OBJECT ( gdbudp );
 REQUIRE_OBJECT ( gdbstub_cmd );
 #endif
+#ifdef DMESG
+REQUIRE_OBJECT ( dmesg );
+REQUIRE_OBJECT ( dmesg_console );
+REQUIRE_OBJECT ( dmesg_cmd );
+#endif
 
 /*
  * Drag in objects that are always required, but not dragged in via
diff --git a/src/config/general.h b/src/config/general.h
index bfab5b6..244acaf 100644
--- a/src/config/general.h
+++ b/src/config/general.h
@@ -144,6 +144,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #undef GDBSERIAL               /* Remote GDB debugging over serial */
 #undef GDBUDP                  /* Remote GDB debugging over UDP
                                 * (both may be set) */
+#undef  DMESG                   /* Console memory buffer for debugging
*/
+#define  DMESG_PAGE_SIZE 10240   /* Memory buffer page size */
+#define  DMESG_PAGES 20          /* Total memory buffer size is
+                                 * pages * page_size */
 
 #include <config/local/general.h>
 
diff --git a/src/core/dmesg.c b/src/core/dmesg.c
new file mode 100644
index 0000000..409cbc4
--- /dev/null
+++ b/src/core/dmesg.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 Matthew Lowe <[email protected]>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <curses.h>
+#include <errno.h>
+#include <config/general.h>
+#include <gpxe/dmesg.h>
+#include <gpxe/init.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/errfile.h>
+
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct dmesg_buffer dmesg_buffer = {
+  .buffer = NULL,
+  .pos = NULL,
+  .size = 0,
+  .locked = FALSE,
+};
+
+/**
+ * 
+ * Cycle data buffer by one page
+ *
+ */
+void shift_one_page ()
+{
+  int len = (int) ( dmesg_buffer.pos + 1 - dmesg_buffer.buffer) -
DMESG_PAGE_SIZE;
+
+  if ( len <= 0)
+    return;
+
+  /* Copy buffer memory and include null terminator */
+  memcpy ( dmesg_buffer.buffer, dmesg_buffer.buffer + DMESG_PAGE_SIZE,

+          len );
+
+  dmesg_buffer.pos -= DMESG_PAGE_SIZE;
+}
+
+/**
+ *
+ * Write character to buffer
+ * @v ch              Character to write
+ *
+ */
+void dmesg_putc ( int ch ) {
+
+  /* Ensure the buffer is not locked and is allocated */
+  if ( dmesg_buffer.locked || !dmesg_buffer.size )
+    return;
+
+  /* Remap delete to backspace */
+  if ( ch == 0x7f )
+    ch = 0x08;
+
+  /* If we're out of buffer space (include NULL terminator), 
+   * cycle the buffer one page */
+  if ( (unsigned int) ( dmesg_buffer.pos + 1 - dmesg_buffer.buffer ) ==
+       dmesg_buffer.size ) 
+    shift_one_page ();
+  
+  *(dmesg_buffer.pos++) = (char) ch;
+
+  /* Null terminate buffer for convenience, but do not increment write
position */
+  *dmesg_buffer.pos = '\0';
+
+}
+
+/**
+ * Dmesg getc console driver function
+ * @ret ch          Always 0, no input to return
+ */
+int dmesg_getc ( void ) {
+  return 0; 
+}
+
+/**
+ * Dmesg ischar console driver function
+ * @ret status      Always 0, no input to return
+ */
+int dmesg_ischar ( void ) {
+  return 0;
+}
+
+/**
+ * Initialize dmesg memory buffer
+ */
+static void dmesg_init ( void ) {
+  
+  /* If buffer is already initialized, do not initialize again */
+  if ( dmesg_buffer.buffer )
+    return;
+
+#if DMESG_PAGES < 2
+# error "DMESG_PAGES must be at least 2"
+#endif
+
+#if DMESG_PAGE_SIZE < 2
+# error "DMESG_PAGES must be at least 2"
+#endif
+
+  dmesg_buffer.user_buffer = umalloc ( DMESG_PAGE_SIZE * DMESG_PAGES );
+
+  if ( !dmesg_buffer.user_buffer )
+    {
+      DBGC ( dmesg_buffer, "Could not initiliaze dmesg console buffer:
%s\n", strerror (-ENOMEM) );
+      goto out;
+    }
+ 
+  dmesg_buffer.buffer = (char *) user_to_phys (
dmesg_buffer.user_buffer, 0 );
+
+  dmesg_buffer.size = DMESG_PAGE_SIZE * DMESG_PAGES;
+  dmesg_buffer.pos = dmesg_buffer.buffer;
+
+ out:
+       return;
+}
+
+/**
+ * Free dmesg buffer
+ * @v __unused          Unused
+ */
+static void dmesg_fini ( int flags __unused ) {
+
+  /* Sanity check - don't free the buffer while in use */
+  if ( dmesg_buffer.locked )
+    {
+      DBGC ( dmesg_buffer, "Could not free dmesg buffer because buffer
is locked.\n" );
+      return;
+    }
+
+  /* Only free buffer if one was allocated */
+  if ( dmesg_buffer.user_buffer )
+    ufree ( dmesg_buffer.user_buffer );
+  
+  /* Zero memory buffer access structure */
+  memset ( &dmesg_buffer, 0, sizeof (dmesg_buffer) );  
+}
+
+/**
+ * Dmesg driver initialization function
+ *
+ * Initialize memory buffer at the same time as the serial driver.
+ * Why does the serial driver have it's own INIT level?
+ */
+struct init_fn dmesg_init_fn __init_fn ( INIT_SERIAL ) = {
+       .initialise = dmesg_init,
+};
+
+/** Dmesg driver startup (and shutdown) function */
+struct startup_fn dmesg_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+       .shutdown = dmesg_fini,
+};
diff --git a/src/core/dmesg_console.c b/src/core/dmesg_console.c
new file mode 100644
index 0000000..fac2617
--- /dev/null
+++ b/src/core/dmesg_console.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Matthew Lowe <[email protected]>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <console.h>
+#include <gpxe/init.h>
+#include <gpxe/dmesg.h>
+
+/** @file
+ *
+ * Dmesg console
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct console_driver dmesg_console __console_driver;
+
+/**
+ * Enable console
+ */
+static void dmesg_console_init ( void ) {
+       dmesg_console.disabled = 0;
+}
+
+/** 
+ * Dmesg console driver
+ */
+struct console_driver dmesg_console __console_driver = {
+       .putchar = dmesg_putc,
+       .getchar = dmesg_getc,
+       .iskey = dmesg_ischar,
+       .disabled = 1,
+};
+
+/**
+ * Dmesg console initialization function
+ */
+struct init_fn dmesg_console_init_fn __init_fn ( INIT_CONSOLE ) = {
+       .initialise = dmesg_console_init,
+};
diff --git a/src/hci/commands/dmesg_cmd.c b/src/hci/commands/dmesg_cmd.c
new file mode 100644
index 0000000..bdd4ff2
--- /dev/null
+++ b/src/hci/commands/dmesg_cmd.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2010 Matthew Lowe <[email protected]>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <curses.h>
+#include <console.h>
+#include <byteswap.h>
+#include <gpxe/process.h>
+#include <gpxe/errfile.h>
+#include <gpxe/command.h>
+#include <gpxe/xfer.h>
+#include <gpxe/tcpip.h>
+#include <gpxe/open.h>
+#include <gpxe/job.h>
+#include <gpxe/monojob.h>
+#include <gpxe/dmesg.h>
+
+/** @file
+ * 
+ * Dmesg userpace command
+ * 
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+extern struct dmesg_buffer dmesg_buffer;
+
+/** 
+ *
+ * Dump dmesg buffer and page output
+ *
+ * @v rows     Rows per page
+ * @v cols     Columns per row
+ * @ret rc     Return status code
+ */ 
+static int page_dmesg (int rows, int cols)
+{
+  char *ls, *pos, c;
+  int lines;
+
+  /* Ensure the buffer is not locked */
+  if ( dmesg_buffer.locked )
+    return -EADDRINUSE;
+
+  if ( !dmesg_buffer.buffer )
+    return -EINVAL;
+  
+  dmesg_buffer.locked = TRUE;
+  
+  ls = pos = dmesg_buffer.buffer;
+
+  /* Begin parsing buffer, one line at a time */
+  while ( pos < dmesg_buffer.pos )
+    {
+      pos = strchr (ls, '\n');
+      
+      if ( !pos )
+       pos = dmesg_buffer.pos;
+
+      /* Auto-wrap output if characters before NL
+       * is greater than desired columns */
+      if ((pos - ls) > cols)
+       pos = ls + cols;
+
+      /* Null terminate line */
+      c = *pos;
+      *pos = '\0';
+      
+      printf ("%s\n",ls);
+      
+      /* Remove null terminator */
+      *pos = c;
+
+      if (c == '\n')
+       pos++;
+
+      ls = pos;
+      lines ++;
+
+      /* If we've displayed one page out output, wait for user
+       * input to continue */
+      if (lines == (rows - 1))
+       {
+         lines = 0;
+         printf ( "--More--" );
+         c = getchar();
+
+         /* Erase prompt - incase we are logging output somewhere else
+          * such as a serial console */
+         printf ( "\r          \r" );
+
+         if (c == 'q')
+           {
+             dmesg_buffer.locked = FALSE;
+             return -ECANCELED;
+           }
+
+       }
+    }
+
+  dmesg_buffer.locked = FALSE;
+  return PXENV_STATUS_SUCCESS;
+}
+
+/** TCP Sender */
+struct tcpsender {
+  /** Reference count for this object */
+  struct refcnt refcnt;
+
+  /** Job control interface */
+  struct job_interface job;
+
+  /** Process control interface */
+  struct process process;
+
+  /** TCP Socket */
+  struct xfer_interface socket;
+  struct sockaddr_tcpip server;
+
+  /** Data Information */
+  char *pos;
+  char *end;
+};
+
+/**
+ * Cleanup all interfaces and terminate process
+ *
+ * @v tcsender                TCPsender session
+ * @v rc                      Return status code
+ */
+static void tcpsender_finished ( struct tcpsender *tcpsender, int rc )
{
+
+  /* Remove process */
+  process_del ( &tcpsender->process );
+
+  /* Block further incoming messages */
+  job_nullify ( &tcpsender->job );
+  xfer_nullify ( &tcpsender->socket );
+
+  /* Free resources and close interfaces */
+  xfer_close ( &tcpsender->socket, rc );
+  job_done ( &tcpsender->job, rc );
+}
+
+/**
+ * Gracefully handle socket closure
+ *
+ * @v socket               TCP socket
+ * @v rc                   Return status code
+ */
+static void tcpsender_xfer_close ( struct xfer_interface *socket, int
rc ) {
+        struct tcpsender *tcpsender =
+         container_of ( socket, struct tcpsender, socket );
+
+        /* Terminate tcpsender */
+        tcpsender_finished ( tcpsender, rc );
+}
+
+/**
+ * Tcpsender process
+ *
+ * @v process             Tcpsender process
+ */
+static void tcpsender_step ( struct process *process ) {
+  int rc;
+  int ws;
+
+  struct tcpsender *tcpsender =
+    container_of ( process, struct tcpsender, process );
+
+  /* Set processing positions in dmesg buffer if they are not set yet
*/
+  if ( !tcpsender->pos )
+    tcpsender->pos = dmesg_buffer.buffer;
+  
+  if ( !tcpsender->end )
+    tcpsender->end = dmesg_buffer.pos;
+  
+  /* Wait for TCP connection to establish or TX queue to empty */
+  if ( ( ws = xfer_window ( &tcpsender->socket ) ) )
+    {
+      /* If we have no more data terminate the job */
+      if ( tcpsender->pos == tcpsender->end)
+       {
+         tcpsender_finished ( tcpsender, PXENV_STATUS_SUCCESS );
+         return;
+       }
+
+      if ( ( tcpsender->pos + ws ) > tcpsender->end )
+       ws = (int) ( tcpsender->end - tcpsender->pos );
+      
+      
+      rc = xfer_deliver_raw ( &tcpsender->socket, tcpsender->pos, ws );
+      tcpsender->pos += ws;
+
+      if ( rc )
+       tcpsender_finished ( tcpsender, rc );
+    }
+}
+
+/**
+ * Gracefully handle kill request
+ * @v job                  Job interface
+ */
+static void tcpsender_job_kill ( struct job_interface *job ) {
+        struct tcpsender *tcpsender =
+         container_of ( job, struct tcpsender, job );
+
+        /* Terminate tcpsender */
+        tcpsender_finished ( tcpsender, -ECANCELED );
+}
+
+/* Tcpsender job control interface */
+static struct job_interface_operations tcpsender_job_operations = {
+  .done           = ignore_job_done,
+  .kill           = tcpsender_job_kill,
+  .progress       = ignore_job_progress,
+};
+
+/* Tcpsender xfer interface */
+static struct xfer_interface_operations tcpsender_socket_operations = {
+  .close          = tcpsender_xfer_close,
+  .vredirect      = xfer_vreopen,
+  .window         = unlimited_xfer_window,
+  .alloc_iob      = default_xfer_alloc_iob,
+  .deliver_iob    = xfer_deliver_as_raw,
+  .deliver_raw    = ignore_xfer_deliver_raw,
+  };
+
+/**
+ * Free tcpsender session
+ * @v refcnt              Reference counter
+ */
+static void tcpsender_free ( struct refcnt *refcnt ) {
+        struct tcpsender *tcpsender =
+         container_of ( refcnt, struct tcpsender, refcnt );
+
+        free ( tcpsender );
+}
+
+/**
+ * Send dmesg buffer to remote host
+ * @v host                 Remote host
+ * @v port                 Destination port
+ * @ret rc                 Return status
+ */
+static int dmesg_tcp (char *host, char *port)
+{
+  struct tcpsender *tcpsender;
+  int rc;
+
+  /* Sanity check host and port parameters */
+  if (!host || !port)
+    return -EINVAL;
+  
+  /* Ensure the dmesg buffer is available */
+  if ( dmesg_buffer.locked )
+    {
+      printf ("DMESG buffer is currently locked.\n");
+      return -EADDRINUSE;
+    }
+
+  dmesg_buffer.locked = TRUE;
+
+  /* Allocate and initialize tcpsender session */
+  if ( ! ( tcpsender = zalloc ( sizeof(*tcpsender) ) ) )
+    {
+      dmesg_buffer.locked = FALSE;
+      return -ENOMEM;
+    }
+
+  tcpsender->refcnt.free = tcpsender_free;
+
+  job_init ( &tcpsender->job, &tcpsender_job_operations, 
+            &tcpsender->refcnt );
+
+  xfer_init ( &tcpsender->socket, &tcpsender_socket_operations, 
+             &tcpsender->refcnt );
+
+  process_init ( &tcpsender->process, tcpsender_step,
&tcpsender->refcnt );
+
+  tcpsender->server.st_port = htons ( strtoul ( port, NULL, 0 ) );
+  
+  /* Open socket to remote host */
+  if ( ( rc = xfer_open_named_socket ( &tcpsender->socket, SOCK_STREAM,

+                                      (struct sockaddr *)
&tcpsender->server,
+                                      host, NULL ) ) != 0 )
+    goto err;
+  
+  /* Attach parent interface */
+  job_plug_plug ( &tcpsender->job, &monojob );
+
+  /* Wait for send to complete */
+  rc = monojob_wait ( "Sending" );
+  
+  ref_put ( &tcpsender->refcnt );
+   
+  dmesg_buffer.locked = FALSE;
+
+  return rc;
+   
+ err:
+  tcpsender_finished ( tcpsender, rc );
+  ref_put ( &tcpsender->refcnt );
+  
+  dmesg_buffer.locked = FALSE;
+   
+  return rc;
+}
+
+/**
+ * Display command usage
+ */
+static void dmesg_print_syntax ( ) {
+  printf ( "Usage:\n"
+          "  dmesg [OPTIONS]\n"
+          "     -h, --help - Show this help message\n"
+          "     -n, --nopage - Do not page output\n"
+          "     -r rows, --rows rows - Print [rows] rows per page\n"
+          "     -c cols, --cols cols - Print [cols] characters per
row\n"
+          "     -t host:port, --tcp ip:port - Dump dmesg output to
[ip]:[port] via TCP\n"
+          "\n"
+          "Display debug messages\n");
+}
+
+/**
+ * dmesg command
+ * @v argc            Argument count
+ * @v argv            Argument list
+ * @ret rc            Exit code
+ */
+static int dmesg_exec ( int argc, char **argv ) {
+
+       static struct option longopts[] = {
+         { "help", no_argument, NULL, 'h' },
+         { "rows", required_argument, NULL, 'r' },
+         { "cols", required_argument, NULL, 'c' },
+         { "nopage", no_argument, NULL, 'n' },
+         { "tcp", required_argument, NULL, 't'},
+         { NULL, 0, NULL, 0 },
+        };
+
+        int c;
+       int rows = 25;
+       int cols = 80;
+       char *port;
+
+        /* Parse options */
+        while ( ( c = getopt_long ( argc, argv, "hr:c:nt:",
+                                    longopts, NULL ) ) >= 0 ) {
+         switch ( c ) {
+         case 'r':
+           /* Set number of rows per page */
+           rows = strtoul ( optarg, NULL, 0 );     
+
+           /* Sanity check */
+           if ( rows < 2 )
+             {
+               dmesg_print_syntax ();
+               return -EINVAL;
+             }
+      
+           break;
+         case 'c':
+           /* Set number of cols per row */
+           cols = strtoul ( optarg, NULL, 10 );
+
+           /* Sanity check */
+           if ( cols < 1 )
+             {
+               dmesg_print_syntax ();
+               return -EINVAL;
+             }
+
+           break;
+         case 'n':
+           /* Do not page output, just dump it all to console */
+
+           /* Ensure the buffer is not locked */
+           if ( dmesg_buffer.locked )
+             return -EADDRINUSE;
+           
+           /* Lock buffer and dump contents to console */
+           dmesg_buffer.locked = TRUE;
+           printf ( "%s", dmesg_buffer.buffer );
+           dmesg_buffer.locked = FALSE;
+
+           return PXENV_STATUS_SUCCESS;
+         case 't':
+           /* Send debug output to remote host via TCP */
+           port = strchr ( optarg, ':' );
+
+           if ( !port || !*(port+1) )
+             {
+               printf ( "Error: No port specified.\n" );
+               dmesg_print_syntax ();
+               return -EINVAL;
+             }
+           *(port++) = '\0';
+           
+           return dmesg_tcp (optarg, port);
+         case 'h':
+           /* Display help text */
+         default:
+           /* Unrecognised/invalid option */
+           dmesg_print_syntax ();
+
+         return -EINVAL;
+         }
+        }
+
+       return (page_dmesg (rows,cols));
+}
+
+/** Dmesg command */
+struct command time_command __command = {
+       .name = "dmesg",
+       .exec = dmesg_exec,
+};
+
diff --git a/src/include/gpxe/dmesg.h b/src/include/gpxe/dmesg.h
new file mode 100644
index 0000000..ae83d75
--- /dev/null
+++ b/src/include/gpxe/dmesg.h
@@ -0,0 +1,57 @@
+#ifndef _GPXE_DMESG_H
+#define _GPXE_DMESG_H
+
+/*
+ * Copyright (C) 2010 Matthew Lowe <[email protected]>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * Dmesg driver functions
+ *
+ */
+
+/* Include user memory pointer type */
+#include <gpxe/umalloc.h>
+
+
+
+/* Container structure that describes console memory buffer */
+struct dmesg_buffer {
+  userptr_t user_buffer;
+
+  /* Physical buffer locations */
+  char *buffer;
+  char *pos;
+  
+  unsigned int size;
+
+  /* Basic access control */
+  int locked;
+};
+
+/* Console interface prototypes */
+extern void dmesg_putc ( int ch );
+extern int dmesg_getc ( void );
+extern int dmesg_ischar ( void );
+
+
+#endif /* _GPXE_DMESG_H */
diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h
index a17da90..f0e6afe 100644
--- a/src/include/gpxe/errfile.h
+++ b/src/include/gpxe/errfile.h
@@ -55,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_bitmap                ( ERRFILE_CORE | 0x000f0000 )
 #define ERRFILE_base64                ( ERRFILE_CORE | 0x00100000 )
 #define ERRFILE_base16                ( ERRFILE_CORE | 0x00110000 )
+#define ERRFILE_dmesg                  ( ERRFILE_CORE | 0x00120000 )
 
 #define ERRFILE_eisa                ( ERRFILE_DRIVER | 0x00000000 )
 #define ERRFILE_isa                 ( ERRFILE_DRIVER | 0x00010000 )
@@ -210,6 +211,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_login_ui             ( ERRFILE_OTHER | 0x00170000 )
 #define ERRFILE_ib_srpboot           ( ERRFILE_OTHER | 0x00180000 )
 #define ERRFILE_iwmgmt               ( ERRFILE_OTHER | 0x00190000 )
+#define ERRFILE_dmesg_cmd             ( ERRFILE_OTHER | 0x001A0000 )
 
 /** @} */
 
-- 
1.7.1

_______________________________________________
gPXE mailing list
[email protected]
http://etherboot.org/mailman/listinfo/gpxe

Reply via email to