ppc64: Kconfig switch for bootblock in SEEPROM, zero HRMOR

On PPC64 each address is logically OR'ed with HRMOR (Hypervisor Real
Mode Offset Register) before it is dispatched to the underlying memory,
meaning that memory space overlaps at the least significant bit set in
HRMOR. coreboot is entered with HRMOR = 4GB-128MB both on hardware
(when started by hostboot bootloader) and in Qemu in hb-mode. This means
that memory overlaps every 128MB in this particular case. HRMOR can be
explicitly ignored when MSB of an address is set, but this would require
using different memory model for linking.

If we zero HRMOR in bootblock, linking can be done against real address.
This greatly simplifies memory layout and allows to forget about HRMOR
from that point on.

Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com>
Signed-off-by: Maciej Pijanowski <maciej.pijanowski@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Change-Id: I0170463968c91b943c4b0dc15fe73fa616a164da
Reviewed-on: https://review.coreboot.org/c/coreboot/+/67067
Reviewed-by: Elyes Haouas <ehaouas@noos.fr>
Reviewed-by: David Hendricks <david.hendricks@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Krystian Hebel 2021-02-16 18:22:22 +01:00 committed by Matt DeVillier
commit 44ec090551
9 changed files with 353 additions and 58 deletions

View file

@ -1,6 +1,15 @@
## SPDX-License-Identifier: GPL-2.0-only
ppc64_flags = -I$(src)/arch/ppc64/ -mbig-endian -mcpu=power8 -mtune=power8
ifeq ($(CONFIG_COMPILER_GCC),y)
# disable use of %r11 for static chains on invoking nested functions through
# pointers
ppc64_flags += -mno-pointers-to-nested-functions
else ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y)
# use GNU assember at least until LLVM's integrated PPC64 assember will be able
# to handle "slbia" instruction with an argument
ppc64_flags += -fno-integrated-as
endif
ppc64_asm_flags =

View file

