Booting


Q & A

  • where is the tagged memory being partitioned?

The call path:

boot2 ->

? -> locore.S: _locore/_start/unknown/btext -> platform_start()

? cheri_cpu_startup() in mips/cheri/cheri.c

conf/ldscript.mips.mips64:34 ENTRY(_start) ?

The BERIpad tablet: open-source construction, CPU, OS and applications:

A small built-in ROM in BERI is able to relocate a FreeBSD kernel out of flash, or, if a DIP switch is set, make use of a kernel loaded directly into DRAM using JTAG. After relocation, the boot ROM interprets the kernel’s ELF header, passing in information on DRAM configuration when it jumps to the kernel’s start routine.

We describe BERI board configurations using Flat Device Trees (FDT), which are also used on PowerPC and ARM-based systems [12]. FDT files describe the bus topology, generally simple for system-onchip configurations, including memory addresses and interrupt information for peripherals available to the operating system.

beri_machdep.c: platform_start

// sys/mips/beri/beri_machdep.c: 182

void
platform_start(__register_t a0, __register_t a1,  __register_t a2,
    __register_t a3){

...

#ifdef CPU_CHERI
                /* Ensure that we don't write to the tag memory */
                if (memsize > 2ul * 1024 * 1024 * 1024)
                        panic("invalid memsize 0x%lx", memsize);
                /*
                 * The memory size reported by miniboot is wrong for CHERI:
                 * The tag memory always starts at 3EFFC000, so we mustn't
                 * write to any addresses higher than 3EFFC000.
                 */
                memsize = MIN(memsize, 0x3EFFC000);
#endif


...
	}

locore.S

Where to call locore.S:_start:

conf/ldscript.mips.mips64:34 ENTRY(_start)

_start/_locore/unkown/btext

  • Enable FPU (COP_1);
  • Enable CHERI (COP_2);

    /sys/mips/mips/locore.S
    
    #if defined(CPU_CHERI)
        CHERI_LOCORE_ROOT_CAPS
    
        /*
         * Create the parent kernel sealing capablity.
         */
        cmove           CHERI_REG_C27, CHERI_REG_C28
        REG_LI  t0, CHERI_SEALCAP_KERNEL_BASE
        csetoffset      CHERI_REG_C27, CHERI_REG_C27, t0
        REG_LI  t0, CHERI_SEALCAP_KERNEL_LENGTH
        csetbounds      CHERI_REG_C27, CHERI_REG_C27, t0
        REG_LI  t0, CHERI_SEALCAP_KERNEL_PERMS
        candperm        CHERI_REG_C27, CHERI_REG_C27, t0
        PTR_LA  t0, _C_LABEL(kernel_sealcap)
        csc     CHERI_REG_C27, t0, 0($ddc)
    ...
    #endif 

boot2

who calls locore.S:_start?

_start

In sys/mips/mips/locore.S: ASM_ENTRY(_start), from about line 125 to 214:

  • enable coprocessor 2;
  • derive DDC/KDC, PCC/KCC, in CHERI_LOCORE_ROOT_CAPS (sys/mips/include/cheriasm.h).
  • create the parent kernel sealing capability; derived from macro CHERI_SEALCAP_KERNEL_BASE, etc.; stored as kernel_sealcap;
  • create a capability covering all of userspace from which to derive new capabilities in execve(), etc; stored as userspace_cap;
  • create a user space sealing capability; derived from macro CHERI_SEALCAP_USERSPACE_BASE, etc.; stored as user_sealcap;
  • user space seal cap is also used as swap_restore_cap;

_start -> mi_startup(frame)

initial DDC/KDC

The capabilities derived for DDC/KDC and for PCC/KCC cover the entirety of the kernel. (Acutally changing base/length requires changes in linkage).

It is done in a macro CHERI_LOCORE_ROOT_CAPS (sys/mips/include/cheriasm.h), defined as follows:

