console/flashsconsole: Add spi flash console for debugging
If CONSOLE_SPI_FLASH config is enabled, we write the cbmem messages to the 'CONSOLE' area in FMAP which allows us to grab the log when we read the flash. This is useful when you don't have usb debugging, and UART lines are hard to find. Since a failure to boot would require a hardware flasher anyways, we can get the log at the same time. This feature should only be used when no alternative is found and only when we can't boot the system, because excessive writes to the flash is not recommended. This has been tested on purism/librem13 v2 and librem 15 v3 which run Intel Skylake hardware. It has not been tested on other archs or with a driver other than the fast_spi. Change-Id: I74a297b94f6881d8c27cbe5168f161d8331c3df3 Signed-off-by: Youness Alaoui <youness.alaoui@puri.sm> Reviewed-on: https://review.coreboot.org/19849 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Philippe Mathieu-Daudé <philippe.mathieu.daude@gmail.com>
This commit is contained in:
parent
cadd7c7ed3
commit
c4b4ff3b1f
27
Makefile.inc
27
Makefile.inc
|
@ -790,9 +790,19 @@ FMAP_BIOS_SIZE := $(shell echo $(CONFIG_CBFS_SIZE) | tr A-F a-f)
|
|||
# position and size of flashmap, relative to BIOS_BASE
|
||||
FMAP_FMAP_BASE := 0
|
||||
FMAP_FMAP_SIZE := 0x100
|
||||
# position, size and entry line of CONSOLE relative to BIOS_BASE, if enabled
|
||||
ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
FMAP_CONSOLE_BASE := $(call int-add, $(FMAP_FMAP_BASE) $(FMAP_FMAP_SIZE))
|
||||
FMAP_CONSOLE_SIZE := $(CONFIG_CONSOLE_SPI_FLASH_BUFFER_SIZE)
|
||||
FMAP_CONSOLE_ENTRY := CONSOLE@$(FMAP_CONSOLE_BASE) $(FMAP_CONSOLE_SIZE)
|
||||
else # ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
FMAP_CONSOLE_BASE := 0
|
||||
FMAP_CONSOLE_SIZE := 0
|
||||
FMAP_CONSOLE_ENTRY :=
|
||||
endif # ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
# position and size of CBFS, relative to BIOS_BASE
|
||||
FMAP_CBFS_BASE := $(FMAP_FMAP_SIZE)
|
||||
FMAP_CBFS_SIZE := $(call int-subtract, $(FMAP_BIOS_SIZE) $(FMAP_FMAP_SIZE))
|
||||
FMAP_CBFS_BASE := $(call int-add, $(FMAP_CONSOLE_SIZE) $(FMAP_FMAP_SIZE))
|
||||
FMAP_CBFS_SIZE := $(call int-subtract, $(FMAP_BIOS_SIZE) $(FMAP_CBFS_BASE))
|
||||
else # ifeq ($(CONFIG_ARCH_X86),y)
|
||||
DEFAULT_FLASHMAP:=$(top)/util/cbfstool/default.fmd
|
||||
# entire flash
|
||||
|
@ -805,8 +815,18 @@ FMAP_BIOS_SIZE := $(CONFIG_CBFS_SIZE)
|
|||
# position and size of flashmap, relative to BIOS_BASE
|
||||
FMAP_FMAP_BASE := 0x20000
|
||||
FMAP_FMAP_SIZE := 0x100
|
||||
# position, size and entry line of CONSOLE relative to BIOS_BASE, if enabled
|
||||
ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
FMAP_CONSOLE_BASE := $(call int-add, $(FMAP_FMAP_BASE) $(FMAP_FMAP_SIZE))
|
||||
FMAP_CONSOLE_SIZE := $(CONFIG_CONSOLE_SPI_FLASH_BUFFER_SIZE)
|
||||
FMAP_CONSOLE_ENTRY := CONSOLE@$(FMAP_CONSOLE_BASE) $(FMAP_CONSOLE_SIZE)
|
||||
else # ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
FMAP_CONSOLE_BASE := 0
|
||||
FMAP_CONSOLE_SIZE := 0
|
||||
FMAP_CONSOLE_ENTRY :=
|
||||
endif # ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
# position and size of CBFS, relative to BIOS_BASE
|
||||
FMAP_CBFS_BASE := $(call int-add,$(FMAP_FMAP_BASE) $(FMAP_FMAP_SIZE))
|
||||
FMAP_CBFS_BASE := $(call int-add,$(FMAP_FMAP_BASE) $(FMAP_FMAP_SIZE) $(FMAP_CONSOLE_SIZE))
|
||||
FMAP_CBFS_SIZE := $(call int-subtract,$(FMAP_BIOS_SIZE) $(FMAP_CBFS_BASE))
|
||||
endif # ifeq ($(CONFIG_ARCH_X86),y)
|
||||
|
||||
|
@ -817,6 +837,7 @@ $(obj)/fmap.fmd: $(top)/Makefile.inc $(DEFAULT_FLASHMAP) $(obj)/config.h
|
|||
-e "s,##BIOS_SIZE##,$(FMAP_BIOS_SIZE)," \
|
||||
-e "s,##FMAP_BASE##,$(FMAP_FMAP_BASE)," \
|
||||
-e "s,##FMAP_SIZE##,$(FMAP_FMAP_SIZE)," \
|
||||
-e "s,##CONSOLE_ENTRY##,$(FMAP_CONSOLE_ENTRY)," \
|
||||
-e "s,##CBFS_BASE##,$(FMAP_CBFS_BASE)," \
|
||||
-e "s,##CBFS_SIZE##,$(FMAP_CBFS_SIZE)," \
|
||||
$(DEFAULT_FLASHMAP) > $@.tmp
|
||||
|
|
|
@ -233,6 +233,37 @@ config CONSOLE_CBMEM_DUMP_TO_UART
|
|||
|
||||
endif
|
||||
|
||||
config CONSOLE_SPI_FLASH
|
||||
bool "SPI Flash console output"
|
||||
default n
|
||||
select BOOT_DEVICE_SPI_FLASH_RW_NOMMAP_EARLY if !COMMON_CBFS_SPI_WRAPPER
|
||||
help
|
||||
Send coreboot debug output to the SPI Flash in the FMAP CONSOLE area
|
||||
|
||||
This option can cause premature wear on the SPI flash and should not
|
||||
be used as a normal means of debugging. It is only to be enabled and
|
||||
used when porting a new motherboard which has no other console
|
||||
available (no UART, no POST, no cbmem access(non bootable)). Since
|
||||
a non bootable machine will require the use of an external SPI Flash
|
||||
programmer, the developer can grab the console log at the same time.
|
||||
|
||||
The flash console will not be erased on reboot, so once it is full,
|
||||
the flashconsole driver will stop writing to it. This is to avoid
|
||||
wear on the flash, and to avoid erasing sectors (which may freeze
|
||||
the SPI controller on skylake).
|
||||
|
||||
The 'CONSOLE' area can be extracted from the FMAP with :
|
||||
cbfstool rom.bin read -r CONSOLE -f console.log
|
||||
|
||||
config CONSOLE_SPI_FLASH_BUFFER_SIZE
|
||||
hex "Room allocated for console output in FMAP"
|
||||
default 0x20000
|
||||
depends on CONSOLE_SPI_FLASH
|
||||
help
|
||||
Space allocated for console output storage in FMAP. The default
|
||||
value (128K or 0x20000 bytes) is large enough to accommodate
|
||||
even the BIOS_SPEW level.
|
||||
|
||||
config CONSOLE_QEMU_DEBUGCON
|
||||
bool "QEMU debug console output"
|
||||
depends on BOARD_EMULATION_QEMU_X86
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <console/uart.h>
|
||||
#include <console/usb.h>
|
||||
#include <console/spi.h>
|
||||
#include <console/flash.h>
|
||||
#include <rules.h>
|
||||
|
||||
void console_hw_init(void)
|
||||
|
@ -33,6 +34,7 @@ void console_hw_init(void)
|
|||
__ne2k_init();
|
||||
__usbdebug_init();
|
||||
__spiconsole_init();
|
||||
__flashconsole_init();
|
||||
}
|
||||
|
||||
void console_tx_byte(unsigned char byte)
|
||||
|
@ -53,6 +55,7 @@ void console_tx_byte(unsigned char byte)
|
|||
__ne2k_tx_byte(byte);
|
||||
__usb_tx_byte(byte);
|
||||
__spiconsole_tx_byte(byte);
|
||||
__flashconsole_tx_byte(byte);
|
||||
}
|
||||
|
||||
void console_tx_flush(void)
|
||||
|
@ -60,6 +63,7 @@ void console_tx_flush(void)
|
|||
__uart_tx_flush();
|
||||
__ne2k_tx_flush();
|
||||
__usb_tx_flush();
|
||||
__flashconsole_tx_flush();
|
||||
}
|
||||
|
||||
void console_write_line(uint8_t *buffer, size_t number_of_bytes)
|
||||
|
|
|
@ -7,6 +7,14 @@ ramstage-y += spiconsole.c
|
|||
smm-$(CONFIG_DEBUG_SMI) += spiconsole.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_CONSOLE_SPI_FLASH),y)
|
||||
bootblock-y += flashconsole.c
|
||||
romstage-y += flashconsole.c
|
||||
ramstage-y += flashconsole.c
|
||||
smm-$(CONFIG_DEBUG_SMI) += flashconsole.c
|
||||
|
||||
endif
|
||||
|
||||
bootblock-y += spi-generic.c
|
||||
bootblock-$(CONFIG_COMMON_CBFS_SPI_WRAPPER) += cbfs_spi.c
|
||||
bootblock-$(CONFIG_SPI_FLASH) += spi_flash.c
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <arch/early_variables.h>
|
||||
#include <region.h>
|
||||
#include <boot_device.h>
|
||||
#include <fmap.h>
|
||||
#include <console/console.h>
|
||||
#include <console/flash.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LINE_BUFFER_SIZE 128
|
||||
#define READ_BUFFER_SIZE 0x100
|
||||
|
||||
static const struct region_device *g_rdev_ptr CAR_GLOBAL;
|
||||
static struct region_device g_rdev CAR_GLOBAL;
|
||||
static uint8_t g_line_buffer[LINE_BUFFER_SIZE] CAR_GLOBAL;
|
||||
static size_t g_offset CAR_GLOBAL;
|
||||
static size_t g_line_offset CAR_GLOBAL;
|
||||
|
||||
void flashconsole_init(void)
|
||||
{
|
||||
struct region_device *rdev = car_get_var_ptr(&g_rdev);
|
||||
uint8_t buffer[READ_BUFFER_SIZE];
|
||||
size_t size;
|
||||
size_t offset = 0;
|
||||
size_t len = READ_BUFFER_SIZE;
|
||||
size_t i;
|
||||
|
||||
if (fmap_locate_area_as_rdev_rw("CONSOLE", rdev)) {
|
||||
printk(BIOS_INFO, "Can't find 'CONSOLE' area in FMAP\n");
|
||||
return;
|
||||
}
|
||||
size = region_device_sz(rdev);
|
||||
|
||||
/*
|
||||
* We need to check the region until we find a 0xff indicating
|
||||
* the end of a previous log write.
|
||||
* We can't erase the region because one stage would erase the
|
||||
* data from the previous stage. Also, it looks like doing an
|
||||
* erase could completely freeze the SPI controller and then
|
||||
* we can't write anything anymore (apparently might happen if
|
||||
* the sector is already erased, so we would need to read
|
||||
* anyways to check if it's all 0xff).
|
||||
*/
|
||||
for (i = 0; i < len && offset < size; ) {
|
||||
// Fill the buffer on first iteration
|
||||
if (i == 0) {
|
||||
len = min(READ_BUFFER_SIZE, size - offset);
|
||||
if (rdev_readat(rdev, buffer, offset, len) != len)
|
||||
return;
|
||||
}
|
||||
if (buffer[i] == 0xff) {
|
||||
offset += i;
|
||||
break;
|
||||
}
|
||||
// If we're done, repeat the process for the next sector
|
||||
if (++i == READ_BUFFER_SIZE) {
|
||||
offset += len;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
// Make sure there is still space left on the console
|
||||
if (offset >= size) {
|
||||
printk(BIOS_INFO, "No space left on 'console' region in SPI flash\n");
|
||||
return;
|
||||
}
|
||||
|
||||
car_set_var(g_offset, offset);
|
||||
/* Set g_rdev_ptr last so tx_byte doesn't get executed early */
|
||||
car_set_var(g_rdev_ptr, rdev);
|
||||
}
|
||||
|
||||
void flashconsole_tx_byte(unsigned char c)
|
||||
{
|
||||
const struct region_device *rdev = car_get_var(g_rdev_ptr);
|
||||
uint8_t *line_buffer;
|
||||
size_t offset;
|
||||
size_t len;
|
||||
size_t region_size;
|
||||
|
||||
if (!rdev)
|
||||
return;
|
||||
|
||||
line_buffer = car_get_var_ptr(g_line_buffer);
|
||||
offset = car_get_var(g_offset);
|
||||
len = car_get_var(g_line_offset);
|
||||
region_size = region_device_sz(rdev);
|
||||
|
||||
line_buffer[len++] = c;
|
||||
car_set_var(g_line_offset, len);
|
||||
|
||||
if (len >= LINE_BUFFER_SIZE ||
|
||||
offset + len >= region_size || c == '\n') {
|
||||
flashconsole_tx_flush();
|
||||
}
|
||||
}
|
||||
|
||||
void flashconsole_tx_flush(void)
|
||||
{
|
||||
const struct region_device *rdev = car_get_var(g_rdev_ptr);
|
||||
uint8_t *line_buffer = car_get_var_ptr(g_line_buffer);
|
||||
size_t offset = car_get_var(g_offset);
|
||||
size_t len = car_get_var(g_line_offset);
|
||||
size_t region_size;
|
||||
|
||||
if (!rdev)
|
||||
return;
|
||||
|
||||
/* Prevent any recursive loops in case the spi flash driver
|
||||
* calls printk (in case of transaction timeout or
|
||||
* any other error while writing) */
|
||||
car_set_var(g_rdev_ptr, NULL);
|
||||
|
||||
region_size = region_device_sz(rdev);
|
||||
if (offset + len >= region_size)
|
||||
len = region_size - offset;
|
||||
|
||||
if (rdev_writeat(rdev, line_buffer, offset, len) != len)
|
||||
rdev = NULL;
|
||||
|
||||
// If the region is full, stop future write attempts
|
||||
if (offset + len >= region_size)
|
||||
rdev = NULL;
|
||||
|
||||
car_set_var(g_offset, offset + len);
|
||||
car_set_var(g_line_offset, 0);
|
||||
|
||||
car_set_var(g_rdev_ptr, rdev);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CONSOLE_FLASH_H
|
||||
#define CONSOLE_FLASH_H 1
|
||||
|
||||
#include <rules.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void flashconsole_init(void);
|
||||
void flashconsole_tx_byte(unsigned char c);
|
||||
void flashconsole_tx_flush(void);
|
||||
|
||||
#define __CONSOLE_FLASH_ENABLE__ IS_ENABLED(CONFIG_CONSOLE_SPI_FLASH)
|
||||
|
||||
#if __CONSOLE_FLASH_ENABLE__
|
||||
static inline void __flashconsole_init(void) { flashconsole_init(); }
|
||||
static inline void __flashconsole_tx_byte(u8 data)
|
||||
{
|
||||
flashconsole_tx_byte(data);
|
||||
}
|
||||
static inline void __flashconsole_tx_flush(void)
|
||||
{
|
||||
flashconsole_tx_flush();
|
||||
}
|
||||
#else
|
||||
static inline void __flashconsole_init(void) {}
|
||||
static inline void __flashconsole_tx_byte(u8 data) {}
|
||||
static inline void __flashconsole_tx_flush(void) {}
|
||||
#endif /* __CONSOLE_FLASH_ENABLE__ */
|
||||
|
||||
|
||||
#endif /* CONSOLE_FLASH_H */
|
|
@ -10,6 +10,7 @@
|
|||
FLASH@##ROM_BASE## ##ROM_SIZE## {
|
||||
BIOS@##BIOS_BASE## ##BIOS_SIZE## {
|
||||
FMAP@##FMAP_BASE## ##FMAP_SIZE##
|
||||
##CONSOLE_ENTRY##
|
||||
COREBOOT(CBFS)@##CBFS_BASE## ##CBFS_SIZE##
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ FLASH@##ROM_BASE## ##ROM_SIZE## {
|
|||
BIOS@##BIOS_BASE## ##BIOS_SIZE## {
|
||||
BOOTBLOCK 128K
|
||||
FMAP@##FMAP_BASE## ##FMAP_SIZE##
|
||||
##CONSOLE_ENTRY##
|
||||
COREBOOT(CBFS)@##CBFS_BASE## ##CBFS_SIZE##
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue