libcheri_invoke
is defined in assembly code; two versions for different ABIs respectively.
The Hybrid MIPS ABI (github):
# file: lib/libcheri/mips/libcheri_invoke_hybrid.S
#
# Assembly wrapper for CCall on an object-capability. Its function is to save
# and restore any general-purpose and capability registers needed on either
# side of CCall, but not handled by the compiler. This is done by creating an
# on-stack frame which will be pointed to by $idc before CCall, and then
# unwrapping it again. We rely on the compiler and CCall to sort out clearing
# of registers that require it, since they have the information to do so.
#
# Calling conventions into libcheri_invoke:
#
# Registers Description
# $c0 MIPS address space
# $c1, $c2 Invoked capabilities
# $c3..$c10 Argument capabilities
# $c11..$c16 Caller-save capabilities
# $c17..$c26 Callee-save capabilities
#
# Calling conventions implemented by CCall:
#
# $c1 Invoked code capability
# $c2 Invoked data capability
# $c3..$c10 Argument capabilities; $c3 as return capability
# $c11..$c16 n/a
# $c17..$c25 n/a
# $c26 IDC
#
# XXXRW: Worries/TODO:
#
# - Floating-point registers.
# - The compiler needs to handle unused argument/return registers.
#
.text
.global libcheri_invoke
.global cheri_invoke
.ent libcheri_invoke
libcheri_invoke:
cheri_invoke:
#
# Wrap up all caller-save state suitable to be preseved by CCall and
# restored by CReturn. This happens in two phases:
#
# 1. First, use the conventional stack to save as many caller-save
# general-purpose and capability registers as possible.
#
# 2. Produce a value for $csp that bundles these up suitable to
# bootstrap restoration. This will save the original $idc, $csp,
# $sp, and $ddc.
#
# Then after CReturn, reverse these steps by first unwrapping $idc,
# and then performing a more normal function return.
#
# The caller $csp will describe a stack fragment, which gives us a bit
# of space to store useful things, such as $sp, that are otherwise
# quite hard to restore (for obvious reasons).
#
# NB: This is still required for the hybrid ABI, unlike the
# pure-capability ABI, because we need to save and restore $sp.
#
# Save callee-save general-purpose registers.
#
# Caller-save registers are: $s0..$s7, $gp, $sp, $s8 ($fp).
#
# Do also save $ra so that we can return properly.
#
# NB: Use 96 rather than 88 for the stack to ensure 32-byte alignment
# for capabilities stored and loaded from it later.
#
# XXXRW: Possibly with the __ccall calling convention, the compiler
# should be doing this?
#
libcheri_invoke_save_regs:
daddiu $sp, -96
sd $s0, 0($sp)
...
sd $s7, 56($sp)
sd $gp, 64($sp)
sd $fp, 72($sp)
sd $ra, 80($sp)
sd $t9, 88($sp)
#
# Save capability registers we later need to restore (that won't be
# handled by CCall for us).
#
libcheri_invoke_save_caps:
cgetdefault $c11
daddiu $sp, -10*CHERICAP_SIZE
csc $c17, $sp, 0($c11)
csc $c18, $sp, 1*CHERICAP_SIZE($c11)
...
csc $c26, $sp, 9*CHERICAP_SIZE($c11)
#
# Stash $sp in the offset of $c11 so that it will be saved and
# restored by CCall.
#
csetoffset $c11, $c11, $sp
#
# The compiler is responsible for scrubbing unused argument registers
# (since only it has the information required to do so). CCall is
# responsible for scrubbing all other registers.
#
#
# Construct link-address PCC.
#
# XXXRW: Do we want a CCall variant like CJALR that automatically
# builds the desired capability?
#
dla $t0, libcheri_invoke_ccall_linkaddr
cgetpcc $c17
csetoffset $c17, $c17, $t0
#
# Invoke object capability. CCall/CReturn will save and restore $csp.
#
libcheri_invoke_ccall:
CCALL($c1, $c2)
libcheri_invoke_ccall_linkaddr:
#
# Unstash $sp from offset of $c11; restore $c11 offset to zero and
# install as DDC.
#
cgetoffset $sp, $c11
csetoffset $c11, $c11, $zero
csetdefault $c11
#
# Restore capability registers from stack.
#
libcheri_invoke_restore_caps:
clc $c17, $sp, 0($c11)
clc $c18, $sp, 1*CHERICAP_SIZE($c11)
...
clc $c26, $sp, 9*CHERICAP_SIZE($c11)
...
#
# CCall has conservatively cleared all non-return-value registers, and
# so we don't need to.
#
# Restore general-purpose registers from the stack.
#
libcheri_invoke_restore_regs:
ld $s0, 0($sp)
ld $s1, 8($sp)
...
ld $s7, 56($sp)
ld $gp, 64($sp)
ld $fp, 72($sp)
ld $ra, 80($sp)
daddiu $sp, 96
#
# Return to C-language caller.
#
libcheri_invoke_return:
jr $ra
nop # Branch-delay slot
The Pure-capability CheriABI: (github)
# file: lib/libcheri/mips/libcheri_invoke_cabi.S
# Calling conventions into libcheri_invoke:
#
# Registers Description
# $c0 MIPS address space
# $c1, $c2 Invoked capabilities
# $c3..$c10 Argument capabilities
# $c11..$c16 Caller-save capabilities
# $c17..$c26 Callee-save capabilities
#
# Calling conventions implemented by CCall:
#
# $c1 Invoked code capability
# $c2 Invoked data capability
# $c3..$c10 Argument capabilities; $c3 as return capability
# $c11..$c16 n/a
# $c17..$c25 n/a
# $c26 IDC
#
# XXXRW: Worries/TODO:
#
# - Floating-point registers.
# - The compiler needs to handle unused argument/return registers.
#
#define LIBCHERI_INVOKE_FRAME_SIZE (11*CHERICAP_SIZE + 96)
NESTED(libcheri_invoke, LIBCHERI_INVOKE_FRAME_SIZE, _FRAME_RETURN_REG)
XNESTED(cheri_invoke)
#
# Wrap up all caller-save state suitable to be preseved by CCall and
# restored by CReturn. This happens in two phases:
#
# 1. First, use the conventional stack to save as many caller-save
# general-purpose and capability registers as possible.
#
# 2. Produce a value for $csp that bundles these up suitable to
# bootstrap restoration. This will save the original $idc, $csp,
# and $ddc.
#
# Then after CReturn, reverse these steps by first unwrapping $idc,
# and then performing a more normal function return.
#
# The caller $csp will describe a stack fragment, which gives us a bit
# of space to store useful things that are otherwise hard to restore.
#
# NB: This is no longer required for the pure-capability ABI.
#
# Save callee-save general-purpose registers.
#
# Caller-save registers are: $s0..$s7, $gp, $s8 ($fp).
#
# NB: Use 96 rather than 88 for the stack to ensure 32-byte alignment
# for capabilities stored and loaded from it later.
#
# XXXRW: Possibly with the __ccall calling convention, the compiler
# should be doing this?
#
libcheri_invoke_save_regs:
cincoffset $csp, $csp, -96
csd $s0, $zero, 0($csp)
csd $s1, $zero, 8($csp)
...
csd $s7, $zero, 56($csp)
csd $gp, $zero, 64($csp)
csd $fp, $zero, 72($csp)
csd $ra, $zero, 80($csp)
#
# Save capability registers we later need to restore (that won't be
# handled by CCall for us).
#
libcheri_invoke_save_caps:
cgetdefault $c12
cincoffset $csp, $csp, -11*CHERICAP_SIZE
csc $c17, $zero, 0($csp)
csc $c18, $zero, CHERICAP_SIZE($csp)
...
csc $c26, $zero, 9*CHERICAP_SIZE($csp)
csc $c12, $zero, 10*CHERICAP_SIZE($csp)
#
# The compiler is responsible for scrubbing unused argument registers
# (since only it has the information required to do so). CCall is
# responsible for scrubbing all other registers.
#
#
# Construct link-address PCC.
#
# XXXRW: Do we want a CCall variant like CJALR that automatically
# builds the desired capability?
#
dla $t0, libcheri_invoke_ccall_linkaddr
cgetpcc $c17
csetoffset $c17, $c17, $t0
#
# Invoke object capability. CCall/CReturn will save and restore $csp.
#
libcheri_invoke_ccall:
CCALL($c1, $c2)
libcheri_invoke_ccall_linkaddr:
#
# Restore capability registers from stack.
#
libcheri_invoke_restore_caps:
clc $c17, $zero, 0($csp)
clc $c18, $zero, CHERICAP_SIZE($csp)
...
clc $c26, $zero, 9*CHERICAP_SIZE($csp)
clc $c12, $zero, 10*CHERICAP_SIZE($csp)
csetdefault $c12
...
# Restore general-purpose registers from the stack.
#
# XXXRW: Possibly with the __ccall calling convention, the compiler
# should be doing this?
#
libcheri_invoke_restore_regs:
cld $s0, $zero, 0($csp)
cld $s1, $zero, 8($csp)
cld $s2, $zero, 16($csp)
cld $s3, $zero, 24($csp)
cld $s4, $zero, 32($csp)
cld $s5, $zero, 40($csp)
cld $s6, $zero, 48($csp)
cld $s7, $zero, 56($csp)
cld $gp, $zero, 64($csp)
cld $fp, $zero, 72($csp)
cld $ra, $zero, 80($csp)
cincoffset $csp, $csp, 96
#
# Return to C-language caller.
#
libcheri_invoke_return:
cjr $c17
nop # Branch-delay slot
END(libcheri_invoke)
CCALL
is defined as a macro wrapper for ccall
in CheriBSD, source code: (github)
// file: sys/mips/include/cheriasm.h
#define CCALL(cb, cd) \
.set push; \
.set noreorder; \
ccall cb, cd, 1; \
nop; /* Fill branch delay slot for old harware*/ \
.set pop;
The libcheri_invoke_ccall
after compilation:
128-bit mode are the same:
Reference1
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?