#define CHERI_LOCORE_ROOT_CAPS \
	/* Grab the initial omnipotent capability */                 \
	cgetdefault	CHERI_REG_C28;                               \
	                                                             \
	/* Create a reduced DDC.  */                                 \
	cmove		CHERI_REG_C27, CHERI_REG_C28;                \
	REG_LI		t0, CHERI_CAP_KERN_BASE;                     \
	csetoffset	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	REG_LI		t0, CHERI_CAP_KERN_LENGTH;                   \
	csetbounds	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	REG_LI		t0, CHERI_PERMS_KERNEL_DATA;                 \
	candperm	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	                                                             \
	/* Preserve a copy in KDC for exception handlers. */         \
	csetkdc		CHERI_REG_C27;                               \
	                                                             \
	/* Install the new DDC. */                                   \
	csetdefault	CHERI_REG_C27;                               \
	                                                             \
	/* Create a reduced PCC.  */                                 \
	cgetpcc		CHERI_REG_C27;                               \
	REG_LI		t0, CHERI_CAP_KERN_BASE;                     \
	csetoffset	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	REG_LI		t0, CHERI_CAP_KERN_LENGTH;                   \
	csetbounds	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	REG_LI		t0, CHERI_PERMS_KERNEL_CODE;                 \
	candperm	CHERI_REG_C27, CHERI_REG_C27, t0;            \
	                                                             \
	/* Preserve a copy in KCC for exception handlers.  */        \
	                                                             \
	csetkcc		CHERI_REG_C27;                               \
	                                                             \
	/* Install the new PCC. */                                   \
	REG_LI		t0, CHERI_CAP_KERN_BASE;                     \
	cgetpcc		CHERI_REG_C28;                       /* 0 */ \
	cgetoffset	t1, CHERI_REG_C28;                   /* 1 */ \
	PTR_SUBU	t1, t1, t0;                          /* 2 */ \
	PTR_ADDIU	t1, t1, (4 * 7);                     /* 3 */ \
	csetoffset	CHERI_REG_C27, CHERI_REG_C27, t1;    /* 4 */ \
	cjr		CHERI_REG_C27;                       /* 5 */ \
	nop;                                                 /* 6 */ \
	/* 7 (land here) */

From above code:

KDC and DDC are set to be the same, whose offset: CHERI_CAP_KERN_BASE, bounds CHERI_CAP_KERN_LENGTH, perm CHERI_PERMS_KERNEL_DATA.

KCC is set with offset CHERI_CAP_KERN_BASE, bounds CHERI_CAP_KERN_LENGTH, permission CHERI_PERMS_KERNEL_CODE.

PCC is set with offset -CHERI_CAP_KERN_BASE + 4*7, bounds, permission, and finally set by cjr. NO BOUNDS, PERMS are set for PCC???

After above executed, C28 stores PCC with offset at line /* 0 */, C27 stores KCC with offset at line /* 7 */.

Init kernel sealing capability

In the previous step, C27 stores PCC with offset at line /* 7 */; C28 stores PCC with offset at line /* 0 */. Next these two will be reused to create kernel sealing capability.

in file: sys/mips/mips/locore.S: ASM_ENTRY(_start):

	CHERI_LOCORE_ROOT_CAPS /* set default DDC/KDC; also stored in C27 (DDC), C28*/
/*
* Create the parent kernel sealing capablity.
*/
cmove           CHERI_REG_C27, CHERI_REG_C28  /* C27 = C28 */
REG_LI  t0, CHERI_SEALCAP_KERNEL_BASE         /* li load immediate to t0*/
csetoffset      CHERI_REG_C27, CHERI_REG_C27, t0
REG_LI  t0, CHERI_SEALCAP_KERNEL_LENGTH
csetbounds      CHERI_REG_C27, CHERI_REG_C27, t0
REG_LI  t0, CHERI_SEALCAP_KERNEL_PERMS
candperm        CHERI_REG_C27, CHERI_REG_C27, t0
PTR_LA  t0, _C_LABEL(kernel_sealcap)
csc     CHERI_REG_C27, t0, 0($ddc)

Here kernel_sealcap is initialized to have PCC with offset CHERI_SEALCAP_KERNEL_BASE, bounds CHERI_SEALCAP_KERNEL_LENGTH, perms CHERI_SEALCAP_KERNEL_PERMS. Then the sealing cap is stored to memory as kernel_sealcap.

Note that how address of kernel_sealcap, t0, is used to store a capability by csc CHERI_REG_C27, t0, 0($ddc).

In order to store a capability to memory, we need a capability, usually $ddc, to grant permissions, an address, here t0 must be a valid address within this capability.

mi_startup(frame)

Created Aug 27, 2019 // Last Updated May 1, 2020

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?