Skip to content

Commit

Permalink
RISC-V: enable XIP
Browse files Browse the repository at this point in the history
Introduce XIP (eXecute In Place) support for RISC-V platforms.
It allows code to be executed directly from non-volatile storage
directly addressable by the CPU, such as QSPI NOR flash which can
be found on many RISC-V platforms. This makes way for significant
optimization of RAM footprint. The XIP kernel is not compressed
since it has to run directly from flash, so it will occupy more
space on the non-volatile storage. The physical flash address used
to link the kernel object files and for storing it has to be known
at compile time and is represented by a Kconfig option.

XIP on RISC-V will for the time being only work on MMU-enabled
kernels.

Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.com>
[Alex: Rebase on top of "Move kernel mapping outside the linear mapping" ]
Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
[Palmer: disable XIP for allyesconfig]
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
  • Loading branch information
vwool authored and palmer-dabbelt committed Apr 26, 2021
1 parent 5640975 commit 44c9225
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 19 deletions.
60 changes: 56 additions & 4 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ config RISCV
select ARCH_HAS_PTE_SPECIAL
select ARCH_HAS_SET_DIRECT_MAP
select ARCH_HAS_SET_MEMORY
select ARCH_HAS_STRICT_KERNEL_RWX if MMU
select ARCH_HAS_STRICT_MODULE_RWX if MMU
select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
Expand Down Expand Up @@ -468,7 +468,7 @@ config EFI_STUB

config EFI
bool "UEFI runtime support"
depends on OF
depends on OF && !XIP_KERNEL
select LIBFDT
select UCS2_STRING
select EFI_PARAMS_FROM_FDT
Expand All @@ -492,11 +492,63 @@ config STACKPROTECTOR_PER_TASK
def_bool y
depends on STACKPROTECTOR && CC_HAVE_STACKPROTECTOR_TLS

config PHYS_RAM_BASE_FIXED
bool "Explicitly specified physical RAM address"
default n

config PHYS_RAM_BASE
hex "Platform Physical RAM address"
depends on PHYS_RAM_BASE_FIXED
default "0x80000000"
help
This is the physical address of RAM in the system. It has to be
explicitly specified to run early relocations of read-write data
from flash to RAM.

config XIP_KERNEL
bool "Kernel Execute-In-Place from ROM"
depends on MMU && SPARSEMEM
# This prevents XIP from being enabled by all{yes,mod}config, which
# fail to build since XIP doesn't support large kernels.
depends on !COMPILE_TEST
select PHYS_RAM_BASE_FIXED
help
Execute-In-Place allows the kernel to run from non-volatile storage
directly addressable by the CPU, such as NOR flash. This saves RAM
space since the text section of the kernel is not loaded from flash
to RAM. Read-write sections, such as the data section and stack,
are still copied to RAM. The XIP kernel is not compressed since
it has to run directly from flash, so it will take more space to
store it. The flash address used to link the kernel object files,
and for storing it, is configuration dependent. Therefore, if you
say Y here, you must know the proper physical address where to
store the kernel image depending on your own flash memory usage.

Also note that the make target becomes "make xipImage" rather than
"make zImage" or "make Image". The final kernel binary to put in
ROM memory will be arch/riscv/boot/xipImage.

SPARSEMEM is required because the kernel text and rodata that are
flash resident are not backed by memmap, then any attempt to get
a struct page on those regions will trigger a fault.

If unsure, say N.

config XIP_PHYS_ADDR
hex "XIP Kernel Physical Location"
depends on XIP_KERNEL
default "0x21000000"
help
This is the physical address in your flash memory the kernel will
be linked for and stored to. This address is dependent on your
own flash usage.

endmenu

config BUILTIN_DTB
def_bool n
bool
depends on OF
default y if XIP_KERNEL

menu "Power management options"

Expand Down
8 changes: 7 additions & 1 deletion arch/riscv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ CHECKFLAGS += -D__riscv -D__riscv_xlen=$(BITS)

