1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2016-20 Intel Corporation. */ |
3 | |
4 | #include <stddef.h> |
5 | #include "defines.h" |
6 | |
7 | /* |
8 | * Data buffer spanning two pages that will be placed first in the .data |
9 | * segment via the linker script. Even if not used internally the second page |
10 | * is needed by external test manipulating page permissions, so mark |
11 | * encl_buffer as "used" to make sure it is entirely preserved by the compiler. |
12 | */ |
13 | static uint8_t __used __section(".data.encl_buffer" ) encl_buffer[8192] = { 1 }; |
14 | |
15 | enum sgx_enclu_function { |
16 | EACCEPT = 0x5, |
17 | EMODPE = 0x6, |
18 | }; |
19 | |
20 | static void do_encl_emodpe(void *_op) |
21 | { |
22 | struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0}; |
23 | struct encl_op_emodpe *op = _op; |
24 | |
25 | secinfo.flags = op->flags; |
26 | |
27 | asm volatile(".byte 0x0f, 0x01, 0xd7" |
28 | : /* no outputs */ |
29 | : "a" (EMODPE), |
30 | "b" (&secinfo), |
31 | "c" (op->epc_addr) |
32 | : "memory" /* read from secinfo pointer */); |
33 | } |
34 | |
35 | static void do_encl_eaccept(void *_op) |
36 | { |
37 | struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0}; |
38 | struct encl_op_eaccept *op = _op; |
39 | int rax; |
40 | |
41 | secinfo.flags = op->flags; |
42 | |
43 | asm volatile(".byte 0x0f, 0x01, 0xd7" |
44 | : "=a" (rax) |
45 | : "a" (EACCEPT), |
46 | "b" (&secinfo), |
47 | "c" (op->epc_addr) |
48 | : "memory" /* read from secinfo pointer */); |
49 | |
50 | op->ret = rax; |
51 | } |
52 | |
53 | static void *memcpy(void *dest, const void *src, size_t n) |
54 | { |
55 | size_t i; |
56 | |
57 | for (i = 0; i < n; i++) |
58 | ((char *)dest)[i] = ((char *)src)[i]; |
59 | |
60 | return dest; |
61 | } |
62 | |
63 | static void *memset(void *dest, int c, size_t n) |
64 | { |
65 | size_t i; |
66 | |
67 | for (i = 0; i < n; i++) |
68 | ((char *)dest)[i] = c; |
69 | |
70 | return dest; |
71 | } |
72 | |
73 | static void do_encl_init_tcs_page(void *_op) |
74 | { |
75 | struct encl_op_init_tcs_page *op = _op; |
76 | void *tcs = (void *)op->tcs_page; |
77 | uint32_t val_32; |
78 | |
79 | memset(dest: tcs, c: 0, n: 16); /* STATE and FLAGS */ |
80 | memcpy(dest: tcs + 16, src: &op->ssa, n: 8); /* OSSA */ |
81 | memset(dest: tcs + 24, c: 0, n: 4); /* CSSA */ |
82 | val_32 = 1; |
83 | memcpy(dest: tcs + 28, src: &val_32, n: 4); /* NSSA */ |
84 | memcpy(dest: tcs + 32, src: &op->entry, n: 8); /* OENTRY */ |
85 | memset(dest: tcs + 40, c: 0, n: 24); /* AEP, OFSBASE, OGSBASE */ |
86 | val_32 = 0xFFFFFFFF; |
87 | memcpy(dest: tcs + 64, src: &val_32, n: 4); /* FSLIMIT */ |
88 | memcpy(dest: tcs + 68, src: &val_32, n: 4); /* GSLIMIT */ |
89 | memset(dest: tcs + 72, c: 0, n: 4024); /* Reserved */ |
90 | } |
91 | |
92 | static void do_encl_op_put_to_buf(void *op) |
93 | { |
94 | struct encl_op_put_to_buf *op2 = op; |
95 | |
96 | memcpy(dest: &encl_buffer[0], src: &op2->value, n: 8); |
97 | } |
98 | |
99 | static void do_encl_op_get_from_buf(void *op) |
100 | { |
101 | struct encl_op_get_from_buf *op2 = op; |
102 | |
103 | memcpy(dest: &op2->value, src: &encl_buffer[0], n: 8); |
104 | } |
105 | |
106 | static void do_encl_op_put_to_addr(void *_op) |
107 | { |
108 | struct encl_op_put_to_addr *op = _op; |
109 | |
110 | memcpy(dest: (void *)op->addr, src: &op->value, n: 8); |
111 | } |
112 | |
113 | static void do_encl_op_get_from_addr(void *_op) |
114 | { |
115 | struct encl_op_get_from_addr *op = _op; |
116 | |
117 | memcpy(dest: &op->value, src: (void *)op->addr, n: 8); |
118 | } |
119 | |
120 | static void do_encl_op_nop(void *_op) |
121 | { |
122 | |
123 | } |
124 | |
125 | /* |
126 | * Symbol placed at the start of the enclave image by the linker script. |
127 | * Declare this extern symbol with visibility "hidden" to ensure the compiler |
128 | * does not access it through the GOT and generates position-independent |
129 | * addressing as __encl_base(%rip), so we can get the actual enclave base |
130 | * during runtime. |
131 | */ |
132 | extern const uint8_t __attribute__((visibility("hidden" ))) __encl_base; |
133 | |
134 | typedef void (*encl_op_t)(void *); |
135 | static const encl_op_t encl_op_array[ENCL_OP_MAX] = { |
136 | do_encl_op_put_to_buf, |
137 | do_encl_op_get_from_buf, |
138 | do_encl_op_put_to_addr, |
139 | do_encl_op_get_from_addr, |
140 | do_encl_op_nop, |
141 | do_encl_eaccept, |
142 | do_encl_emodpe, |
143 | do_encl_init_tcs_page, |
144 | }; |
145 | |
146 | void encl_body(void *rdi, void *rsi) |
147 | { |
148 | struct encl_op_header * = (struct encl_op_header *)rdi; |
149 | encl_op_t op; |
150 | |
151 | if (header->type >= ENCL_OP_MAX) |
152 | return; |
153 | |
154 | /* |
155 | * The enclave base address needs to be added, as this call site |
156 | * *cannot be* made rip-relative by the compiler, or fixed up by |
157 | * any other possible means. |
158 | */ |
159 | op = ((uint64_t)&__encl_base) + encl_op_array[header->type]; |
160 | |
161 | (*op)(header); |
162 | } |
163 | |