Patchwork [PATCH/RFC] Add Alternative Log Buffer Support for printk Messages

login
register
mail settings
Submitter Grant Erickson
Date Nov. 25, 2008, 6:34 p.m.
Message ID <1227638045-12862-1-git-send-email-gerickson@nuovations.com>
Download mbox | patch
Permalink /patch/10735/
State Changes Requested
Headers show

Comments

Grant Erickson - Nov. 25, 2008, 6:34 p.m.
This merges support for the previously DENX-only kernel feature of
specifying an alternative, "external" buffer for kernel printk
messages and their associated metadata. In addition, this ports
architecture support for this feature from arch/ppc to arch/powerpc.

Signed-off-by: Grant Erickson <gerickson@nuovations.com>
---

When this option is enabled, an architecture- or machine-specific log
buffer is used for all printk messages. This allows entities such as
boot loaders (e.g. U-Boot) to place printk-compatible messages into
this buffer and for the kernel to coalesce them with its normal
messages.

The code has historically been used and proven to work on the LWMON5
platform under arch/ppc and is now used (by me) successfully on the
AMCC Haleakala and Kilauea platforms.

As implemented for arch/powerpc, two suboptions for the alternative
log buffer are supported. The buffer may be contiguous with the
metadata and message data colocated or the metadata and message
storage may be in discontiguous regions of memory (e.g. a set of
scratch registers and an SRAM buffer). On Kilauea and Haleakala, I
have used the former; whereas LWMON5 has traditionally used the latter.

The code here is, more or less, as-is from the DENX GIT tree. Comments
welcome.

 arch/powerpc/kernel/prom.c |   93 +++++++++++++++++++++++++++
 include/linux/logbuff.h    |   56 ++++++++++++++++
 init/Kconfig               |   25 +++++++
 init/main.c                |    4 +
 kernel/printk.c            |  149 +++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 324 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/logbuff.h
Matt Sealey - Nov. 25, 2008, 6:53 p.m.
Nitpick, really.. shouldn't the logbuffer location(s) be some device tree
property(ies), perhaps something in the
/chosen node that U-Boot etc. can then fill out?
Josh Boyer - Nov. 25, 2008, 6:55 p.m.
On Tue, 25 Nov 2008 12:53:12 -0600
"Matt Sealey" <matt@genesi-usa.com> wrote:

> Nitpick, really.. shouldn't the logbuffer location(s) be some device tree
> property(ies), perhaps something in the
> /chosen node that U-Boot etc. can then fill out?

I don't think that's a nitpick.  It's a fundamental change in how this
would all work.  However, I do think you're generally right.

Perhaps not /chosen, but maybe something like /rtas or /firmware, etc.

josh
Matt Sealey - Nov. 25, 2008, 7:01 p.m.
On Tue, Nov 25, 2008 at 12:55 PM, Josh Boyer <jwboyer@linux.vnet.ibm.com>wrote:

> On Tue, 25 Nov 2008 12:53:12 -0600
> "Matt Sealey" <matt@genesi-usa.com> wrote:
>
> > Nitpick, really.. shouldn't the logbuffer location(s) be some device tree
> > property(ies), perhaps something in the
> > /chosen node that U-Boot etc. can then fill out?
>
> I don't think that's a nitpick.  It's a fundamental change in how this
> would all work.  However, I do think you're generally right.
>
> Perhaps not /chosen, but maybe something like /rtas or /firmware, etc.
>
> josh


I think the best place is chosen, with things like stdin, stdout etc. - this
is where you generally go and dump weird little variables which need to be
passed in for early boot. You could consider the log buffer a kind of
stdin/out variation.

I don't think it needs a whole new device tree node just for two memory
locations.. is the support really worth a compatible property, etc. to
differentiate between different operating modes?
Grant Erickson - Nov. 25, 2008, 7:04 p.m.
On 11/25/08 10:55 AM, Josh Boyer wrote:
> On Tue, 25 Nov 2008 12:53:12 -0600
> "Matt Sealey" <matt@genesi-usa.com> wrote:
>> Nitpick, really.. shouldn't the logbuffer location(s) be some device tree
>> property(ies), perhaps something in the
>> /chosen node that U-Boot etc. can then fill out?
> 
> I don't think that's a nitpick.  It's a fundamental change in how this
> would all work.  However, I do think you're generally right.
> 
> Perhaps not /chosen, but maybe something like /rtas or /firmware, etc.

I'm inclined to agree with you both; however, the submitted implementation
was a choice of expediency given the existing DENX implementation and a
customer that needed the feature "yesterday".

ARM, MIPS, et al have not yet adopted device trees, correct? If so, is there
value in providing the submitted implementation and adding support for
getting said information from the device tree as another option if such
information exists?

Regards,

Grant
Matt Sealey - Nov. 25, 2008, 7:31 p.m.
Actually there is an ARM board out there with an Open Firmware (Toshiba
TOPAS910 -
http://www.toshiba-components.com/microcontroller/BMSKTOPAS910.html) so,
yes, device trees do appear on those platforms. QEMU is looking to go that
way too, seems for every architecture it would be a pretty awesome idea
(especially on ARM).

In any case, for platforms that don't, you can then make sure the "depends
on" line in Kconfig is for boards with no device trees - right now that
particular thing escapes me but I bet !PPC_OF or similar couldn't be too far
from the truth - so the ability to set the values is taken away if you have
device tree support built-in.

On MIPS or ARM with no DT support, if they don't have device tree support
compiled in, the options magically appear. You get the best of both worlds.

There is also no reason you can't hard-code the locations into the device
tree, to support older U-Boots that don't know about
/chosen/linux,log-metadata and /chosen/linux,log-buffer*.

I can think of a bunch of reasons why it's a good idea.. what if your board
supports a DIMM and you want to keep the buffer up near the top of memory,
or the MBAR/CCSRBAR/IMMRBAR or whatever can move location so your SRAM moves
around depending on your firmware version or feature support, or, similarly,
if your L2 and SRAM are the same thing and can be configured to use
different sizes on each boot - pick a different size for the L2/SRAM and the
location may have to be moved, which means compiling a new firmware AND a
new kernel and matching up the values.. in the end it makes everyone's life
easier.

* naive implementation - if you only specify log-buffer then that means
metadata and buffer are "colocated" as your docs said, that'd be the least
complicated way to implement it without involving a hell of a lot of new
stuff, plus it means you can check the existance/value of two properties in
a node you can guarantee exists. Also since the log buffer format is
Linux-specific and Linux-compatible, and entirely a Linux feature, just like
the linux,initrd-start and -end, making a whole new node seems wasteful.
Bill Gatliff - Nov. 25, 2008, 7:51 p.m.
Matt Sealey wrote:

> I can think of a bunch of reasons why it's a good idea.. 

Can you point to a GPL/LGPL/BSD/etc. source code for an OpenFirmware
implementation?


b.g.
Matt Sealey - Nov. 25, 2008, 8:07 p.m.
On Tue, Nov 25, 2008 at 1:51 PM, Bill Gatliff <bgat@billgatliff.com> wrote:

> Matt Sealey wrote:
>
> > I can think of a bunch of reasons why it's a good idea..
>
> Can you point to a GPL/LGPL/BSD/etc. source code for an OpenFirmware
> implementation?
>

Yes, there's FirmWorks, CodeGen SmartFirmware, IBM SLOF and OpenBIOS..
they're all linked from the OpenBIOS website (along with a bunch of the
documentation from http://www.openfirmware.org in more-readable formats like
PDF)

http://www.openbios.org/


But here's the real question; why do you need an opensource implementation?
Curiosity?
Grant Likely - Nov. 25, 2008, 8:14 p.m.
.  F

On Tue, Nov 25, 2008 at 12:51 PM, Bill Gatliff <bgat@billgatliff.com> wrote:
> Matt Sealey wrote:
>
>> I can think of a bunch of reasons why it's a good idea..
>
> Can you point to a GPL/LGPL/BSD/etc. source code for an OpenFirmware
> implementation?

In powerpc land using the Open Firmware device tree does not depend on
also using Open Firmware.  From that perspective the availability of
an open sourced Open Firmware is irrelevant.

But, both OpenFirmware and SmartFirmware have been open sourced:

http://www.openfirmware.info/Open_Firmware
http://www.openfirmware.info/SmartFirmware

Cheers,
g.
Grant Likely - Nov. 25, 2008, 8:17 p.m.
On Tue, Nov 25, 2008 at 1:07 PM, Matt Sealey <matt@genesi-usa.com> wrote:
>
>
> On Tue, Nov 25, 2008 at 1:51 PM, Bill Gatliff <bgat@billgatliff.com> wrote:
>>
>> Matt Sealey wrote:
>>
>> > I can think of a bunch of reasons why it's a good idea..
>>
>> Can you point to a GPL/LGPL/BSD/etc. source code for an OpenFirmware
>> implementation?
>
> Yes, there's FirmWorks, CodeGen SmartFirmware, IBM SLOF and OpenBIOS..
> they're all linked from the OpenBIOS website (along with a bunch of the
> documentation from http://www.openfirmware.org in more-readable formats like
> PDF)
>
> http://www.openbios.org/
>
>
> But here's the real question; why do you need an opensource implementation?
> Curiosity?

Umm, so that it can be ported to new boards perhaps?  So that
developers can work with it?

g.
Bill Gatliff - Nov. 25, 2008, 8:19 p.m.
Matt Sealey wrote:
> Yes, there's FirmWorks, CodeGen SmartFirmware, IBM SLOF and OpenBIOS..
> they're all linked from the OpenBIOS website (along with a bunch of the
> documentation from http://www.openfirmware.org in more-readable formats
> like PDF)
> 
> http://www.openbios.org/
> 
> 
> But here's the real question; why do you need an opensource
> implementation? Curiosity?