@ -27,15 +27,67 @@
oris r,r, (e)@h; \
ori r,r, (e)@l;
/* Load an immediate 32-bit value into a register */
#define LOAD_IMM32(r, e) \
li r, 0; \
oris r,r, (e)@h; \
ori r,r, (e)@l;
/*
* On POWER, 0 is wherever HRMOR points to rather than physical DRAM start.
* HRMOR is ORed with address, not added to it, meaning that memory space
* overlaps after 2^(least significant set bit of HRMOR). This becomes
* chaotic when nonconsecutive bits are set...
*
* Two and a half possible cases:
* 0. bootblock started with QEMU in hb-mode
* - NIA = 0x10 (bug?)
* - HRMOR = 0x08000000 (128M)
* - no physical memory to enable/train, everything accessible from start
* 1. bootblock loaded by HBBL
* - NIA = 0
* - HRMOR = 0xF8000000 (4G - 128M)
* - initialized L3 = 0x400000 (4M)
* - top address before RAM = 0xF8400000
* 2. bootblock in SEEPROM, loaded by SBE
* - NIA = 0x3000 (placeholder for int. vectors)
* - HRMOR = 0xF8200000 (4G - 128 M + 2 M)
* - initialized L3 = 0x8000 (bootblock/HBBL size = 32K)
* - no way 32K will be enough, must initialize more L3 in bootblock
* - HRMOR still applies, so memory overlaps every 2M
*
* Common subset (assuming 2. initializes as much memory as possible) is
* 0xF8200000-0xF8400000. 2M should be more than enough for pre-RAM code,
* but it isn't enough to load ramstage. We could implement postcar stage,
* but KISS: initialize L3 from _ebootblock to 0xF8980000: up to 9.5M into
* cache, leaving bottom 2M (0xF8000000-0xF8200000) either uninitialized
* (when started from SEEPROM) or just unused for anything but bootblock
* (loaded by HBBL). Last 0.5M of L3 cache is left for interrupt vectors
* normally located at address 0.
*
* Set HRMOR to 0 before jumping to C code in bootblock and forget it even
* exists.
*
* For QEMU s/0xF8/0x08/ in above description but code remains the same.
* L3 initialization is unnecessary in this case but won't break anything.
*
* TODO: there is a structure with SBE->HBBL data at 0 in 2nd option. It
* holds some useful data like XSCOM BAR and LPC BAR. If, for any reason,
* these addresses are different than default, they should be used instead
* of predefined values.
*/
.section ".text._start", "ax", %progbits
.globl _start
_start:
/* QEMU with hb-mode=on starts at address 0x10, while hardware at 0x0 */
#if !CONFIG(BOOTBLOCK_IN_SEEPROM)
nop
nop
nop
nop
FIXUP_ENDIAN
#endif
/* Store FDT address provided by QEMU in %r3 to pass it later to
* payload */
@ -44,32 +96,54 @@ _start:
/* Set program priority to medium */
or %r2, %r2, %r2
/* Stack */
lis %r1, _estack@ha
addi %r1, %r1, _estack@l
li %r10, 1
rotldi %r10, %r10, 63 /* MSB is "ignore HRMOR" */
/* Clear .bss section */
/* Currently not needed, .bss is zeroed in the file. If it were to be
* used, make sure that .bss is 128B aligned (size of cache line),
* otherwise dcbz will clear (part of) .opd section! */
/*
lis %r5, _bss@ha
addi %r5, %r5, _bss@l
lis %r6, _ebss@ha
addi %r6, %r6, _ebss@l
/* Assumption: we are linked at address that isn't changed by HRMOR */
LOAD_IMM32(%r7, ignoreHRMOR)
or %r9, %r7, %r10
mtlr %r9
blr
ignoreHRMOR:
/* Now we are at 0x8000000000000000 | linked address */
li %r0, 0
mtspr SPR_HRMOR, %r0 /* Clear HRMOR */
isync
/* We can't just "b stopIgnoringHRMOR", it would use relative offset */
addi %r9, %r7, stopIgnoringHRMOR - ignoreHRMOR
mtlr %r9
blr
stopIgnoringHRMOR:
/* Now we are at linked address */
slbia 7
sync
isync
/*
* When coming from SBE, L3 cache is invalid except for [2M, end of HBBL]
* range. Make the rest of it valid, or embrace the checkstops.
*/
/* Validate and initialize to zeroes [end of HBBL, 9.5M] range */
LOAD_IMM32(%r5, _ebootblock) /* Assume it is at least 128B aligned */
LOAD_IMM32(%r6, _epreram_cbfs_cache) /* Same */
addi %r6, %r6, -1
1:
dcbz 0, %r5
addi %r5, %r5, 128
cmpld cr7, %r5, %r6
blt cr7, 1b
*/
/* Stack */
LOAD_IMM32(%r1, _estack)
/* This is tested by checkstack() just before jumping to payload */
LOAD_IMM64(%r3, 0xDEADBEEFDEADBEEF)
lis %r5, _stack@ha
addi %r5, %r5, _stack@l
subi %r5, %r5, 8
LOAD_IMM32(%r5, _stack - 8)
sub %r4, %r1, %r5
sradi %r4, %r4, 3 /* Divide by 8 */
mtctr %r4
@ -87,11 +161,10 @@ _start:
mfmsr %r3
ori %r3, %r3, 0x2000 /* FP = 1 */
oris %r3, %r3, 0x0280 /* VEC = 1, VSX = 1 */
mtmsr %r3
mtmsrd %r3
/* Load official procedure descriptor address for main() */
lis %r12, main@ha
addi %r12, %r12, main@l
LOAD_IMM32(%r12, main)
/* Load TOC pointer and jump to main() */
ld %r2, 8(%r12)

View file

@ -4,15 +4,74 @@
#include <arch/header.ld>
// TODO: fill in these blanks for Power8.
SECTIONS
{
DRAM_START(0x0)
BOOTBLOCK(0x100, 64K)
ROMSTAGE(0x20000, 128K)
STACK(0x40000, 0x3ff00)
PRERAM_CBMEM_CONSOLE(0x80000, 8K)
FMAP_CACHE(0x82000, 2K)
CBFS_MCACHE(0x82800, 8K)
RAMSTAGE(0x100000, 16M)
/*
* On POWER, 0 is wherever HRMOR points to rather than physical DRAM start.
* HRMOR is ORed with address, not added to it, meaning that memory space
* overlaps after 2^(least significant set bit of HRMOR). This becomes
* chaotic when nonconsecutive bits are set...
*
* Two and a half possible cases:
* 0. bootblock started with QEMU in hb-mode
* - NIA = 0x10 (bug?)
* - HRMOR = 0x08000000 (128M)
* - no physical memory to enable/train, everything accessible from start
* 1. bootblock loaded by HBBL
* - NIA = 0
* - HRMOR = 0xF8000000 (4G - 128M)
* - initialized L3 = 0x400000 (4M)
* - top address before RAM = 0xF8400000
* 2. bootblock in SEEPROM, loaded by SBE
* - NIA = 0x3000 (placeholder for int. vectors)
* - HRMOR = 0xF8200000 (4G - 128 M + 2 M)
* - initialized L3 = 0x8000 (bootblock/HBBL size = 32K)
* - no way 32K will be enough, must initialize more L3 in bootblock
* - HRMOR still applies, so memory overlaps every 2M
*
* Common subset (assuming 2. initializes as much memory as possible) is
* 0xF8200000-0xF8400000. 2M should be more than enough for pre-RAM code,
* but it isn't enough to load ramstage. We could implement postcar stage,
* but KISS: initialize L3 from _ebootblock to 0xF8980000: up to 9.5M into
* cache, leaving bottom 2M (0xF8000000-0xF8200000) either uninitialized
* (when started from SEEPROM) or just unused for anything but bootblock
* (loaded by HBBL). Last 0.5M of L3 cache is left for interrupt vectors
* normally located at address 0.
*
* Set HRMOR to 0 before jumping to C code in bootblock and forget it even
* exists.
*
* For QEMU s/0xF8/0x08/ in above description but code remains the same.
* L3 initialization is unnecessary in this case but won't break anything.
*
* TODO: there is a structure with SBE->HBBL data at 0 in 2nd option. It
* holds some useful data like XSCOM BAR and LPC BAR. If, for any reason,
* these addresses are different than default, they should be used instead
* of predefined values.
*/
#if !CONFIG(BOOTBLOCK_IN_SEEPROM)
BOOTBLOCK( 0x08000000, 32K)
#else
BOOTBLOCK( 0x08203000, 20K)
#endif
STACK( 0x08208000, 32K)
PRERAM_CBMEM_CONSOLE(0x08210000, 128K)
FMAP_CACHE( 0x08230000, 4K)
CBFS_MCACHE( 0x08231000, 8K)
TIMESTAMP( 0x08233000, 4K)
ROMSTAGE( 0x08240000, 256K)
/*
* bootblock_crt0.S assumes this is the last part of L3, leaving for
* interrupt vectors at least 0.5M because of cache associativity. If
* more CBFS_CACHE is needed, split this into pre-/postram caches.
*/
CBFS_CACHE( 0x08280000, 7M)
RAMSTAGE( 0x09000000, 2M)
}

View file

@ -8,27 +8,70 @@ SECTIONS
{
DRAM_START(0x0)
BOOTBLOCK(0, 32K)
/*
* On POWER, 0 is wherever HRMOR points to rather than physical DRAM start.
* HRMOR is ORed with address, not added to it, meaning that memory space
* overlaps after 2^(least significant set bit of HRMOR). This becomes
* chaotic when nonconsecutive bits are set...
*
* Two and a half possible cases:
* 0. bootblock started with QEMU in hb-mode
* - NIA = 0x10 (bug?)
* - HRMOR = 0x08000000 (128M)
* - no physical memory to enable/train, everything accessible from start
* 1. bootblock loaded by HBBL
* - NIA = 0
* - HRMOR = 0xF8000000 (4G - 128M)
* - initialized L3 = 0x400000 (4M)
* - top address before RAM = 0xF8400000
* 2. bootblock in SEEPROM, loaded by SBE
* - NIA = 0x3000 (placeholder for int. vectors)
* - HRMOR = 0xF8200000 (4G - 128 M + 2 M)
* - initialized L3 = 0x8000 (bootblock/HBBL size = 32K)
* - no way 32K will be enough, must initialize more L3 in bootblock
* - HRMOR still applies, so memory overlaps every 2M
*
* Common subset (assuming 2. initializes as much memory as possible) is
* 0xF8200000-0xF8400000. 2M should be more than enough for pre-RAM code,
* but it isn't enough to load ramstage. We could implement postcar stage,
* but KISS: initialize L3 from _ebootblock to 0xF8980000: up to 9.5M into
* cache, leaving bottom 2M (0xF8000000-0xF8200000) either uninitialized
* (when started from SEEPROM) or just unused for anything but bootblock
* (loaded by HBBL). Last 0.5M of L3 cache is left for interrupt vectors
* normally located at address 0.
*
* Set HRMOR to 0 before jumping to C code in bootblock and forget it even
* exists.
*
* For QEMU s/0xF8/0x08/ in above description but code remains the same.
* L3 initialization is unnecessary in this case but won't break anything.
*
* TODO: there is a structure with SBE->HBBL data at 0 in 2nd option. It
* holds some useful data like XSCOM BAR and LPC BAR. If, for any reason,
* these addresses are different than default, they should be used instead
* of predefined values.
*/
ROMSTAGE(0x1f00000, 1M)
#if !ENV_RAMSTAGE
STACK(0x2000000, 32K)
#if !CONFIG(BOOTBLOCK_IN_SEEPROM)
BOOTBLOCK( 0x08000000, 32K)
#else
BOOTBLOCK( 0x08203000, 20K)
#endif
FMAP_CACHE(0x2108000, 4K)
CBFS_MCACHE(0x2109000, 8K)
TIMESTAMP(0x210b000, 4K)
CBFS_CACHE(0x210c000, 512K)
PRERAM_CBMEM_CONSOLE(0x218c000, 128K)
STACK( 0x08208000, 32K)
PRERAM_CBMEM_CONSOLE(0x08210000, 128K)
FMAP_CACHE( 0x08230000, 4K)
CBFS_MCACHE( 0x08231000, 8K)
TIMESTAMP( 0x08233000, 4K)
/* By default all memory addresses are affected by the value of HRMOR
* (Hypervisor Real Mode Offset Register) which is ORed to them. HRMOR
* has initial value of 0x8000000 in QEMU and is changed to 0 in
* ramstage. This means that before ramstage 0 actually points to
* 0x8000000. */
#if ENV_RAMSTAGE
STACK(0xa000000, 32K)
#endif
RAMSTAGE(0xa008000, 2M)
ROMSTAGE( 0x08240000, 256K)
/*
* bootblock_crt0.S assumes this is the last part of L3, leaving for
* interrupt vectors at least 0.5M because of cache associativity. If
* more CBFS_CACHE is needed, split this into pre-/postram caches.
*/
CBFS_CACHE( 0x08280000, 7M)
RAMSTAGE( 0x09000000, 2M)
}

View file

@ -2,6 +2,14 @@
if BOARD_RAPTOR_CS_TALOS_2
config BOOTBLOCK_IN_SEEPROM
bool "Bootblock in SEEPROM"
default n
help
Enable this option if coreboot shall build image with separate
bootblock (i.e. not in coreboot.rom) to be put into SEEPROM
directly.
config BOARD_SPECIFIC_OPTIONS
def_bool y
select CPU_POWER9
@ -15,6 +23,11 @@ config BOARD_SPECIFIC_OPTIONS
config MEMLAYOUT_LD_FILE
default "src/mainboard/\$(CONFIG_MAINBOARD_DIR)/memlayout.ld"
config FMDFILE
string
default "src/mainboard/\$(CONFIG_MAINBOARD_DIR)/board-bootblock-in-seeprom.fmd" if BOOTBLOCK_IN_SEEPROM
default "src/mainboard/\$(CONFIG_MAINBOARD_DIR)/board.fmd" if !BOOTBLOCK_IN_SEEPROM
config MAINBOARD_DIR
default "raptor-cs/talos-2"

View file

@ -0,0 +1,15 @@
# layout for firmware when flash address space matches used address layout
# +-------------+ <-- 0
# | unspecified |
# +-------------+ <-- BIOS_BASE
# | FMAP |
# +-------------+ <-- BIOS_BASE + 128K + FMAP_SIZE
# | CBFS |
# +-------------+ <-- ROM_SIZE
FLASH@0 CONFIG_ROM_SIZE {
BIOS@0 CONFIG_ROM_SIZE {
FMAP 0x200
COREBOOT(CBFS)
}
}

View file

@ -0,0 +1,18 @@
# layout for firmware when flash address space matches used address layout
# +-------------+ <-- 0
# | unspecified |
# +-------------+ <-- BIOS_BASE
# | bootblock |
# +-------------+ <-- BIOS_BASE + 128K
# | FMAP |
# +-------------+ <-- BIOS_BASE + 128K + FMAP_SIZE
# | CBFS |
# +-------------+ <-- ROM_SIZE
FLASH@0 CONFIG_ROM_SIZE {
BIOS@0 CONFIG_ROM_SIZE {
BOOTBLOCK 128K
FMAP 0x200
COREBOOT(CBFS)
}
}

View file

@ -4,15 +4,74 @@
#include <arch/header.ld>
// TODO: fill in these blanks for Power9.
SECTIONS
{
DRAM_START(0x0)
BOOTBLOCK(0, 64K)
ROMSTAGE(0x120000, 128K)
STACK(0x140000, 0x3ff00)
PRERAM_CBMEM_CONSOLE(0x180000, 8K)
FMAP_CACHE(0x182000, 2K)
CBFS_MCACHE(0x182800, 8K)
RAMSTAGE(0x200000, 16M)
/*
* On POWER, 0 is wherever HRMOR points to rather than physical DRAM start.
* HRMOR is ORed with address, not added to it, meaning that memory space
* overlaps after 2^(least significant set bit of HRMOR). This becomes
* chaotic when nonconsecutive bits are set...
*
* Two and a half possible cases:
* 0. bootblock started with QEMU in hb-mode
* - NIA = 0x10 (bug?)
* - HRMOR = 0x08000000 (128M)
* - no physical memory to enable/train, everything accessible from start
* 1. bootblock loaded by HBBL
* - NIA = 0
* - HRMOR = 0xF8000000 (4G - 128M)
* - initialized L3 = 0x400000 (4M)
* - top address before RAM = 0xF8400000
* 2. bootblock in SEEPROM, loaded by SBE
* - NIA = 0x3000 (placeholder for int. vectors)
* - HRMOR = 0xF8200000 (4G - 128 M + 2 M)
* - initialized L3 = 0x8000 (bootblock/HBBL size = 32K)
* - no way 32K will be enough, must initialize more L3 in bootblock
* - HRMOR still applies, so memory overlaps every 2M
*
* Common subset (assuming 2. initializes as much memory as possible) is
* 0xF8200000-0xF8400000. 2M should be more than enough for pre-RAM code,
* but it isn't enough to load ramstage. We could implement postcar stage,
* but KISS: initialize L3 from _ebootblock to 0xF8980000: up to 9.5M into
* cache, leaving bottom 2M (0xF8000000-0xF8200000) either uninitialized
* (when started from SEEPROM) or just unused for anything but bootblock
* (loaded by HBBL). Last 0.5M of L3 cache is left for interrupt vectors
* normally located at address 0.
*
* Set HRMOR to 0 before jumping to C code in bootblock and forget it even
* exists.
*
* For QEMU s/0xF8/0x08/ in above description but code remains the same.
* L3 initialization is unnecessary in this case but won't break anything.
*
* TODO: there is a structure with SBE->HBBL data at 0 in 2nd option. It
* holds some useful data like XSCOM BAR and LPC BAR. If, for any reason,
* these addresses are different than default, they should be used instead
* of predefined values.
*/
#if !CONFIG(BOOTBLOCK_IN_SEEPROM)
BOOTBLOCK( 0xF8000000, 32K)
#else
BOOTBLOCK( 0xF8203000, 20K)
#endif
STACK( 0xF8208000, 32K)
PRERAM_CBMEM_CONSOLE(0xF8210000, 128K)
FMAP_CACHE( 0xF8230000, 4K)
CBFS_MCACHE( 0xF8231000, 8K)
TIMESTAMP( 0xF8233000, 4K)
ROMSTAGE( 0xF8240000, 256K)
/*
* bootblock_crt0.S assumes this is the last part of L3, leaving for
* interrupt vectors at least 0.5M because of cache associativity. If
* more CBFS_CACHE is needed, split this into pre-/postram caches.
*/
CBFS_CACHE( 0xF8280000, 7M)
RAMSTAGE( 0xF9000000, 2M)
}

