/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2015, 2017 embedded brains GmbH. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/asm.h>
#include <rtems/score/cpuimpl.h>
#include <rtems/score/percpu.h>
#define FRAME_OFFSET_BUFFER_0 (SPARC_MINIMUM_STACK_FRAME_SIZE)
#define FRAME_OFFSET_BUFFER_1 (FRAME_OFFSET_BUFFER_0 + 0x04)
#define FRAME_OFFSET_BUFFER_2 (FRAME_OFFSET_BUFFER_1 + 0x04)
#define FRAME_OFFSET_L0 (FRAME_OFFSET_BUFFER_2 + 0x04)
#define FRAME_OFFSET_L1 (FRAME_OFFSET_L0 + 0x04)
#define FRAME_OFFSET_L2 (FRAME_OFFSET_L1 + 0x04)
#define FRAME_OFFSET_L3 (FRAME_OFFSET_L2 + 0x04)
#define FRAME_OFFSET_L4 (FRAME_OFFSET_L3 + 0x04)
#define FRAME_OFFSET_L5 (FRAME_OFFSET_L4 + 0x04)
#define FRAME_OFFSET_L6 (FRAME_OFFSET_L5 + 0x04)
#define FRAME_OFFSET_L7 (FRAME_OFFSET_L6 + 0x04)
#define FRAME_OFFSET_I0 (FRAME_OFFSET_L7 + 0x04)
#define FRAME_OFFSET_I1 (FRAME_OFFSET_I0 + 0x04)
#define FRAME_OFFSET_I2 (FRAME_OFFSET_I1 + 0x04)
#define FRAME_OFFSET_I3 (FRAME_OFFSET_I2 + 0x04)
#define FRAME_OFFSET_I4 (FRAME_OFFSET_I3 + 0x04)
#define FRAME_OFFSET_I5 (FRAME_OFFSET_I4 + 0x04)
#define FRAME_OFFSET_I6 (FRAME_OFFSET_I5 + 0x04)
#define FRAME_OFFSET_I7 (FRAME_OFFSET_I6 + 0x04)
#define FRAME_END (FRAME_OFFSET_I7 + 0x04)
#define FRAME_SIZE \
((FRAME_END + CPU_STACK_ALIGNMENT - 1) & ~(CPU_STACK_ALIGNMENT - 1))
/*
* The FSR pattern is masked with undefined fields, reserved fields, ftt
* (cleared by fmovs), cexc (cleared by fmovs), and system-specific values
* (e.g. FPU architecture version, FP queue).
*/
#define FSR_PATTERN_MASK 0xcf800fe0
.macro check_register reg
sub %g1, 1, %g1
cmp %g1, \reg
bne restore_registers
nop
.endm
.macro check_float_register reg
sub %g1, 1, %g1
st \reg, [%sp + FRAME_OFFSET_BUFFER_0]
cmp %g0, %sp
fmovs \reg, \reg
be restore_registers
nop
cmp %g0, %g0
fmovs \reg, \reg
bne restore_registers
nop
ld [%sp + FRAME_OFFSET_BUFFER_0], %o1
cmp %g1, %o1
bne restore_registers
nop
.endm
.macro write_register reg
add %g1, 1, %g1
mov %g1, \reg
.endm
.macro write_float_register reg
add %g1, 1, %g1
st %g1, [%sp + FRAME_OFFSET_BUFFER_0]
ld [%sp + FRAME_OFFSET_BUFFER_0], \reg
.endm
.align 4
PUBLIC(_CPU_Context_validate)
SYM(_CPU_Context_validate):
/* g2 indicates if the FPU should be checked */
#if defined(SPARC_USE_LAZY_FP_SWITCH)
ld [%g6 + PER_CPU_OFFSET_EXECUTING], %g2
ld [%g2 + %lo(SPARC_THREAD_CONTROL_FP_CONTEXT_OFFSET)], %g2
#else
mov %psr, %g2
sethi %hi(SPARC_PSR_EF_MASK), %g3
and %g2, %g3, %g2
#endif
/* g1 is used to save the original pattern */
mov %o0, %g1
/* g4 establishes window counter */
clr %g4
add %sp, -FRAME_SIZE, %sp
st %l0, [%sp + FRAME_OFFSET_L0]
st %l1, [%sp + FRAME_OFFSET_L1]
st %l2, [%sp + FRAME_OFFSET_L2]
st %l3, [%sp + FRAME_OFFSET_L3]
st %l4, [%sp + FRAME_OFFSET_L4]
st %l5, [%sp + FRAME_OFFSET_L5]
st %l6, [%sp + FRAME_OFFSET_L6]
st %l7, [%sp + FRAME_OFFSET_L7]
st %i0, [%sp + FRAME_OFFSET_I0]
st %i1, [%sp + FRAME_OFFSET_I1]
st %i2, [%sp + FRAME_OFFSET_I2]
st %i3, [%sp + FRAME_OFFSET_I3]
st %i4, [%sp + FRAME_OFFSET_I4]
st %i5, [%sp + FRAME_OFFSET_I5]
st %i6, [%sp + FRAME_OFFSET_I6]
st %i7, [%sp + FRAME_OFFSET_I7]
cmp %g4, 0
bne write_locals_and_outputs
nop
be check_for_fp
nop
new_check_cycle:
clr %g4
add %g4, 1, %g4
ld [%sp + FRAME_OFFSET_BUFFER_1], %g1
b switch_to_next_window
nop
/* Write pattern values into registers */
check_for_fp:
cmp %g2, 0
be write_y
nop
/* Write masked pattern to FSR */
st %fsr, [%sp + FRAME_OFFSET_BUFFER_0]
ld [%sp + FRAME_OFFSET_BUFFER_0], %o1
add %g1, 1, %g1
sethi %hi(FSR_PATTERN_MASK), %g3
or %g3, %lo(FSR_PATTERN_MASK), %g3
and %g1, %g3, %g3
or %o1, %g3, %g3
st %g3, [%sp + FRAME_OFFSET_BUFFER_0]
ld [%sp + FRAME_OFFSET_BUFFER_0], %fsr
write_float_register %f0
write_float_register %f1
write_float_register %f2
write_float_register %f3
write_float_register %f4
write_float_register %f5
write_float_register %f6
write_float_register %f7
write_float_register %f8
write_float_register %f9
write_float_register %f10
write_float_register %f11
write_float_register %f12
write_float_register %f13
write_float_register %f14
write_float_register %f15
write_float_register %f16
write_float_register %f17
write_float_register %f18
write_float_register %f19
write_float_register %f20
write_float_register %f21
write_float_register %f22
write_float_register %f23
write_float_register %f24
write_float_register %f25
write_float_register %f26
write_float_register %f27
write_float_register %f28
write_float_register %f29
write_float_register %f30
write_float_register %f31
write_y:
write_register %y
write_register %i0
write_register %i1
write_register %i2
write_register %i3
write_register %i4
write_register %i5
/* Don't write register $i6 => frame pointer */
/* Don't write register $i7 => return address */
b write_locals_and_outputs
nop
switch_to_next_window:
save %sp, -FRAME_SIZE, %sp
write_locals_and_outputs:
/* l0 is used as a scratch register */
write_register %l1
write_register %l2
write_register %l3
write_register %l4
write_register %l5
write_register %l6
write_register %l7
write_register %o1
write_register %o2
write_register %o3
write_register %o4
write_register %o5
/* Don't write register $o6 => stack pointer */
/* Don't write register $o7 => return address */
add %g4, 1, %g4
cmp %g4, 1
bne no_store
nop
st %g1, [%sp + FRAME_OFFSET_BUFFER_1]
no_store:
cmp %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS
bne switch_to_next_window
nop
/* Dummy increment to set up reverse mechanism for checking process */
add %g1, 1, %g1
clr %g4
/* Checking begins here */
window_checking:
cmp %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS
be y_checking
nop
further_checking:
cmp %g4, 0
bne goto_local_registers
nop
/* Check normal registers */
check_register %o5
check_register %o4
check_register %o3
check_register %o2
check_register %o1
goto_local_registers:
check_register %l7
check_register %l6
check_register %l5
check_register %l4
check_register %l3
check_register %l2
check_register %l1
check_register %i5
check_register %i4
check_register %i3
check_register %i2
check_register %i1
/*
For the last window i0 also needs to be checked as this variable
is not overwritten by the outputs of another window.
*/
add %g4, 1, %g4
cmp %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS
bne dont_check_i0
nop
check_register %i0
b y_checking
nop
dont_check_i0:
restore
ba window_checking
nop
/* Check Y register */
y_checking:
st %o1, [%sp + FRAME_OFFSET_BUFFER_2]
mov %y, %o1
check_register %o1
ld [%sp + FRAME_OFFSET_BUFFER_2], %o1
cmp %g2, 0
be new_check_cycle
nop
st %o1, [%sp + FRAME_OFFSET_BUFFER_2]
SPARC_LEON3FT_B2BST_NOP
/* Check floating point registers */
check_float_register %f31
check_float_register %f30
check_float_register %f29
check_float_register %f28
check_float_register %f27
check_float_register %f26
check_float_register %f25
check_float_register %f24
check_float_register %f23
check_float_register %f22
check_float_register %f21
check_float_register %f20
check_float_register %f19
check_float_register %f18
check_float_register %f17
check_float_register %f16
check_float_register %f15
check_float_register %f14
check_float_register %f13
check_float_register %f12
check_float_register %f11
check_float_register %f10
check_float_register %f9
check_float_register %f8
check_float_register %f7
check_float_register %f6
check_float_register %f5
check_float_register %f4
check_float_register %f3
check_float_register %f2
check_float_register %f1
check_float_register %f0
st %fsr, [%sp + FRAME_OFFSET_BUFFER_0]
ld [%sp + FRAME_OFFSET_BUFFER_0], %o1
sub %g1, 1, %g1
clr %g3
sethi %hi(FSR_PATTERN_MASK), %g3
or %g3, %lo(FSR_PATTERN_MASK), %g3
and %g1, %g3, %g3
and %o1, %g3, %o1
cmp %o1, %g3
bne restore_registers
ld [%sp + FRAME_OFFSET_BUFFER_2], %o1
b new_check_cycle
nop
/****** RESTORE STARTS HERE *******/
/* Restore non-volatile registers */
restore_registers:
and %g4, (SPARC_NUMBER_OF_REGISTER_WINDOWS - 1), %g4
cmp %g4, 0
be real_restore
nop
restore
sub %g4, 1, %g4
bne restore_registers
nop
real_restore:
ld [%sp + FRAME_OFFSET_L0], %l0
ld [%sp + FRAME_OFFSET_L1], %l1
ld [%sp + FRAME_OFFSET_L2], %l2
ld [%sp + FRAME_OFFSET_L3], %l3
ld [%sp + FRAME_OFFSET_L4], %l4
ld [%sp + FRAME_OFFSET_L5], %l5
ld [%sp + FRAME_OFFSET_L6], %l6
ld [%sp + FRAME_OFFSET_L7], %l7
ld [%sp + FRAME_OFFSET_I0], %i0
ld [%sp + FRAME_OFFSET_I1], %i1
ld [%sp + FRAME_OFFSET_I2], %i2
ld [%sp + FRAME_OFFSET_I3], %i3
ld [%sp + FRAME_OFFSET_I4], %i4
ld [%sp + FRAME_OFFSET_I5], %i5
ld [%sp + FRAME_OFFSET_I6], %i6
ld [%sp + FRAME_OFFSET_I7], %i7
sub %sp, -FRAME_SIZE, %sp
return_value:
/* Load callback address and jump back */
jmp %o7 + 8
add %sp, FRAME_SIZE, %sp