# Default target when executing plain make
boot := arch/riscv/boot
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := $(boot)/xipImage
else
KBUILD_IMAGE := $(boot)/Image.gz
endif

head-y := arch/riscv/kernel/head.o

Expand All @@ -96,12 +100,14 @@ PHONY += vdso_install
vdso_install:
$(Q)$(MAKE) $(build)=arch/riscv/kernel/vdso $@

ifneq ($(CONFIG_XIP_KERNEL),y)
ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_SOC_CANAAN),yy)
KBUILD_IMAGE := $(boot)/loader.bin
else
KBUILD_IMAGE := $(boot)/Image.gz
endif
BOOT_TARGETS := Image Image.gz loader loader.bin
endif
BOOT_TARGETS := Image Image.gz loader loader.bin xipImage

all: $(notdir $(KBUILD_IMAGE))

Expand Down
13 changes: 13 additions & 0 deletions arch/riscv/boot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,21 @@
KCOV_INSTRUMENT := n

OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
OBJCOPYFLAGS_xipImage :=-O binary -R .note -R .note.gnu.build-id -R .comment -S

targets := Image Image.* loader loader.o loader.lds loader.bin
targets := Image Image.* loader loader.o loader.lds loader.bin xipImage

ifeq ($(CONFIG_XIP_KERNEL),y)

quiet_cmd_mkxip = $(quiet_cmd_objcopy)
cmd_mkxip = $(cmd_objcopy)

$(obj)/xipImage: vmlinux FORCE
$(call if_changed,mkxip)
@$(kecho) ' Physical Address of xipImage: $(CONFIG_XIP_PHYS_ADDR)'

endif

$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
Expand Down
21 changes: 21 additions & 0 deletions arch/riscv/include/asm/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ extern unsigned long va_pa_offset;
#ifdef CONFIG_64BIT
extern unsigned long va_kernel_pa_offset;
#endif
#ifdef CONFIG_XIP_KERNEL
extern unsigned long va_kernel_xip_pa_offset;
#endif
extern unsigned long pfn_base;
#define ARCH_PFN_OFFSET (pfn_base)
#else
Expand All @@ -107,11 +110,29 @@ extern unsigned long pfn_base;
extern unsigned long kernel_virt_addr;

#define linear_mapping_pa_to_va(x) ((void *)((unsigned long)(x) + va_pa_offset))
#ifdef CONFIG_XIP_KERNEL
#define kernel_mapping_pa_to_va(y) ({ \
unsigned long _y = y; \
(_y >= CONFIG_PHYS_RAM_BASE) ? \
(void *)((unsigned long)(_y) + va_kernel_pa_offset + XIP_OFFSET) : \
(void *)((unsigned long)(_y) + va_kernel_xip_pa_offset); \
})
#else
#define kernel_mapping_pa_to_va(x) ((void *)((unsigned long)(x) + va_kernel_pa_offset))
#endif
#define __pa_to_va_nodebug(x) linear_mapping_pa_to_va(x)

#define linear_mapping_va_to_pa(x) ((unsigned long)(x) - va_pa_offset)
#ifdef CONFIG_XIP_KERNEL
#define kernel_mapping_va_to_pa(y) ({ \
unsigned long _y = y; \
(_y < kernel_virt_addr + XIP_OFFSET) ? \
((unsigned long)(_y) - va_kernel_xip_pa_offset) : \
((unsigned long)(_y) - va_kernel_pa_offset - XIP_OFFSET); \
})
#else
#define kernel_mapping_va_to_pa(x) ((unsigned long)(x) - va_kernel_pa_offset)
#endif
#define __va_to_pa_nodebug(x) ({ \
unsigned long _x = x; \
(_x < kernel_virt_addr) ? \
Expand Down
25 changes: 23 additions & 2 deletions arch/riscv/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@
#define FIXADDR_SIZE PGDIR_SIZE
#endif
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)