View file

@ -30,18 +30,24 @@ endif
[ -e "$(KEYDIR)/hw_key_b.key" ] || ( echo "error: $(KEYDIR)/hw_key_b.key" is missing; exit 1 )
[ -e "$(KEYDIR)/hw_key_c.key" ] || ( echo "error: $(KEYDIR)/hw_key_c.key" is missing; exit 1 )
[ -e "$(KEYDIR)/sw_key_p.key" ] || ( echo "error: $(KEYDIR)/sw_key_p.key" is missing; exit 1 )
$(CREATE_CONTAINER) -a $(KEYDIR)/hw_key_a.key -b $(KEYDIR)/hw_key_b.key -c $(KEYDIR)/hw_key_c.key \
-p $(KEYDIR)/sw_key_p.key --payload $(objcbfs)/bootblock.bin \
--imagefile $(obj)/bootblock.signed
$(CREATE_CONTAINER) -a $(KEYDIR)/hw_key_a.key -b $(KEYDIR)/hw_key_b.key -c $(KEYDIR)/hw_key_c.key \
-p $(KEYDIR)/sw_key_p.key --payload $< --imagefile $<.signed
@printf " ECC $(subst $(obj)/,,$<)\n"
$(ECCTOOL) --inject $<.signed --output $<.signed.ecc --p8
ifeq ($(CONFIG_BOOTBLOCK_IN_SEEPROM),y)
@printf " ECC bootblock\n"
$(ECCTOOL) --inject $(objcbfs)/bootblock.bin --output $(obj)/bootblock.ecc --p8
else
@printf " SBSIGN bootblock\n"
$(CREATE_CONTAINER) -a $(KEYDIR)/hw_key_a.key -b $(KEYDIR)/hw_key_b.key -c $(KEYDIR)/hw_key_c.key \
-p $(KEYDIR)/sw_key_p.key --payload $(objcbfs)/bootblock.bin \
--imagefile $(obj)/bootblock.signed
$(ECCTOOL) --inject $< --output $<.ecc --p8
@printf " ECC bootblock\n"
dd if=$(obj)/bootblock.signed of=$(obj)/bootblock.signed.pad ibs=25486 conv=sync 2> /dev/null
$(ECCTOOL) --inject $(obj)/bootblock.signed.pad --output $(obj)/bootblock.signed.ecc --p8
rm $(obj)/bootblock.signed $(obj)/bootblock.signed.pad
endif # CONFIG_BOOTBLOCK_IN_SEEPROM
files_added:: sign_and_add_ecc