That, and I prefer Free *ware whenever that's an option.  :)

Nothing against the commercial alternatives, of course.  But I'm
already doing my own heavy lifting, because my platforms are all
full-custom with very limited production runs.  Since I'm into the
guts of all my code anyway, I'm not inclined to outsource a bootloader
development effort.

Just trying to figure out where the walls of this "sandbox" are.  I've
been aware of the concept of Open Firmware for a while, but haven't
really looked into it before now--- mostly because my impression until
now was that the available implementations were both closed-source,
and not supporting embedded, non-PPC targets like ARM.


b.g.
Matt Sealey - Nov. 25, 2008, 8:46 p.m.
On Tue, Nov 25, 2008 at 2:17 PM, Grant Likely <grant.likely@secretlab.ca>wrote:

> On Tue, Nov 25, 2008 at 1:07 PM, Matt Sealey <matt@genesi-usa.com> wrote:
>
> > But here's the real question; why do you need an opensource
> implementation?
> > Curiosity?
>
> Umm, so that it can be ported to new boards perhaps?  So that
> developers can work with it?
>

You can port closed-source firmwares to new boards just as easily as with
open source ones.

For companies who have larger production requirements than Bill Gatliff,
porting an open source firmware may not be a task they want to get into.
Linux support is a big enough moving target without adding firmware
development to it and tying up programmers implementing the same drivers
twice (even if it is practically copy&paste in U-Boot).

In this case they may just buy one in. If everything had to be open source
and freely downloadable there would be no industry at all. And if paying for
someone else to write code for you is the issue, then we wouldn't have asked
you for a quote last week, and you certainly wouldn't have given us a price
:D

Bill, as for ARM etc. support, you can imagine that most of the guts of the
firmware are fairly well abstracted. Vast portions of the same code work on
most platforms with the same peripherals - USB, Ethernet controllers & PHYs,
PCI, SDIO, etc. The device tree is how hardware designers (note; not OS
designers) are meant to abstract the differences in hardware and programming
model so that an OS can load the right drivers and Do The Right Thing. It
doesn't matter if the device tree was generated as a flattened
representation, or by an open-source firmware or closed-source firmware.

However the IEEE 1275 standard is not as big a moving target as U-Boot. It
has an ABI and a client interface which hasn't been moved around or modified
since 1994. And when your board doesn't provide a device tree entry for
something, well, you just write some Forth and can edit entries in the
device tree in-place (no recompiles required). If you're so inclined you can
even write things like scsi host controller drivers in Forth and present
them such that they are then working - no reboots or recompiles required,
just load the script - and ready to use to boot the system. You can poke
around in the FirmWorks code for how powerful this can be, or poke around
http://www.powerdeveloper.org/platforms/efika/devicetree for how mundane and
simple it can be just fixing up some entries and making sure the chip is
configured right..
David VomLehn - Nov. 25, 2008, 8:54 p.m.
On Tue, 2008-11-25 at 14:19 -0600, Bill Gatliff wrote:

> Just trying to figure out where the walls of this "sandbox" are.  I've
> been aware of the concept of Open Firmware for a while, but haven't
> really looked into it before now--- mostly because my impression until
> now was that the available implementations were both closed-source,
> and not supporting embedded, non-PPC targets like ARM.

FWIW, at the last OLS there was interest expressed by at least one
ARM-based platform and I'm working on a set of patches to add the device
tree for MIPS. It's going very slowly and is dependent on a patchset to
add support for our platform, so it will take a while to make it
available. The important point, though, is that device tree is the only
thing approaching a standard on any non-x86-based platform for passing
structured information from the bootloader to the kernel. The command
line is just not sufficient for this.
--
David VomLehn




     - - - - -                              Cisco                            - - - - -         
This e-mail and any attachments may contain information which is confidential, 
proprietary, privileged or otherwise protected by law. The information is solely 
intended for the named addressee (or a person responsible for delivering it to 
the addressee). If you are not the intended recipient of this message, you are 
not authorized to read, print, retain, copy or disseminate this message or any 
part of it. If you have received this e-mail in error, please notify the sender 
immediately by return e-mail and delete it from your computer.
Wolfgang Denk - Nov. 25, 2008, 9:05 p.m.
Dear Matt,

In message <b5e2fc790811251131g63ea9444x57ec0829ca0b069e@mail.gmail.com> you wrote:
>
> There is also no reason you can't hard-code the locations into the device
> tree, to support older U-Boots that don't know about
> /chosen/linux,log-metadata and /chosen/linux,log-buffer*.

Actually there is such reason - U-Boot traditionally allocates the log
buffer close to the upper end of system memory, so the start address
depends on the memory size on the board, which may vary.

Best regards,

Wolfgang Denk
Matt Sealey - Nov. 25, 2008, 9:14 p.m.
On Tue, Nov 25, 2008 at 3:05 PM, Wolfgang Denk <wd@denx.de> wrote:

> Dear Matt,
>
> In message <b5e2fc790811251131g63ea9444x57ec0829ca0b069e@mail.gmail.com>
> you wrote:
> >
> > There is also no reason you can't hard-code the locations into the device
> > tree, to support older U-Boots that don't know about
> > /chosen/linux,log-metadata and /chosen/linux,log-buffer*.
>
> Actually there is such reason - U-Boot traditionally allocates the log
> buffer close to the upper end of system memory, so the start address
> depends on the memory size on the board, which may vary.
>


I think I covered that in "I can think of a bunch of reasons why it's a good
idea.. " - but the current implementation is hardcoded in the kernel so
there is no problem with *certain* boards having it done this way, and that
information being moved from the kernel into a static entry in a device
tree.
David Brownell - Nov. 25, 2008, 9:45 p.m.
No comment from me on $SUBJECT beyond "it seems plausible", but ...

On Tuesday 25 November 2008, David VomLehn wrote:
> The important point, though, is that device tree is the only
> thing approaching a standard on any non-x86-based platform for passing
> structured information from the bootloader to the kernel. The command
> line is just not sufficient for this.

Me, I'll be happier if I don't have to try using that device tree.
Having board-specific code in the kernel is a more complete solution,
and makes it a lot easier to cope with all the hardware goofage.

Recall that the *original* notion behind OpenBoot (now "OpenFirmware")
was to have tables for the stuff that was table-friendly, and call
out to FORTH code (possibly not just at boot time) for the rest.
(Given the choice of FORTH vs ACPI bytecodes, I'd go for FORTH; but
the better option is "neither".)

Right now I see an awful lot of work going into trying to force lots
of stuff into table format.  Even when it's the sort of one-off or
board-specific quirkery that was an original motivation for having
FORTH escapes (tasks that were not table-friendly).

- Dave
Mike Frysinger - Nov. 26, 2008, 1:23 a.m.
On Tue, Nov 25, 2008 at 13:34, Grant Erickson wrote:
> This merges support for the previously DENX-only kernel feature of
> specifying an alternative, "external" buffer for kernel printk
> messages and their associated metadata. In addition, this ports
> architecture support for this feature from arch/ppc to arch/powerpc.
>
> Signed-off-by: Grant Erickson <gerickson@nuovations.com>
> ---
>
> When this option is enabled, an architecture- or machine-specific log
> buffer is used for all printk messages. This allows entities such as
> boot loaders (e.g. U-Boot) to place printk-compatible messages into
> this buffer and for the kernel to coalesce them with its normal
> messages.
> <snip>

this extended info should be part of the changelog and thus above the
--- marker ...
-mike
Matt Sealey - Nov. 26, 2008, 8:57 p.m.
On Tue, Nov 25, 2008 at 3:45 PM, David Brownell <david-b@pacbell.net> wrote:
> No comment from me on $SUBJECT beyond "it seems plausible", but ...
>
> On Tuesday 25 November 2008, David VomLehn wrote:
>> The important point, though, is that device tree is the only
>> thing approaching a standard on any non-x86-based platform for passing
>> structured information from the bootloader to the kernel. The command
>> line is just not sufficient for this.
>
> Me, I'll be happier if I don't have to try using that device tree.
> Having board-specific code in the kernel is a more complete solution,
> and makes it a lot easier to cope with all the hardware goofage.

I disagree.

> Recall that the *original* notion behind OpenBoot (now "OpenFirmware")
> was to have tables for the stuff that was table-friendly, and call
> out to FORTH code (possibly not just at boot time) for the rest.
> (Given the choice of FORTH vs ACPI bytecodes, I'd go for FORTH; but
> the better option is "neither".)

The problem really is that Linux never really cared to run Forth code or even
keep the client interface for OF and 99% of the devices with real device
trees and real client interfaces, have really bad hardware support or methods
implemented that just don't work - not to mention the evil that happens on the
difference between Linux ABI and the OF ABI, the client interface context
switch, real-mode or virtual-mode firmwares.

The most useful thing is the representation of the devices in the device-tree.

I do however agree that sometimes FAR too much is being put into the
device trees,
the massive amount of overly verbose information about devices which do not need
the information there is just redundant. Reading processor registers out of the
device tree (such as the system version register on SoCs) is totally dumb, but
this was suggested at one time.

Once you match the driver with a compatible property, you're set; you know which
driver you're on. You can read out quirks and make inferences from
other parts of
the tree (this is something that most drivers do NOT do since people
think reading
outside the node just matched is some evil) where these are truly board-level
problems.

For instance where the ATA DMA lines are not connected on a 5200B board, this
must be described. Of course whether you decide this from the root
"model" property
and make a guess (all boards of fsl,broken5200  might have the bug) or
specifically
say in /soc/ata that "dma-is-broken" is the big question. In this
event, Grant came
up with a more fun solution which is to specify the maximum modes in
the device tree
therefore not bringing ANY board specific properties into it and
making sure you do not
clutter the driver with board specifics! This is exactly how device
trees should be designed.