#ifdef CONFIG_XIP_KERNEL
#define XIP_OFFSET SZ_8M
#define XIP_FIXUP(addr) ({ \
uintptr_t __a = (uintptr_t)(addr); \
(__a >= CONFIG_XIP_PHYS_ADDR && __a < CONFIG_XIP_PHYS_ADDR + SZ_16M) ? \
__a - CONFIG_XIP_PHYS_ADDR + CONFIG_PHYS_RAM_BASE - XIP_OFFSET :\
__a; \
})
#else
#define XIP_FIXUP(addr) (addr)
#endif /* CONFIG_XIP_KERNEL */

#endif

#ifndef __ASSEMBLY__
Expand Down Expand Up @@ -507,8 +520,16 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
#define kern_addr_valid(addr) (1) /* FIXME */

extern char _start[];
extern void *dtb_early_va;
extern uintptr_t dtb_early_pa;
extern void *_dtb_early_va;
extern uintptr_t _dtb_early_pa;
#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_MMU)
#define dtb_early_va (*(void **)XIP_FIXUP(&_dtb_early_va))
#define dtb_early_pa (*(uintptr_t *)XIP_FIXUP(&_dtb_early_pa))
#else
#define dtb_early_va _dtb_early_va
#define dtb_early_pa _dtb_early_pa
#endif /* CONFIG_XIP_KERNEL */

void setup_bootmem(void);
void paging_init(void);
void misc_mem_init(void);
Expand Down
46 changes: 45 additions & 1 deletion arch/riscv/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,23 @@
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/csr.h>
#include <asm/hwcap.h>
#include <asm/image.h>
#include "efi-header.S"

#ifdef CONFIG_XIP_KERNEL
.macro XIP_FIXUP_OFFSET reg
REG_L t0, _xip_fixup
add \reg, \reg, t0
.endm
_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
#else
.macro XIP_FIXUP_OFFSET reg
.endm
#endif /* CONFIG_XIP_KERNEL */

__HEAD
ENTRY(_start)
/*
Expand Down Expand Up @@ -70,6 +82,7 @@ pe_head_start:
relocate:
/* Relocate return address */
la a1, kernel_virt_addr
XIP_FIXUP_OFFSET a1
REG_L a1, 0(a1)
la a2, _start
sub a1, a1, a2
Expand All @@ -92,6 +105,7 @@ relocate:
* to ensure the new translations are in use.
*/
la a0, trampoline_pg_dir
XIP_FIXUP_OFFSET a0
srl a0, a0, PAGE_SHIFT
or a0, a0, a1
sfence.vma
Expand Down Expand Up @@ -145,7 +159,9 @@ secondary_start_sbi:

slli a3, a0, LGREG
la a4, __cpu_up_stack_pointer
XIP_FIXUP_OFFSET a4
la a5, __cpu_up_task_pointer
XIP_FIXUP_OFFSET a5
add a4, a3, a4
add a5, a3, a5
REG_L sp, (a4)
Expand All @@ -157,6 +173,7 @@ secondary_start_common:
#ifdef CONFIG_MMU
/* Enable virtual memory and relocate to virtual address */
la a0, swapper_pg_dir
XIP_FIXUP_OFFSET a0
call relocate
#endif
call setup_trap_vector
Expand Down Expand Up @@ -237,12 +254,33 @@ pmp_done:
.Lgood_cores:
#endif

#ifndef CONFIG_XIP_KERNEL
/* Pick one hart to run the main boot sequence */
la a3, hart_lottery
li a2, 1
amoadd.w a3, a2, (a3)
bnez a3, .Lsecondary_start

#else
/* hart_lottery in flash contains a magic number */
la a3, hart_lottery
mv a2, a3
XIP_FIXUP_OFFSET a2
lw t1, (a3)
amoswap.w t0, t1, (a2)
/* first time here if hart_lottery in RAM is not set */
beq t0, t1, .Lsecondary_start

