aboutsummaryrefslogtreecommitdiff
path: root/lib/extensions/sve/sve.c
blob: e031bf61b95f039617f32a3ecf77c6f985f91e02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch.h>
#include <arch_helpers.h>
#include <pubsub.h>
#include <stdbool.h>
#include <sve.h>

bool sve_supported(void)
{
	uint64_t features;

	features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_SVE_SHIFT;
	return (features & ID_AA64PFR0_SVE_MASK) == 1U;
}

static void *disable_sve_hook(const void *arg)
{
	uint64_t cptr;

	if (!sve_supported())
		return (void *)-1;

	/*
	 * Disable SVE, SIMD and FP access for the Secure world.
	 * As the SIMD/FP registers are part of the SVE Z-registers, any
	 * use of SIMD/FP functionality will corrupt the SVE registers.
	 * Therefore it is necessary to prevent use of SIMD/FP support
	 * in the Secure world as well as SVE functionality.
	 */
	cptr = read_cptr_el3();
	cptr = (cptr | TFP_BIT) & ~(CPTR_EZ_BIT);
	write_cptr_el3(cptr);

	/*
	 * No explicit ISB required here as ERET to switch to Secure
	 * world covers it
	 */
	return (void *)0;
}

static void *enable_sve_hook(const void *arg)
{
	uint64_t cptr;

	if (!sve_supported())
		return (void *)-1;

	/*
	 * Enable SVE, SIMD and FP access for the Non-secure world.
	 */
	cptr = read_cptr_el3();
	cptr = (cptr | CPTR_EZ_BIT) & ~(TFP_BIT);
	write_cptr_el3(cptr);

	/*
	 * No explicit ISB required here as ERET to switch to Non-secure
	 * world covers it
	 */
	return (void *)0;
}

void sve_enable(bool el2_unused)
{
	uint64_t cptr;

	if (!sve_supported())
		return;

#if CTX_INCLUDE_FPREGS
	/*
	 * CTX_INCLUDE_FPREGS is not supported on SVE enabled systems.
	 */
	assert(0);
#endif
	/*
	 * Update CPTR_EL3 to enable access to SVE functionality for the
	 * Non-secure world.
	 * NOTE - assumed that CPTR_EL3.TFP is set to allow access to
	 * the SIMD, floating-point and SVE support.
	 *
	 * CPTR_EL3.EZ: Set to 1 to enable access to SVE  functionality
	 *  in the Non-secure world.
	 */
	cptr = read_cptr_el3();
	cptr |= CPTR_EZ_BIT;
	write_cptr_el3(cptr);

	/*
	 * Need explicit ISB here to guarantee that update to ZCR_ELx
	 * and CPTR_EL2.TZ do not result in trap to EL3.
	 */
	isb();

	/*
	 * Ensure lower ELs have access to full vector length.
	 */
	write_zcr_el3(ZCR_EL3_LEN_MASK);

	if (el2_unused) {
		/*
		 * Update CPTR_EL2 to enable access to SVE functionality
		 * for Non-secure world, EL2 and Non-secure EL1 and EL0.
		 * NOTE - assumed that CPTR_EL2.TFP is set to allow
		 * access to the SIMD, floating-point and SVE support.
		 *
		 * CPTR_EL2.TZ: Set to 0 to enable access to SVE support
		 *  for EL2 and Non-secure EL1 and EL0.
		 */
		cptr = read_cptr_el2();
		cptr &= ~(CPTR_EL2_TZ_BIT);
		write_cptr_el2(cptr);

		/*
		 * Ensure lower ELs have access to full vector length.
		 */
		write_zcr_el2(ZCR_EL2_LEN_MASK);
	}
	/*
	 * No explicit ISB required here as ERET to switch to
	 * Non-secure world covers it.
	 */
}

SUBSCRIBE_TO_EVENT(cm_exited_normal_world, disable_sve_hook);
SUBSCRIBE_TO_EVENT(cm_entering_normal_world, enable_sve_hook);