Properties such as "fsl5200-clocking" in i2c nodes, is NOT. The
compatible property
of the node has fsl,mpc5200b-i2c, this implies 5200 clocking for the
i2c bus anyway. I
really don't get why this property even exists. It's not like there is
some alternative way
on the chip.. :D

> Right now I see an awful lot of work going into trying to force lots
> of stuff into table format.  Even when it's the sort of one-off or
> board-specific quirkery that was an original motivation for having
> FORTH escapes (tasks that were not table-friendly).

What about drivers where there is no binding for OF or Forth and never was and
never would/will be? There is no Forth escape for that.

Forth - and the client interface - should be used where necessary to
bring the system
up. This be modifying the device tree, implementing methods to boot
(it makes some
sense that if the ATA methods in the client interface work, then Linux
could load it's
own initrd without the help of Yaboot or GRUB, for example, right in
the boot wrapper,
and to the location it sees best to load it, and not what Yaboot
decides for it, and there
would be no need to maintain 4 different filesystem codes in 3
alternative loaders and
the kernel).

But unfortunately most systems have crap CIF support (ours included)
which do not
come up to the task.
Benjamin Herrenschmidt - Jan. 7, 2009, 12:04 a.m.
On Tue, 2008-11-25 at 10:34 -0800, Grant Erickson wrote:
> This merges support for the previously DENX-only kernel feature of
> specifying an alternative, "external" buffer for kernel printk
> messages and their associated metadata. In addition, this ports
> architecture support for this feature from arch/ppc to arch/powerpc.
> 
> Signed-off-by: Grant Erickson <gerickson@nuovations.com>

Considering the extensive changes to generic code, this patch will
have to be submitted via the linux-kernel mailing list.

I suggest you split the generic core change from the powerpc specific
implementation.

I'm not sure whether I like the idea myself or not there, so you'll have
to convince the powers that be to take it.

Cheers,
Ben.
Grant Erickson - Jan. 7, 2009, 2:11 a.m.
On 1/6/09 4:04 PM, Benjamin Herrenschmidt wrote:
> On Tue, 2008-11-25 at 10:34 -0800, Grant Erickson wrote:
>> This merges support for the previously DENX-only kernel feature of
>> specifying an alternative, "external" buffer for kernel printk
>> messages and their associated metadata. In addition, this ports
>> architecture support for this feature from arch/ppc to arch/powerpc.
>> 
>> Signed-off-by: Grant Erickson <gerickson@nuovations.com>
> 
> Considering the extensive changes to generic code, this patch will
> have to be submitted via the linux-kernel mailing list.
> 
> I suggest you split the generic core change from the powerpc specific
> implementation.
> 
> I'm not sure whether I like the idea myself or not there, so you'll have
> to convince the powers that be to take it.

Ben:

Thanks for the feedback. Matt Sealey had some good feedback
<http://ozlabs.org/pipermail/linuxppc-dev/2008-November/065594.html> which I
have on my to-do list to evaluate.

In the interim, I'll hold off on pushing up to linux-kernel until I've done
that.

Regards,

Grant

Patch

diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 3a2dc7e..60282f1 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -32,6 +32,7 @@ 
 #include <linux/debugfs.h>
 #include <linux/irq.h>
 #include <linux/lmb.h>
+#include <linux/logbuff.h>
 
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -61,6 +62,15 @@ 
 #define DBG(fmt...)
 #endif
 
+#ifdef CONFIG_LOGBUFFER
+#ifdef CONFIG_ALT_LB_LOCATION
+# if !defined(BOARD_ALT_LH_ADDR) || !defined(BOARD_ALT_LB_ADDR)
+#  error "Please specify BOARD_ALT_LH_ADDR & BOARD_ALT_LB_ADDR."
+# endif
+#else /* !CONFIG_ALT_LB_LOCATION */
+static phys_addr_t ext_logbuff;
+#endif /* CONFIG_ALT_LB_LOCATION */
+#endif /* CONFIG_LOGBUFFER */
 
 static int __initdata dt_root_addr_cells;
 static int __initdata dt_root_size_cells;
@@ -1018,6 +1028,85 @@  static int __init early_init_dt_scan_memory(unsigned long node,
 	return 0;
 }
 