la sp, _end + THREAD_SIZE
XIP_FIXUP_OFFSET sp
mv s0, a0
call __copy_data

/* Restore a0 copy */
mv a0, s0
#endif

#ifndef CONFIG_XIP_KERNEL
/* Clear BSS for flat non-ELF images */
la a3, __bss_start
la a4, __bss_stop
Expand All @@ -252,15 +290,18 @@ clear_bss:
add a3, a3, RISCV_SZPTR
blt a3, a4, clear_bss
clear_bss_done:

#endif
/* Save hart ID and DTB physical address */
mv s0, a0
mv s1, a1

la a2, boot_cpu_hartid
XIP_FIXUP_OFFSET a2
REG_S a0, (a2)

/* Initialize page tables and relocate to virtual addresses */
la sp, init_thread_union + THREAD_SIZE
XIP_FIXUP_OFFSET sp
#ifdef CONFIG_BUILTIN_DTB
la a0, __dtb_start
#else
Expand All @@ -269,6 +310,7 @@ clear_bss_done:
call setup_vm
#ifdef CONFIG_MMU
la a0, early_pg_dir
XIP_FIXUP_OFFSET a0
call relocate
#endif /* CONFIG_MMU */

Expand All @@ -293,7 +335,9 @@ clear_bss_done:

slli a3, a0, LGREG
la a1, __cpu_up_stack_pointer
XIP_FIXUP_OFFSET a1
la a2, __cpu_up_task_pointer
XIP_FIXUP_OFFSET a2
add a1, a3, a1
add a2, a3, a2

Expand Down
3 changes: 3 additions & 0 deletions arch/riscv/kernel/head.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ extern atomic_t hart_lottery;

asmlinkage void do_page_fault(struct pt_regs *regs);
asmlinkage void __init setup_vm(uintptr_t dtb_pa);
#ifdef CONFIG_XIP_KERNEL
asmlinkage void __init __copy_data(void);
#endif

extern void *__cpu_up_stack_pointer[];
extern void *__cpu_up_task_pointer[];
Expand Down
11 changes: 8 additions & 3 deletions arch/riscv/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <asm/cpu_ops.h>
#include <asm/early_ioremap.h>
#include <asm/pgtable.h>
#include <asm/setup.h>
#include <asm/set_memory.h>
#include <asm/sections.h>
Expand Down Expand Up @@ -51,7 +52,11 @@ struct screen_info screen_info __section(".data") = {
* This is used before the kernel initializes the BSS so it can't be in the
* BSS.
*/
atomic_t hart_lottery __section(".sdata");
atomic_t hart_lottery __section(".sdata")
#ifdef CONFIG_XIP_KERNEL
= ATOMIC_INIT(0xC001BEEF)
#endif
;
unsigned long boot_cpu_hartid;
static DEFINE_PER_CPU(struct cpu, cpu_devices);

Expand Down Expand Up @@ -276,7 +281,7 @@ void __init setup_arch(char **cmdline_p)
#if IS_ENABLED(CONFIG_BUILTIN_DTB)
unflatten_and_copy_device_tree();
#else
if (early_init_dt_verify(__va(dtb_early_pa)))
if (early_init_dt_verify(__va(XIP_FIXUP(dtb_early_pa))))
unflatten_device_tree();
else
pr_err("No DTB found in kernel mappings\n");
Expand All @@ -288,7 +293,7 @@ void __init setup_arch(char **cmdline_p)

if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) {
protect_kernel_text_data();
#if defined(CONFIG_64BIT) && defined(CONFIG_MMU)
#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) && !defined(CONFIG_XIP_KERNEL)
protect_kernel_linear_mapping_text_rodata();
#endif
}
Expand Down
Loading

0 comments on commit 44c9225

Please sign in to comment.