diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c | 254 |
1 files changed, 90 insertions, 164 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c index 314be2192b7d..27c9dfffb9a6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c | |||
@@ -19,184 +19,108 @@ | |||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | * DEALINGS IN THE SOFTWARE. | 20 | * DEALINGS IN THE SOFTWARE. |
21 | */ | 21 | */ |
22 | |||
23 | /* | ||
24 | * Secure boot is the process by which NVIDIA-signed firmware is loaded into | ||
25 | * some of the falcons of a GPU. For production devices this is the only way | ||
26 | * for the firmware to access useful (but sensitive) registers. | ||
27 | * | ||
28 | * A Falcon microprocessor supporting advanced security modes can run in one of | ||
29 | * three modes: | ||
30 | * | ||
31 | * - Non-secure (NS). In this mode, functionality is similar to Falcon | ||
32 | * architectures before security modes were introduced (pre-Maxwell), but | ||
33 | * capability is restricted. In particular, certain registers may be | ||
34 | * inaccessible for reads and/or writes, and physical memory access may be | ||
35 | * disabled (on certain Falcon instances). This is the only possible mode that | ||
36 | * can be used if you don't have microcode cryptographically signed by NVIDIA. | ||
37 | * | ||
38 | * - Heavy Secure (HS). In this mode, the microprocessor is a black box - it's | ||
39 | * not possible to read or write any Falcon internal state or Falcon registers | ||
40 | * from outside the Falcon (for example, from the host system). The only way | ||
41 | * to enable this mode is by loading microcode that has been signed by NVIDIA. | ||
42 | * (The loading process involves tagging the IMEM block as secure, writing the | ||
43 | * signature into a Falcon register, and starting execution. The hardware will | ||
44 | * validate the signature, and if valid, grant HS privileges.) | ||
45 | * | ||
46 | * - Light Secure (LS). In this mode, the microprocessor has more privileges | ||
47 | * than NS but fewer than HS. Some of the microprocessor state is visible to | ||
48 | * host software to ease debugging. The only way to enable this mode is by HS | ||
49 | * microcode enabling LS mode. Some privileges available to HS mode are not | ||
50 | * available here. LS mode is introduced in GM20x. | ||
51 | * | ||
52 | * Secure boot consists in temporarily switching a HS-capable falcon (typically | ||
53 | * PMU) into HS mode in order to validate the LS firmwares of managed falcons, | ||
54 | * load them, and switch managed falcons into LS mode. Once secure boot | ||
55 | * completes, no falcon remains in HS mode. | ||
56 | * | ||
57 | * Secure boot requires a write-protected memory region (WPR) which can only be | ||
58 | * written by the secure falcon. On dGPU, the driver sets up the WPR region in | ||
59 | * video memory. On Tegra, it is set up by the bootloader and its location and | ||
60 | * size written into memory controller registers. | ||
61 | * | ||
62 | * The secure boot process takes place as follows: | ||
63 | * | ||
64 | * 1) A LS blob is constructed that contains all the LS firmwares we want to | ||
65 | * load, along with their signatures and bootloaders. | ||
66 | * | ||
67 | * 2) A HS blob (also called ACR) is created that contains the signed HS | ||
68 | * firmware in charge of loading the LS firmwares into their respective | ||
69 | * falcons. | ||
70 | * | ||
71 | * 3) The HS blob is loaded (via its own bootloader) and executed on the | ||
72 | * HS-capable falcon. It authenticates itself, switches the secure falcon to | ||
73 | * HS mode and setup the WPR region around the LS blob (dGPU) or copies the | ||
74 | * LS blob into the WPR region (Tegra). | ||
75 | * | ||
76 | * 4) The LS blob is now secure from all external tampering. The HS falcon | ||
77 | * checks the signatures of the LS firmwares and, if valid, switches the | ||
78 | * managed falcons to LS mode and makes them ready to run the LS firmware. | ||
79 | * | ||
80 | * 5) The managed falcons remain in LS mode and can be started. | ||
81 | * | ||
82 | */ | ||
83 | |||
22 | #include "priv.h" | 84 | #include "priv.h" |
85 | #include "acr.h" | ||
23 | 86 | ||
24 | #include <subdev/mc.h> | 87 | #include <subdev/mc.h> |
25 | #include <subdev/timer.h> | 88 | #include <subdev/timer.h> |
89 | #include <subdev/pmu.h> | ||
26 | 90 | ||
27 | static const char * | 91 | const char * |
28 | managed_falcons_names[] = { | 92 | nvkm_secboot_falcon_name[] = { |
29 | [NVKM_SECBOOT_FALCON_PMU] = "PMU", | 93 | [NVKM_SECBOOT_FALCON_PMU] = "PMU", |
30 | [NVKM_SECBOOT_FALCON_RESERVED] = "<reserved>", | 94 | [NVKM_SECBOOT_FALCON_RESERVED] = "<reserved>", |
31 | [NVKM_SECBOOT_FALCON_FECS] = "FECS", | 95 | [NVKM_SECBOOT_FALCON_FECS] = "FECS", |
32 | [NVKM_SECBOOT_FALCON_GPCCS] = "GPCCS", | 96 | [NVKM_SECBOOT_FALCON_GPCCS] = "GPCCS", |
33 | [NVKM_SECBOOT_FALCON_END] = "<invalid>", | 97 | [NVKM_SECBOOT_FALCON_END] = "<invalid>", |
34 | }; | 98 | }; |
35 | |||
36 | /* | ||
37 | * Helper falcon functions | ||
38 | */ | ||
39 | |||
40 | static int | ||
41 | falcon_clear_halt_interrupt(struct nvkm_device *device, u32 base) | ||
42 | { | ||
43 | int ret; | ||
44 | |||
45 | /* clear halt interrupt */ | ||
46 | nvkm_mask(device, base + 0x004, 0x10, 0x10); | ||
47 | /* wait until halt interrupt is cleared */ | ||
48 | ret = nvkm_wait_msec(device, 10, base + 0x008, 0x10, 0x0); | ||
49 | if (ret < 0) | ||
50 | return ret; | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int | ||
56 | falcon_wait_idle(struct nvkm_device *device, u32 base) | ||
57 | { | ||
58 | int ret; | ||
59 | |||
60 | ret = nvkm_wait_msec(device, 10, base + 0x04c, 0xffff, 0x0); | ||
61 | if (ret < 0) | ||
62 | return ret; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static int | ||
68 | nvkm_secboot_falcon_enable(struct nvkm_secboot *sb) | ||
69 | { | ||
70 | struct nvkm_device *device = sb->subdev.device; | ||
71 | int ret; | ||
72 | |||
73 | /* enable engine */ | ||
74 | nvkm_mc_enable(device, sb->devidx); | ||
75 | ret = nvkm_wait_msec(device, 10, sb->base + 0x10c, 0x6, 0x0); | ||
76 | if (ret < 0) { | ||
77 | nvkm_error(&sb->subdev, "Falcon mem scrubbing timeout\n"); | ||
78 | nvkm_mc_disable(device, sb->devidx); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | ret = falcon_wait_idle(device, sb->base); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | |||
86 | /* enable IRQs */ | ||
87 | nvkm_wr32(device, sb->base + 0x010, 0xff); | ||
88 | nvkm_mc_intr_mask(device, sb->devidx, true); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int | ||
94 | nvkm_secboot_falcon_disable(struct nvkm_secboot *sb) | ||
95 | { | ||
96 | struct nvkm_device *device = sb->subdev.device; | ||
97 | |||
98 | /* disable IRQs and wait for any previous code to complete */ | ||
99 | nvkm_mc_intr_mask(device, sb->devidx, false); | ||
100 | nvkm_wr32(device, sb->base + 0x014, 0xff); | ||
101 | |||
102 | falcon_wait_idle(device, sb->base); | ||
103 | |||
104 | /* disable engine */ | ||
105 | nvkm_mc_disable(device, sb->devidx); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int | ||
111 | nvkm_secboot_falcon_reset(struct nvkm_secboot *sb) | ||
112 | { | ||
113 | int ret; | ||
114 | |||
115 | ret = nvkm_secboot_falcon_disable(sb); | ||
116 | if (ret) | ||
117 | return ret; | ||
118 | |||
119 | ret = nvkm_secboot_falcon_enable(sb); | ||
120 | if (ret) | ||
121 | return ret; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * nvkm_secboot_falcon_run - run the falcon that will perform secure boot | ||
128 | * | ||
129 | * This function is to be called after all chip-specific preparations have | ||
130 | * been completed. It will start the falcon to perform secure boot, wait for | ||
131 | * it to halt, and report if an error occurred. | ||
132 | */ | ||
133 | int | ||
134 | nvkm_secboot_falcon_run(struct nvkm_secboot *sb) | ||
135 | { | ||
136 | struct nvkm_device *device = sb->subdev.device; | ||
137 | int ret; | ||
138 | |||
139 | /* Start falcon */ | ||
140 | nvkm_wr32(device, sb->base + 0x100, 0x2); | ||
141 | |||
142 | /* Wait for falcon halt */ | ||
143 | ret = nvkm_wait_msec(device, 100, sb->base + 0x100, 0x10, 0x10); | ||
144 | if (ret < 0) | ||
145 | return ret; | ||
146 | |||
147 | /* If mailbox register contains an error code, then ACR has failed */ | ||
148 | ret = nvkm_rd32(device, sb->base + 0x040); | ||
149 | if (ret) { | ||
150 | nvkm_error(&sb->subdev, "ACR boot failed, ret 0x%08x", ret); | ||
151 | falcon_clear_halt_interrupt(device, sb->base); | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | |||
159 | /** | 99 | /** |
160 | * nvkm_secboot_reset() - reset specified falcon | 100 | * nvkm_secboot_reset() - reset specified falcon |
161 | */ | 101 | */ |
162 | int | 102 | int |
163 | nvkm_secboot_reset(struct nvkm_secboot *sb, u32 falcon) | 103 | nvkm_secboot_reset(struct nvkm_secboot *sb, enum nvkm_secboot_falcon falcon) |
164 | { | 104 | { |
165 | /* Unmanaged falcon? */ | 105 | /* Unmanaged falcon? */ |
166 | if (!(BIT(falcon) & sb->func->managed_falcons)) { | 106 | if (!(BIT(falcon) & sb->acr->managed_falcons)) { |
167 | nvkm_error(&sb->subdev, "cannot reset unmanaged falcon!\n"); | 107 | nvkm_error(&sb->subdev, "cannot reset unmanaged falcon!\n"); |
168 | return -EINVAL; | 108 | return -EINVAL; |
169 | } | 109 | } |
170 | 110 | ||
171 | return sb->func->reset(sb, falcon); | 111 | return sb->acr->func->reset(sb->acr, sb, falcon); |
172 | } | ||
173 | |||
174 | /** | ||
175 | * nvkm_secboot_start() - start specified falcon | ||
176 | */ | ||
177 | int | ||
178 | nvkm_secboot_start(struct nvkm_secboot *sb, u32 falcon) | ||
179 | { | ||
180 | /* Unmanaged falcon? */ | ||
181 | if (!(BIT(falcon) & sb->func->managed_falcons)) { | ||
182 | nvkm_error(&sb->subdev, "cannot start unmanaged falcon!\n"); | ||
183 | return -EINVAL; | ||
184 | } | ||
185 | |||
186 | return sb->func->start(sb, falcon); | ||
187 | } | 112 | } |
188 | 113 | ||
189 | /** | 114 | /** |
190 | * nvkm_secboot_is_managed() - check whether a given falcon is securely-managed | 115 | * nvkm_secboot_is_managed() - check whether a given falcon is securely-managed |
191 | */ | 116 | */ |
192 | bool | 117 | bool |
193 | nvkm_secboot_is_managed(struct nvkm_secboot *secboot, | 118 | nvkm_secboot_is_managed(struct nvkm_secboot *sb, enum nvkm_secboot_falcon fid) |
194 | enum nvkm_secboot_falcon fid) | ||
195 | { | 119 | { |
196 | if (!secboot) | 120 | if (!sb) |
197 | return false; | 121 | return false; |
198 | 122 | ||
199 | return secboot->func->managed_falcons & BIT(fid); | 123 | return sb->acr->managed_falcons & BIT(fid); |
200 | } | 124 | } |
201 | 125 | ||
202 | static int | 126 | static int |
@@ -205,9 +129,19 @@ nvkm_secboot_oneinit(struct nvkm_subdev *subdev) | |||
205 | struct nvkm_secboot *sb = nvkm_secboot(subdev); | 129 | struct nvkm_secboot *sb = nvkm_secboot(subdev); |
206 | int ret = 0; | 130 | int ret = 0; |
207 | 131 | ||
132 | switch (sb->acr->boot_falcon) { | ||
133 | case NVKM_SECBOOT_FALCON_PMU: | ||
134 | sb->boot_falcon = subdev->device->pmu->falcon; | ||
135 | break; | ||
136 | default: | ||
137 | nvkm_error(subdev, "Unmanaged boot falcon %s!\n", | ||
138 | nvkm_secboot_falcon_name[sb->acr->boot_falcon]); | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
208 | /* Call chip-specific init function */ | 142 | /* Call chip-specific init function */ |
209 | if (sb->func->init) | 143 | if (sb->func->oneinit) |
210 | ret = sb->func->init(sb); | 144 | ret = sb->func->oneinit(sb); |
211 | if (ret) { | 145 | if (ret) { |
212 | nvkm_error(subdev, "Secure Boot initialization failed: %d\n", | 146 | nvkm_error(subdev, "Secure Boot initialization failed: %d\n", |
213 | ret); | 147 | ret); |
@@ -249,7 +183,7 @@ nvkm_secboot = { | |||
249 | }; | 183 | }; |
250 | 184 | ||
251 | int | 185 | int |
252 | nvkm_secboot_ctor(const struct nvkm_secboot_func *func, | 186 | nvkm_secboot_ctor(const struct nvkm_secboot_func *func, struct nvkm_acr *acr, |
253 | struct nvkm_device *device, int index, | 187 | struct nvkm_device *device, int index, |
254 | struct nvkm_secboot *sb) | 188 | struct nvkm_secboot *sb) |
255 | { | 189 | { |
@@ -257,22 +191,14 @@ nvkm_secboot_ctor(const struct nvkm_secboot_func *func, | |||
257 | 191 | ||
258 | nvkm_subdev_ctor(&nvkm_secboot, device, index, &sb->subdev); | 192 | nvkm_subdev_ctor(&nvkm_secboot, device, index, &sb->subdev); |
259 | sb->func = func; | 193 | sb->func = func; |
260 | 194 | sb->acr = acr; | |
261 | /* setup the performing falcon's base address and masks */ | 195 | acr->subdev = &sb->subdev; |
262 | switch (func->boot_falcon) { | ||
263 | case NVKM_SECBOOT_FALCON_PMU: | ||
264 | sb->devidx = NVKM_SUBDEV_PMU; | ||
265 | sb->base = 0x10a000; | ||
266 | break; | ||
267 | default: | ||
268 | nvkm_error(&sb->subdev, "invalid secure boot falcon\n"); | ||
269 | return -EINVAL; | ||
270 | }; | ||
271 | 196 | ||
272 | nvkm_debug(&sb->subdev, "securely managed falcons:\n"); | 197 | nvkm_debug(&sb->subdev, "securely managed falcons:\n"); |
273 | for_each_set_bit(fid, &sb->func->managed_falcons, | 198 | for_each_set_bit(fid, &sb->acr->managed_falcons, |
274 | NVKM_SECBOOT_FALCON_END) | 199 | NVKM_SECBOOT_FALCON_END) |
275 | nvkm_debug(&sb->subdev, "- %s\n", managed_falcons_names[fid]); | 200 | nvkm_debug(&sb->subdev, "- %s\n", |
201 | nvkm_secboot_falcon_name[fid]); | ||
276 | 202 | ||
277 | return 0; | 203 | return 0; |
278 | } | 204 | } |