+#ifdef CONFIG_LOGBUFFER
+#ifdef CONFIG_ALT_LB_LOCATION
+/* Alternative external log buffer mapping: log metadata header & the
+ * character buffer are separated and allocated not in RAM but in some
+ * other memory-mapped I/O region (e.g. log head in unused registers,
+ * and log buffer in OCM memory)
+ */
+int __init setup_ext_logbuff_mem(volatile logbuff_t **lhead, char **lbuf)
+{
+	void *h, *b;
+
+	if (unlikely(!lhead) || unlikely(!lbuf))
+		return -EINVAL;
+
+	/* map log head */
+	h = ioremap(BOARD_ALT_LH_ADDR, sizeof(logbuff_t));
+	if (unlikely(!h))
+		return -EFAULT;
+
+	/* map log buffer */
+	b = ioremap(BOARD_ALT_LB_ADDR, LOGBUFF_LEN);
+	if (unlikely(!b)) {
+		iounmap(h);
+		return -EFAULT;
+	}
+
+	*lhead = h;
+	*lbuf = b;
+
+	return 0;
+}
+#else /* !CONFIG_ALT_LB_LOCATION */
+/* Usual external log-buffer mapping: log metadata header & the character
+ * buffer are both contiguous in system RAM.
+ */
+int __init setup_ext_logbuff_mem(logbuff_t **lhead, char **lbuf)
+{
+	void *p;
+
+	if (unlikely(!lhead) || unlikely(!lbuf))
+		return -EINVAL;
+
+	if (unlikely(!ext_logbuff) || !lmb_is_reserved(ext_logbuff))
+		return -EFAULT;
+
+	p = ioremap(ext_logbuff, LOGBUFF_RESERVE);
+
+	if (unlikely(!p))
+		return -EFAULT;
+
+	*lhead = (logbuff_t *)(p + LOGBUFF_OVERHEAD -
+			       sizeof(logbuff_t) +
+			       sizeof(((logbuff_t *)0)->buf));
+	*lbuf = (*lhead)->buf;
+
+	return 0;
+}
+
+/* When the external log buffer configuration is used with the
+ * non-alternate location, the log head metadata and character buffer
+ * lie in the LOGBUFF_RESERVE bytes at the end of system RAM. Add this
+ * block of memory to the reserved memory pool so that it is not
+ * allocated for other purposes.
+ */
+static void __init reserve_ext_logbuff_mem(void)
+{
+	phys_addr_t top = lmb_end_of_DRAM();
+	phys_addr_t size = LOGBUFF_RESERVE;
+	phys_addr_t base = top - size;
+
+	if (top > base) {
+		ext_logbuff = base;
+		DBG("reserving: %x -> %x\n", base, size);
+		lmb_reserve(base, size);
+	}
+}
+#endif /* CONFIG_ALT_LB_LOCATION */
+#endif /* CONFIG_LOGBUFFER */
+
 static void __init early_reserve_mem(void)
 {
 	u64 base, size;
@@ -1033,6 +1122,10 @@  static void __init early_reserve_mem(void)
 	self_size = initial_boot_params->totalsize;
 	lmb_reserve(self_base, self_size);
 
+#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_LOCATION)
+	reserve_ext_logbuff_mem();
+#endif /* defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_LOCATION) */
+
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* then reserve the initrd, if any */
 	if (initrd_start && (initrd_end > initrd_start))
diff --git a/include/linux/logbuff.h b/include/linux/logbuff.h
new file mode 100644
index 0000000..22a51c0
--- /dev/null
+++ b/include/linux/logbuff.h
@@ -0,0 +1,56 @@ 
+/*
+ * (C) Copyright 2007
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 (at your option) 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#ifndef _LOGBUFF_H_
+#define _LOGBUFF_H_
+
+#ifdef CONFIG_LOGBUFFER
+
+#define LOGBUFF_MAGIC		0xc0de4ced
+#define LOGBUFF_LEN		16384
+#define LOGBUFF_OVERHEAD	4096
+#define LOGBUFF_RESERVE		(LOGBUFF_LEN + LOGBUFF_OVERHEAD)
+
+/* The mapping used here has to be the same as in logbuff_init_ptrs ()
+   in u-boot/common/cmd_log.c */
+
+typedef struct {
+	unsigned long	tag;
+	unsigned long	start;
+	unsigned long	con;	/* next char to be sent to consoles	*/
+	unsigned long	end;
+	unsigned long	chars;
+	unsigned char	buf[0];
+} logbuff_t;
+
+#ifdef CONFIG_ALT_LB_LOCATION
+# define LOGBUFF_VOLATILE	volatile
+#else
+# define LOGBUFF_VOLATILE
+#endif /* defined(CONFIG_ALT_LB_LOCATION) */
+
+extern void setup_ext_logbuff(void);
+/* arch specific */
+extern int setup_ext_logbuff_mem(LOGBUFF_VOLATILE logbuff_t **lhead, char **lbuf);
+
+#endif /* CONFIG_LOGBUFFER */
+#endif /* _LOGBUFF_H_ */
diff --git a/init/Kconfig b/init/Kconfig
index f763762..e1a1b59 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -619,6 +619,31 @@  config PRINTK
 	  very difficult to diagnose system problems, saying N here is
 	  strongly discouraged.
 
+config LOGBUFFER
+	bool "External logbuffer" if PRINTK
+	default n
+	help
+	  This option enables support for an alternative, "external"
+	  printk log buffer. When enabled, an architecture- or machine-
+	  specific log buffer is used for all printk messages. This
+	  allows entities such as boot loaders to place printk-compatible
+	  messages into this buffer and for the kernel to coalesce them
+	  with its normal messages.
+
+config ALT_LB_LOCATION
+	bool "Alternative logbuffer" if LOGBUFFER
+	default n
+	help
+	  When using an alternative, "external" printk log buffer, an
+	  architecture- or machine-specific log buffer with contiguous
+	  metadata and message storage is used. This option enables
+	  support for discontiguous metadata and message storage
+	  memory (e.g. a set of scratch registers and an SRAM
+	  buffer). By saying Y here, you must also ensure your
+	  architecture- or machine-code specify BOARD_ALT_LH_ADDR and
+	  BOARD_ALT_LB_ADDR, for the metadata and message memory,
+	  respectively.
+
 config BUG
 	bool "BUG() support" if EMBEDDED
 	default y
diff --git a/init/main.c b/init/main.c
index 7e117a2..5687b98 100644
--- a/init/main.c
+++ b/init/main.c
@@ -61,6 +61,7 @@ 
 #include <linux/kthread.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
+#include <linux/logbuff.h>
 #include <linux/idr.h>
 #include <linux/ftrace.h>
 
@@ -563,6 +564,9 @@  asmlinkage void __init start_kernel(void)
  * Interrupts are still disabled. Do necessary setups, then
  * enable them
  */
+#ifdef CONFIG_LOGBUFFER
+	setup_ext_logbuff();
+#endif
 	lock_kernel();
 	tick_init();
 	boot_cpu_init();
diff --git a/kernel/printk.c b/kernel/printk.c
index f492f15..59884e2 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -32,6 +32,7 @@ 
 #include <linux/security.h>
 #include <linux/bootmem.h>
 #include <linux/syscalls.h>
+#include <linux/logbuff.h>
 
 #include <asm/uaccess.h>
 
@@ -101,9 +102,39 @@  static DEFINE_SPINLOCK(logbuf_lock);
  * The indices into log_buf are not constrained to log_buf_len - they
  * must be masked before subscripting
  */
+#ifdef CONFIG_LOGBUFFER
+/* Indexes to the local log buffer */
+static unsigned long _log_start;
+static unsigned long _con_start;
+static unsigned long _log_end;
+static unsigned long _logged_chars;
+/* These will be switched to the external log buffer */
+#ifndef CONFIG_ALT_LB_LOCATION
+/* usual logbuffer location */
+static unsigned long *ext_log_start = &_log_start;
+static unsigned long *ext_con_start = &_con_start;
+static unsigned long *ext_log_end = &_log_end;
+static unsigned long *ext_logged_chars = &_logged_chars;
+#define log_start	(*ext_log_start)
+#define con_start	(*ext_con_start)
+#define log_end		(*ext_log_end)
+#define logged_chars	(*ext_logged_chars)
+#else /* defined(CONFIG_ALT_LB_LOCATION) */
+/* alternative logbuffer location */
+static volatile unsigned long *ext_log_start = &_log_start;
+static volatile unsigned long *ext_con_start = &_con_start;
+static volatile unsigned long *ext_log_end = &_log_end;
+static volatile unsigned long *ext_logged_chars = &_logged_chars;
+#define log_start       (*((volatile u32 *)ext_log_start))
+#define con_start       (*((volatile u32 *)ext_con_start))
+#define log_end         (*((volatile u32 *)ext_log_end))
+#define logged_chars    (*((volatile u32 *)ext_logged_chars))
+#endif /* !defined(CONFIG_ALT_LB_LOCATION) */
+#else /* !defined(CONFIG_LOGBUFFER) */
 static unsigned log_start;	/* Index into log_buf: next char to be read by syslog() */
 static unsigned con_start;	/* Index into log_buf: next char to be sent to consoles */
 static unsigned log_end;	/* Index into log_buf: most-recently-written-char + 1 */
+#endif /* CONFIG_LOGBUFFER */
 
 /*
  *	Array of consoles built from command line options (console=)
@@ -134,10 +165,121 @@  static int console_may_schedule;
 static char __log_buf[__LOG_BUF_LEN];
 static char *log_buf = __log_buf;
 static int log_buf_len = __LOG_BUF_LEN;
+#ifndef CONFIG_LOGBUFFER
 static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+#endif /* !defined(CONFIG_LOGBUFFER) */
+#ifdef CONFIG_LOGBUFFER
+/* Sanity check the external log buffer metadata. When an the external
+ * log buffer is enabled, the log metadata is effectively non-volatile
+ * in that the values are preserved from reboot to reboot (until/unless
+ * the system loses power).
+ */
+static void __init logbuff_check_metadata(LOGBUFF_VOLATILE logbuff_t *log)
+{
+	unsigned long chars;
+
+	/* Sanity check the producer and consumer indices. */
+
+	if (log->end - log->start > LOGBUFF_LEN)
+		log->start = log->end - LOGBUFF_LEN;
+
+	if (log->end - log->con > LOGBUFF_LEN)
+		log->con = log->end - LOGBUFF_LEN;
+
+	/* Occasionally, particularly following a reboot, the start
+	 * consumer index is not properly caught up to the console
+	 * consumer index. If this is the case, catch it up so that
+	 * the log buffer doesn't start with, for example,
+	 * "<0>Restarting system.\n" followed by the 'real' start of
+	 * the log.
+	 */
+
+	if (log->con > log->start)
+		log->start = log->con;
+
+	/* Ensure that the number of characters logged reflects the
+	 * characters actually logged based on the producer and
+	 * consumer indices rather than all characters cumulatively
+	 * logged across all reboots since a power-loss event.
+	 */
+
+	chars = log->end - log->start;
+
+	if (log->chars > chars)
+		log->chars = chars;
+}
+
+/* Coalesce the current log bounded buffer and the external log
+ * bounded buffer by appending the former to the latter. Precedence is
+ * given to the external log buffer when there is more data to be
+ * appended than space exists, so the current log buffer is truncated
+ * instead of overwritting the external buffer.
+ */
+static void __init logbuff_coalesce_buffers(LOGBUFF_VOLATILE logbuff_t *log)
+{
+	unsigned long dspace, ssize, len;
+
+	dspace = LOGBUFF_LEN - (log->end - log->start);
+	ssize = log_end - log_start;
+	len = min(dspace, ssize);
+
+	while (len-- > 0) {
+		log->buf[log->end++ & (LOGBUFF_LEN-1)] = LOG_BUF(log_start++);
+		log->chars++;
+	}
+}
 
+void __init setup_ext_logbuff(void)
+{
+	LOGBUFF_VOLATILE logbuff_t *log;
+	char *ext_log_buf = NULL;
+	unsigned long flags;
+
+	if (setup_ext_logbuff_mem(&log, &ext_log_buf) < 0) {
+		printk(KERN_WARNING
+		       "Failed to setup external logbuffer - ignoring it\n");
+		return;
+	}
+
+	/* When no properly setup buffer is found, reset pointers */
+	if (log->tag != LOGBUFF_MAGIC) {
+		printk(KERN_WARNING
+		       "Unexpected external log buffer magic number. "
+		       "Got %08lx, expected %08x. Resetting pointers and using "
+		       "the buffer anyway.\n",
+		       log->tag, LOGBUFF_MAGIC);
+		log->tag = LOGBUFF_MAGIC;
+		log->start = log->end = log->con = log->chars = 0;
+	}
+
+	spin_lock_irqsave(&logbuf_lock, flags);
+
+	logbuff_check_metadata(log);
+	logbuff_coalesce_buffers(log);
+
+	/* Switch to the external log buffer */
+	ext_log_start		= &log->start;
+	ext_con_start		= &log->con;
+	ext_log_end		= &log->end;
+	ext_logged_chars	= &log->chars;
+
+	log_buf			= ext_log_buf;
+	log_buf_len 		= LOGBUFF_LEN;
+
+	spin_unlock_irqrestore(&logbuf_lock, flags);
+
+	printk(KERN_NOTICE "log_buf=%p\n", log_buf);
+}
+#endif /* CONFIG_LOGBUFFER */
 static int __init log_buf_len_setup(char *str)
 {
+#ifdef CONFIG_LOGBUFFER
+	/* Log buffer size is LOGBUFF_LEN bytes */
+	printk(KERN_NOTICE
+	       "External log buffer configured; "
+	       "ignoring log_buf_len param.\n");
+	return 1;
+#else
 	unsigned size = memparse(str, &str);
 	unsigned long flags;
 
@@ -173,6 +315,7 @@  static int __init log_buf_len_setup(char *str)
 	}
 out:
 	return 1;
+#endif /* CONFIG_LOGBUFFER */
 }
 
 __setup("log_buf_len=", log_buf_len_setup);
@@ -230,7 +373,7 @@  static void boot_delay_msec(void)
 static inline void boot_delay_msec(void)
 {
 }
-#endif
+#endif /* CONFIG_BOOT_PRINTK_DELAY */
 
 /*
  * Commands to do_syslog:
@@ -740,7 +883,7 @@  out_restore_irqs:
 EXPORT_SYMBOL(printk);
 EXPORT_SYMBOL(vprintk);
 
-#else
+#else /* !CONFIG_PRINTK */
 
 asmlinkage long sys_syslog(int type, char __user *buf, int len)
 {
@@ -751,7 +894,7 @@  static void call_console_drivers(unsigned start, unsigned end)
 {
 }
 
-#endif
+#endif /* CONFIG_PRINTK */
 
 static int __add_preferred_console(char *name, int idx, char *options,
 				   char *brl_options)