]> git.saurik.com Git - apple/xnu.git/blame - osfmk/arm64/hibernate_restore.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / arm64 / hibernate_restore.c
CommitLineData
f427ee49
A
1/*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*!
29 * ARM64-specific functions required to support hibernation exit.
30 */
31
32#include <mach/mach_types.h>
33#include <kern/misc_protos.h>
34#include <IOKit/IOHibernatePrivate.h>
35#include <machine/pal_hibernate.h>
36#include <pexpert/arm/dockchannel.h>
37#include <ptrauth.h>
38#include <arm/cpu_data_internal.h>
39#include <arm/cpu_internal.h>
2a1bd2d3 40#include <libkern/section_keywords.h>
f427ee49 41
f427ee49
A
42
43pal_hib_tramp_result_t gHibTramp;
2a1bd2d3
A
44pal_hib_globals_t gHibernateGlobals MARK_AS_HIBERNATE_DATA_CONST_LATE;
45
46// as a workaround for <rdar://problem/70121432> References between different compile units in xnu shouldn't go through GOT
47// all of the extern symbols that we refer to in this file have to be declared with hidden visibility
48extern IOHibernateImageHeader *gIOHibernateCurrentHeader __attribute__((visibility("hidden")));
49extern const uint32_t ccsha256_initial_state[8] __attribute__((visibility("hidden")));
50extern void AccelerateCrypto_SHA256_compress(ccdigest_state_t state, size_t numBlocks, const void *data) __attribute__((visibility("hidden")));
51extern void ccdigest_final_64be(const struct ccdigest_info *di, ccdigest_ctx_t, unsigned char *digest) __attribute__((visibility("hidden")));
52extern struct pmap_cpu_data_array_entry pmap_cpu_data_array[MAX_CPUS] __attribute__((visibility("hidden")));
53extern bool hib_entry_pmap_lockdown __attribute__((visibility("hidden")));
f427ee49
A
54
55uintptr_t
56hibernate_restore_phys_page(uint64_t src, uint64_t dst, uint32_t len, __unused uint32_t procFlags)
57{
58 void *d = (void*)pal_hib_map(DEST_COPY_AREA, dst);
59 __nosan_memcpy(d, (void*)src, len);
60 return (uintptr_t)d;
61}
62
63uintptr_t
64pal_hib_map(pal_hib_map_type_t virt, uint64_t phys)
65{
66 switch (virt) {
67 case DEST_COPY_AREA:
68 case COPY_PAGE_AREA:
69 case SCRATCH_AREA:
70 case WKDM_AREA:
71 return phys + gHibTramp.memSlide;
72 case BITMAP_AREA:
73 case IMAGE_AREA:
74 case IMAGE2_AREA:
75 return phys;
76 default:
77 HIB_ASSERT(0);
78 }
79}
80
81void
82pal_hib_restore_pal_state(__unused uint32_t *arg)
83{
84}
85
86void
87pal_hib_resume_init(pal_hib_ctx_t *ctx, hibernate_page_list_t *map, uint32_t *nextFree)
88{
f427ee49
A
89}
90
91void
92pal_hib_restored_page(pal_hib_ctx_t *ctx, pal_hib_restore_stage_t stage, ppnum_t ppnum)
93{
f427ee49
A
94}
95
96void
97pal_hib_patchup(pal_hib_ctx_t *ctx)
98{
f427ee49 99
c3c9b80d
A
100 /* Reinit the ppl hib lock as it was saved to the hibernation image held. */
101 ppl_hib_lock_reinit();
102
f427ee49 103 // DRAM pages are captured from a PPL context, so here we restore all cpu_data structures to a non-PPL context
f427ee49
A
104 for (int i = 0; i < MAX_CPUS; i++) {
105 pmap_cpu_data_array[i].cpu_data.ppl_state = PPL_STATE_KERNEL;
106 pmap_cpu_data_array[i].cpu_data.ppl_kern_saved_sp = 0;
107 }
108
109 // cluster CTRR state needs to be reconfigured
110 init_ctrr_cluster_states();
111
112 // Calls into the pmap that could potentially modify pmap data structures
113 // during image copying were explicitly blocked on hibernation entry.
114 // Resetting this variable to false allows those calls to be made again.
f427ee49
A
115 hib_entry_pmap_lockdown = false;
116}
117
118void
119pal_hib_decompress_page(void *src, void *dst, void *scratch, unsigned int compressedSize)
120{
121 const void *wkdmSrc;
122 if (((uint64_t)src) & 63) {
123 // the wkdm instruction requires that our source buffer be aligned, so copy into an aligned buffer if necessary
124 __nosan_memcpy(scratch, src, compressedSize);
125 wkdmSrc = scratch;
126 } else {
127 wkdmSrc = src;
128 }
129 HIB_ASSERT((((uint64_t)wkdmSrc) & 63) == 0);
130 HIB_ASSERT((((uint64_t)dst) & PAGE_MASK) == 0);
131 struct {
132 uint32_t reserved:12;
133 uint32_t status:3;
134 uint32_t reserved2:17;
135 uint32_t popcnt:18;
136 uint32_t reserved3:14;
137 } result = { .status = ~0u };
138 __asm__ volatile ("wkdmd %0, %1" : "=r"(result): "r"(dst), "0"(wkdmSrc));
139 HIB_ASSERT(result.status == 0);
140}
141
142// proc_reg's ARM_TTE_TABLE_NS has both NSTABLE and NS set
143#define ARM_LPAE_NSTABLE 0x8000000000000000ULL
144
145#define TOP_LEVEL 1
146#define LAST_TABLE_LEVEL 3
147#define PAGE_GRANULE_SHIFT 14
148#define PAGE_GRANULE_SIZE ((size_t)1<<PAGE_GRANULE_SHIFT)
149#define PAGE_GRANULE_MASK (PAGE_GRANULE_SIZE-1)
150#define LEVEL_SHIFT(level) (47 - (level * 11))
151
152#define PTE_EMPTY(ent) ((ent) == 0)
153
154typedef struct {
155 hibernate_page_list_t *bitmap;
156 uint32_t nextFree;
157 uint64_t page_table_base;
158} map_ctx;
159
160static void
161hib_bzero(volatile void *s, size_t n)
162{
163 // can't use __nosan_bzero while the MMU is off, so do it manually
164 while (n > sizeof(uint64_t)) {
165 *(volatile uint64_t *)s = 0;
166 s += sizeof(uint64_t);
167 n -= sizeof(uint64_t);
168 }
169 while (n > sizeof(uint32_t)) {
170 *(volatile uint32_t *)s = 0;
171 s += sizeof(uint32_t);
172 n -= sizeof(uint32_t);
173 }
174 while (n) {
175 *(volatile char *)s = 0;
176 s++;
177 n--;
178 }
179}
180
181static uint64_t
182allocate_page(map_ctx *ctx)
183{
184 // pages that were unnecessary for preservation when we entered hibernation are
185 // marked as free in ctx->bitmap, so they are available for scratch usage during
186 // resume; here, we "borrow" one of these free pages to use as part of our temporary
187 // page tables
188 ppnum_t ppnum = hibernate_page_list_grab(ctx->bitmap, &ctx->nextFree);
189 hibernate_page_bitset(ctx->bitmap, FALSE, ppnum);
190 uint64_t result = ptoa_64(ppnum);
191 hib_bzero((void *)result, PAGE_SIZE);
192 return result;
193}
194
195static void
196create_map_entries(map_ctx *ctx, uint64_t vaddr, uint64_t paddr, uint64_t size, uint64_t map_flags)
197{
198 // if we've set gHibTramp.memSlide, we should already be running with the MMU on;
199 // in this case, we don't permit further modification to the page table
200 HIB_ASSERT(!gHibTramp.memSlide);
201
202 int level = TOP_LEVEL;
203 volatile uint64_t *table_base = (uint64_t *)ctx->page_table_base;
204 if (map_flags == 0) {
205 paddr = 0; // no physical address for none mappings
206 }
207
208 while (size) {
209 HIB_ASSERT(level >= 1);
210 HIB_ASSERT(level <= LAST_TABLE_LEVEL);
211
212 size_t level_shift = LEVEL_SHIFT(level);
213 size_t level_entries = PAGE_GRANULE_SIZE / sizeof(uint64_t);
214 size_t level_size = 1ull << level_shift;
215 size_t level_mask = level_size - 1;
216 size_t index = (vaddr >> level_shift) & (level_entries - 1);
217 // Can we make block entries here? Must be permitted at this
218 // level, have enough bytes remaining, and both virtual and
219 // physical addresses aligned to a block.
220 if ((level >= 2) &&
221 size >= level_size &&
222 ((vaddr | paddr) & level_mask) == 0) {
223 // Map contiguous blocks.
224 size_t num_entries = MIN(size / level_size, level_entries - index);
225 if (map_flags) {
226 uint64_t entry = map_flags | ((level < LAST_TABLE_LEVEL) ? ARM_TTE_TYPE_BLOCK : ARM_TTE_TYPE_L3BLOCK);
227 for (size_t i = 0; i < num_entries; i++) {
228 HIB_ASSERT(PTE_EMPTY(table_base[index + i]));
229 table_base[index + i] = entry | paddr;
230 paddr += level_size;
231 }
232 } else {
233 // make sure all the corresponding entries are empty
234 for (size_t i = 0; i < num_entries; i++) {
235 HIB_ASSERT(PTE_EMPTY(table_base[index + i]));
236 }
237 }
238 size_t mapped = num_entries * level_size;
239 size -= mapped;
240 if (size) {
241 // map the remaining at the top level
242 level = TOP_LEVEL;
243 table_base = (uint64_t *)ctx->page_table_base;
244 vaddr += mapped;
245 // paddr already incremented above if necessary
246 }
247 } else {
248 // Sub-divide into a next level table.
249 HIB_ASSERT(level < LAST_TABLE_LEVEL);
250 uint64_t entry = table_base[index];
251 HIB_ASSERT((entry & (ARM_TTE_VALID | ARM_TTE_TYPE_MASK)) != (ARM_TTE_VALID | ARM_TTE_TYPE_BLOCK)); // Breaking down blocks not implemented
252 uint64_t sub_base = entry & ARM_TTE_TABLE_MASK;
253 if (!sub_base) {
254 sub_base = allocate_page(ctx);
255 HIB_ASSERT((sub_base & PAGE_GRANULE_MASK) == 0);
256 table_base[index] = sub_base | ARM_LPAE_NSTABLE | ARM_TTE_TYPE_TABLE | ARM_TTE_VALID;
257 }
258 // map into the sub table
259 level++;
260 table_base = (uint64_t *)sub_base;
261 }
262 }
263}
264
265static void
266map_range_start_end(map_ctx *ctx, uint64_t start, uint64_t end, uint64_t slide, uint64_t flags)
267{
268 HIB_ASSERT(end >= start);
269 create_map_entries(ctx, start + slide, start, end - start, flags);
270}
271
272#define MAP_FLAGS_COMMON (ARM_PTE_AF | ARM_PTE_NS | ARM_TTE_VALID | ARM_PTE_SH(SH_OUTER_MEMORY) | ARM_PTE_ATTRINDX(CACHE_ATTRINDX_WRITEBACK))
273#define MAP_DEVICE (ARM_PTE_AF | ARM_TTE_VALID | ARM_PTE_PNX | ARM_PTE_NX | ARM_PTE_SH(SH_NONE) | ARM_PTE_ATTRINDX(CACHE_ATTRINDX_DISABLE))
274#define MAP_RO (MAP_FLAGS_COMMON | ARM_PTE_PNX | ARM_PTE_NX | ARM_PTE_AP(AP_RONA))
275#define MAP_RW (MAP_FLAGS_COMMON | ARM_PTE_PNX | ARM_PTE_NX)
276#define MAP_RX (MAP_FLAGS_COMMON | ARM_PTE_AP(AP_RONA))
277
278static void
279map_register_page(map_ctx *ctx, vm_address_t regPage)
280{
281 uint64_t regBase = trunc_page(regPage);
282 if (regBase) {
283 map_range_start_end(ctx, regBase, regBase + PAGE_SIZE, 0, MAP_DEVICE);
284 }
285}
286
287static void
288iterate_bitmaps(const map_ctx *ctx, bool (^callback)(const hibernate_bitmap_t *bank_bitmap))
289{
290 hibernate_bitmap_t *bank_bitmap = &ctx->bitmap->bank_bitmap[0];
291 for (uint32_t bank = 0; bank < ctx->bitmap->bank_count; bank++) {
292 if (!callback(bank_bitmap)) {
293 return;
294 }
295 bank_bitmap = (hibernate_bitmap_t*)&bank_bitmap->bitmap[bank_bitmap->bitmapwords];
296 }
297}
298
299// during hibernation resume, we can't use the original kernel page table (because we don't know what it was), so we instead
300// create a temporary page table to use during hibernation resume; since the original kernel page table was part of DRAM,
301// it will be restored by the time we're done with hibernation resume, at which point we can jump through the reset vector
302// to reload the original page table
303void
304pal_hib_resume_tramp(uint32_t headerPpnum)
305{
306 uint64_t header_phys = ptoa_64(headerPpnum);
307 IOHibernateImageHeader *header = (IOHibernateImageHeader *)header_phys;
308 IOHibernateHibSegInfo *seg_info = &header->hibSegInfo;
309 uint64_t hib_text_start = ptoa_64(header->restore1CodePhysPage);
310
311 __block map_ctx ctx = {};
312 uint64_t map_phys = header_phys
313 + (offsetof(IOHibernateImageHeader, fileExtentMap)
314 + header->fileExtentMapSize
315 + ptoa_32(header->restore1PageCount)
316 + header->previewSize);
317 ctx.bitmap = (hibernate_page_list_t *)map_phys;
318
319 // find the bank describing xnu's map
320 __block uint64_t phys_start = 0, phys_end = 0;
321 iterate_bitmaps(&ctx, ^bool (const hibernate_bitmap_t *bank_bitmap) {
322 if ((bank_bitmap->first_page <= header->restore1CodePhysPage) &&
323 (bank_bitmap->last_page >= header->restore1CodePhysPage)) {
324 phys_start = ptoa_64(bank_bitmap->first_page);
325 phys_end = ptoa_64(bank_bitmap->last_page) + PAGE_SIZE;
326 return false;
327 }
328 return true;
329 });
330
331 HIB_ASSERT(phys_start != 0);
332 HIB_ASSERT(phys_end != 0);
333
334 hib_bzero(&gHibTramp, sizeof(gHibTramp));
f427ee49
A
335
336 // During hibernation resume, we create temporary mappings that do not collide with where any of the kernel mappings were originally.
337 // Technically, non-collision isn't a requirement, but doing this means that if some code accidentally jumps to a VA in the original
338 // kernel map, it won't be present in our temporary map and we'll get an exception when jumping to an unmapped address.
339 // The base address of our temporary mappings is adjusted by a random amount as a "poor-man's ASLR". We don’t have a good source of random
340 // numbers in this context, so we just use some of the bits from one of imageHeaderHMMAC, which should be random enough.
341 uint16_t rand = (uint16_t)(((header->imageHeaderHMAC[0]) << 8) | header->imageHeaderHMAC[1]);
2a1bd2d3 342 uint64_t mem_slide = gHibernateGlobals.kernelSlide - (phys_end - phys_start) * 4 - rand * 256 * PAGE_SIZE;
f427ee49
A
343
344 // make sure we don't clobber any of the pages we need for restore
345 hibernate_reserve_restore_pages(header_phys, header, ctx.bitmap);
346
347 // init nextFree
348 hibernate_page_list_grab(ctx.bitmap, &ctx.nextFree);
349
350 // map ttbr1 pages
351 ctx.page_table_base = allocate_page(&ctx);
352 gHibTramp.ttbr1 = ctx.page_table_base;
353
354 uint64_t first_seg_start = 0, last_seg_end = 0, hib_text_end = 0;
355 for (size_t i = 0; i < NUM_HIBSEGINFO_SEGMENTS; i++) {
356 uint64_t size = ptoa_64(seg_info->segments[i].pageCount);
357 if (size) {
358 uint64_t seg_start = ptoa_64(seg_info->segments[i].physPage);
359 uint64_t seg_end = seg_start + size;
360 uint32_t protection = seg_info->segments[i].protection;
361 if (protection != VM_PROT_NONE) {
362 // make sure the segment is in bounds
363 HIB_ASSERT(seg_start >= phys_start);
364 HIB_ASSERT(seg_end <= phys_end);
365
366 if (!first_seg_start) {
367 first_seg_start = seg_start;
368 }
369 if (last_seg_end) {
370 // map the "hole" as RW
371 map_range_start_end(&ctx, last_seg_end, seg_start, mem_slide, MAP_RW);
372 }
373 // map the segments described in machine_header at their original locations
374 bool executable = (protection & VM_PROT_EXECUTE);
375 bool writeable = (protection & VM_PROT_WRITE);
376 uint64_t map_flags = executable ? MAP_RX : writeable ? MAP_RW : MAP_RO;
2a1bd2d3 377 map_range_start_end(&ctx, seg_start, seg_end, gHibernateGlobals.kernelSlide, map_flags);
f427ee49
A
378 last_seg_end = seg_end;
379 }
380 if (seg_info->segments[i].physPage == header->restore1CodePhysPage) {
381 // this is the hibtext segment, so remember where it ends
382 hib_text_end = seg_end;
383 }
384 }
385 }
386 // map the rest of kernel memory (the pages that come before and after our segments) as RW
387 map_range_start_end(&ctx, phys_start, first_seg_start, mem_slide, MAP_RW);
388 map_range_start_end(&ctx, last_seg_end, phys_end, mem_slide, MAP_RW);
389
390 // map all of the remaining banks that we didn't already deal with
391 iterate_bitmaps(&ctx, ^bool (const hibernate_bitmap_t *bank_bitmap) {
392 uint64_t bank_start = ptoa_64(bank_bitmap->first_page);
393 uint64_t bank_end = ptoa_64(bank_bitmap->last_page) + PAGE_SIZE;
394 if (bank_start == phys_start) {
395 // skip this bank since we already covered it above
396 } else {
397 // map the bank RW
398 map_range_start_end(&ctx, bank_start, bank_end, mem_slide, MAP_RW);
399 }
400 return true;
401 });
402
403 // map ttbr0 pages
404 ctx.page_table_base = allocate_page(&ctx);
405 gHibTramp.ttbr0 = ctx.page_table_base;
406
407 // map hib text P=V so that we can still execute at its physical address
408 map_range_start_end(&ctx, hib_text_start, hib_text_end, 0, MAP_RX);
409
410 // map the hib image P=V, RW
411 uint64_t image_start = trunc_page(header_phys);
412 uint64_t image_end = round_page(header_phys + header->image1Size);
413 map_range_start_end(&ctx, image_start, image_end, 0, MAP_RW);
414
415 // map the handoff pages P=V, RO
416 image_start = ptoa_64(header->handoffPages);
417 image_end = image_start + ptoa_64(header->handoffPageCount);
418 map_range_start_end(&ctx, image_start, image_end, 0, MAP_RO);
419
420 // map some device register pages
421 if (gHibernateGlobals.dockChannelRegBase) {
422#define dockchannel_uart_base gHibernateGlobals.dockChannelRegBase
423 vm_address_t dockChannelRegBase = trunc_page(&rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL));
424 map_register_page(&ctx, dockChannelRegBase);
425 }
426 map_register_page(&ctx, gHibernateGlobals.hibUartRegBase);
427 map_register_page(&ctx, gHibernateGlobals.hmacRegBase);
428
429 gHibTramp.memSlide = mem_slide;
430}