Loading...
  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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/*
 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
#include <mach_debug.h>
#include <mach_kdp.h>
#include <debug.h>

#include <mach/vm_types.h>
#include <mach/vm_param.h>
#include <mach/thread_status.h>
#include <kern/misc_protos.h>
#include <kern/assert.h>
#include <kern/cpu_number.h>
#include <kern/thread.h>
#include <vm/vm_map.h>
#include <vm/vm_page.h>
#include <vm/pmap.h>

#include <arm/proc_reg.h>
#include <arm/caches_internal.h>
#include <arm/cpu_data_internal.h>
#include <arm/pmap.h>
#include <arm/misc_protos.h>
#include <arm/lowglobals.h>

#include <pexpert/arm/boot.h>
#include <pexpert/device_tree.h>

#include <libkern/kernel_mach_header.h>

/*
 * Denotes the end of xnu.
 */
extern void *last_kernel_symbol;

/*
 * KASLR parameters
 */
vm_offset_t vm_kernel_base;
vm_offset_t vm_kernel_top;
vm_offset_t vm_kernel_stext;
vm_offset_t vm_kernel_etext;
vm_offset_t vm_kernel_slide;
vm_offset_t vm_kernel_slid_base;
vm_offset_t vm_kernel_slid_top;
vm_offset_t vm_kext_base;
vm_offset_t vm_kext_top;
vm_offset_t vm_prelink_stext;
vm_offset_t vm_prelink_etext;
vm_offset_t vm_prelink_sinfo;
vm_offset_t vm_prelink_einfo;
vm_offset_t vm_slinkedit;
vm_offset_t vm_elinkedit;
vm_offset_t vm_prelink_sdata;
vm_offset_t vm_prelink_edata;

vm_offset_t vm_kernel_builtinkmod_text;
vm_offset_t vm_kernel_builtinkmod_text_end;

unsigned long gVirtBase, gPhysBase, gPhysSize;      /* Used by <mach/arm/vm_param.h> */

vm_offset_t   mem_size;                             /* Size of actual physical memory present
                                                     * minus any performance buffer and possibly
                                                     * limited by mem_limit in bytes */
uint64_t      mem_actual;                           /* The "One True" physical memory size
                                                     * actually, it's the highest physical
                                                     * address + 1 */
uint64_t      max_mem;                              /* Size of physical memory (bytes), adjusted
                                                     * by maxmem */
uint64_t      sane_size;                            /* Memory size to use for defaults
                                                     * calculations */
addr64_t      vm_last_addr = VM_MAX_KERNEL_ADDRESS; /* Highest kernel
                                                     * virtual address known
                                                     * to the VM system */

vm_offset_t            segEXTRADATA;
unsigned long          segSizeEXTRADATA;
vm_offset_t            segLOWESTTEXT;
static vm_offset_t     segTEXTB;
static unsigned long   segSizeTEXT;
static vm_offset_t     segDATAB;
static unsigned long   segSizeDATA;
static vm_offset_t     segLINKB;
static unsigned long   segSizeLINK;
static vm_offset_t     segKLDB;
static unsigned long   segSizeKLD;
static vm_offset_t     segLASTB;
static unsigned long   segSizeLAST;
static vm_offset_t     sectCONSTB;
static unsigned long   sectSizeCONST;
vm_offset_t            segBOOTDATAB;
unsigned long          segSizeBOOTDATA;
extern vm_offset_t     intstack_low_guard;
extern vm_offset_t     intstack_high_guard;
extern vm_offset_t     fiqstack_high_guard;

vm_offset_t     segPRELINKTEXTB;
unsigned long   segSizePRELINKTEXT;
vm_offset_t     segPRELINKINFOB;
unsigned long   segSizePRELINKINFO;

static kernel_segment_command_t *segDATA;
static boolean_t doconstro = TRUE;

vm_offset_t end_kern, etext, sdata, edata;

/*
 * Bootstrap the system enough to run with virtual memory.
 * Map the kernel's code and data, and allocate the system page table.
 * Page_size must already be set.
 *
 * Parameters:
 * first_avail: first available physical page -
 *              after kernel page tables
 * avail_start: PA of first physical page
 * avail_end  : PA of last physical page
 */
vm_offset_t     first_avail;
vm_offset_t     static_memory_end;
pmap_paddr_t    avail_start, avail_end;

#define MEM_SIZE_MAX 0x40000000

extern vm_offset_t ExceptionVectorsBase; /* the code we want to load there */

/* The translation tables have to be 16KB aligned */
#define round_x_table(x) \
	(((pmap_paddr_t)(x) + (ARM_PGBYTES<<2) - 1) & ~((ARM_PGBYTES<<2) - 1))

vm_map_address_t
phystokv(pmap_paddr_t pa)
{
	return pa - gPhysBase + gVirtBase;
}

static void
arm_vm_page_granular_helper(vm_offset_t start, vm_offset_t _end, vm_offset_t va,
    int pte_prot_APX, int pte_prot_XN)
{
	if (va & ARM_TT_L1_PT_OFFMASK) { /* ragged edge hanging over a ARM_TT_L1_PT_SIZE  boundary */
		va &= (~ARM_TT_L1_PT_OFFMASK);
		tt_entry_t *tte = &cpu_tte[ttenum(va)];
		tt_entry_t tmplate = *tte;
		pmap_paddr_t pa;
		pt_entry_t *ppte, ptmp;
		unsigned int i;

		pa = va - gVirtBase + gPhysBase;

		if (pa >= avail_end) {
			return;
		}

		assert(_end >= va);

		if (ARM_TTE_TYPE_TABLE == (tmplate & ARM_TTE_TYPE_MASK)) {
			/* pick up the existing page table. */
			ppte = (pt_entry_t *)phystokv((tmplate & ARM_TTE_TABLE_MASK));
		} else {
			/* TTE must be reincarnated COARSE. */
			ppte = (pt_entry_t *)phystokv(avail_start);
			avail_start += ARM_PGBYTES;
			bzero(ppte, ARM_PGBYTES);

			for (i = 0; i < 4; ++i) {
				tte[i] = pa_to_tte(kvtophys((vm_offset_t)ppte) + (i * 0x400)) | ARM_TTE_TYPE_TABLE;
			}
		}

		vm_offset_t len = _end - va;
		if ((pa + len) > avail_end) {
			_end -= (pa + len - avail_end);
		}
		assert((start - gVirtBase + gPhysBase) >= gPhysBase);

		/* Apply the desired protections to the specified page range */
		for (i = 0; i < (ARM_PGBYTES / sizeof(*ppte)); i++) {
			if (start <= va && va < _end) {
				ptmp = pa | ARM_PTE_AF | ARM_PTE_SH | ARM_PTE_TYPE;
				ptmp = ptmp | ARM_PTE_ATTRINDX(CACHE_ATTRINDX_DEFAULT);
				ptmp = ptmp | ARM_PTE_AP(pte_prot_APX);
				if (pte_prot_XN) {
					ptmp = ptmp | ARM_PTE_NX;
				}

				ppte[i] = ptmp;
			}

			va += ARM_PGBYTES;
			pa += ARM_PGBYTES;
		}
	}
}

static void
arm_vm_page_granular_prot(vm_offset_t start, unsigned long size,
    int tte_prot_XN, int pte_prot_APX, int pte_prot_XN, int force_page_granule)
{
	vm_offset_t _end = start + size;
	vm_offset_t align_start = (start + ARM_TT_L1_PT_OFFMASK) & ~ARM_TT_L1_PT_OFFMASK;
	vm_offset_t align_end = _end & ~ARM_TT_L1_PT_OFFMASK;

	arm_vm_page_granular_helper(start, _end, start, pte_prot_APX, pte_prot_XN);

	while (align_start < align_end) {
		if (force_page_granule) {
			arm_vm_page_granular_helper(align_start, align_end, align_start + 1,
			    pte_prot_APX, pte_prot_XN);
		} else {
			tt_entry_t *tte = &cpu_tte[ttenum(align_start)];
			for (int i = 0; i < 4; ++i) {
				tt_entry_t tmplate = tte[i];

				tmplate = (tmplate & ~ARM_TTE_BLOCK_APMASK) | ARM_TTE_BLOCK_AP(pte_prot_APX);
				tmplate = (tmplate & ~ARM_TTE_BLOCK_NX_MASK);
				if (tte_prot_XN) {
					tmplate = tmplate | ARM_TTE_BLOCK_NX;
				}

				tte[i] = tmplate;
			}
		}
		align_start += ARM_TT_L1_PT_SIZE;
	}

	arm_vm_page_granular_helper(start, _end, _end, pte_prot_APX, pte_prot_XN);
}

static inline void
arm_vm_page_granular_RNX(vm_offset_t start, unsigned long size, int force_page_granule)
{
	arm_vm_page_granular_prot(start, size, 1, AP_RONA, 1, force_page_granule);
}

static inline void
arm_vm_page_granular_ROX(vm_offset_t start, unsigned long size, int force_page_granule)
{
	arm_vm_page_granular_prot(start, size, 0, AP_RONA, 0, force_page_granule);
}

static inline void
arm_vm_page_granular_RWNX(vm_offset_t start, unsigned long size, int force_page_granule)
{
	arm_vm_page_granular_prot(start, size, 1, AP_RWNA, 1, force_page_granule);
}

static inline void
arm_vm_page_granular_RWX(vm_offset_t start, unsigned long size, int force_page_granule)
{
	arm_vm_page_granular_prot(start, size, 0, AP_RWNA, 0, force_page_granule);
}

void
arm_vm_prot_init(boot_args * args)
{
#if __ARM_PTE_PHYSMAP__
	boolean_t force_coarse_physmap = TRUE;
#else
	boolean_t force_coarse_physmap = FALSE;
#endif
	/*
	 * Enforce W^X protections on segments that have been identified so far. This will be
	 * further refined for each KEXT's TEXT and DATA segments in readPrelinkedExtensions()
	 */

	/*
	 * Protection on kernel text is loose here to allow shenanigans early on (e.g. copying exception vectors)
	 * and storing an address into "error_buffer" (see arm_init.c) !?!
	 * These protections are tightened in arm_vm_prot_finalize()
	 */
	arm_vm_page_granular_RWX(gVirtBase, segSizeTEXT + (segTEXTB - gVirtBase), FALSE);

	if (doconstro) {
		/*
		 * We map __DATA with 3 calls, so that the __const section can have its
		 * protections changed independently of the rest of the __DATA segment.
		 */
		arm_vm_page_granular_RWNX(segDATAB, sectCONSTB - segDATAB, FALSE);
		arm_vm_page_granular_RNX(sectCONSTB, sectSizeCONST, FALSE);
		arm_vm_page_granular_RWNX(sectCONSTB + sectSizeCONST, (segDATAB + segSizeDATA) - (sectCONSTB + sectSizeCONST), FALSE);
	} else {
		/* If we aren't protecting const, just map DATA as a single blob. */
		arm_vm_page_granular_RWNX(segDATAB, segSizeDATA, FALSE);
	}
	arm_vm_page_granular_RWNX(segBOOTDATAB, segSizeBOOTDATA, TRUE);
	arm_vm_page_granular_RNX((vm_offset_t)&intstack_low_guard, PAGE_MAX_SIZE, TRUE);
	arm_vm_page_granular_RNX((vm_offset_t)&intstack_high_guard, PAGE_MAX_SIZE, TRUE);
	arm_vm_page_granular_RNX((vm_offset_t)&fiqstack_high_guard, PAGE_MAX_SIZE, TRUE);

	arm_vm_page_granular_ROX(segKLDB, segSizeKLD, force_coarse_physmap);
	arm_vm_page_granular_RWNX(segLINKB, segSizeLINK, force_coarse_physmap);
	arm_vm_page_granular_RWNX(segLASTB, segSizeLAST, FALSE); // __LAST may be empty, but we cannot assume this
	arm_vm_page_granular_RWNX(segPRELINKTEXTB, segSizePRELINKTEXT, TRUE); // Refined in OSKext::readPrelinkedExtensions
	arm_vm_page_granular_RWNX(segPRELINKTEXTB + segSizePRELINKTEXT,
	    end_kern - (segPRELINKTEXTB + segSizePRELINKTEXT), force_coarse_physmap);                          // PreLinkInfoDictionary
	arm_vm_page_granular_RWNX(end_kern, phystokv(args->topOfKernelData) - end_kern, force_coarse_physmap); // Device Tree, RAM Disk (if present), bootArgs, trust caches
	arm_vm_page_granular_RNX(segEXTRADATA, segSizeEXTRADATA, FALSE); // tighter trust cache protection
	arm_vm_page_granular_RWNX(phystokv(args->topOfKernelData), ARM_PGBYTES * 8, FALSE); // boot_tte, cpu_tte

	/*
	 * FIXME: Any page table pages that arm_vm_page_granular_* created with ROX entries in the range
	 * phystokv(args->topOfKernelData) to phystokv(prot_avail_start) should themselves be
	 * write protected in the static mapping of that range.
	 * [Page table pages whose page table entries grant execute (X) privileges should themselves be
	 * marked read-only. This aims to thwart attacks that replace the X entries with vectors to evil code
	 * (relying on some thread of execution to eventually arrive at what previously was a trusted routine).]
	 */
	arm_vm_page_granular_RWNX(phystokv(args->topOfKernelData) + ARM_PGBYTES * 8, ARM_PGBYTES, FALSE); /* Excess physMem over 1MB */
	arm_vm_page_granular_RWX(phystokv(args->topOfKernelData) + ARM_PGBYTES * 9, ARM_PGBYTES, FALSE); /* refined in finalize */

	/* Map the remainder of xnu owned memory. */
	arm_vm_page_granular_RWNX(phystokv(args->topOfKernelData) + ARM_PGBYTES * 10,
	    static_memory_end - (phystokv(args->topOfKernelData) + ARM_PGBYTES * 10), force_coarse_physmap);                       /* rest of physmem */

	/*
	 * Special case write protection for the mapping of ExceptionVectorsBase (EVB) at 0xFFFF0000.
	 * Recall that start.s handcrafted a page table page for EVB mapping
	 */
	pmap_paddr_t p = (pmap_paddr_t)(args->topOfKernelData) + (ARM_PGBYTES * 9);
	pt_entry_t *ppte = (pt_entry_t *)phystokv(p);

	int idx = (HIGH_EXC_VECTORS & ARM_TT_L2_INDEX_MASK) >> ARM_TT_L2_SHIFT;
	pt_entry_t ptmp = ppte[idx];

	ptmp = (ptmp & ~ARM_PTE_APMASK) | ARM_PTE_AP(AP_RONA);

	ppte[idx] = ptmp;
}

void
arm_vm_prot_finalize(boot_args * args)
{
	cpu_stack_alloc(&BootCpuData);
	ml_static_mfree(segBOOTDATAB, segSizeBOOTDATA);
	/*
	 * Naively we could have:
	 * arm_vm_page_granular_ROX(segTEXTB, segSizeTEXT, FALSE);
	 * but, at present, that would miss a 1Mb boundary at the beginning of the segment and
	 * so would force a (wasteful) coarse page (e.g. when gVirtBase is 0x80000000, segTEXTB is 0x80001000).
	 */
	arm_vm_page_granular_ROX(gVirtBase, segSizeTEXT + (segTEXTB - gVirtBase), FALSE);

	arm_vm_page_granular_RWNX(phystokv(args->topOfKernelData) + ARM_PGBYTES * 9, ARM_PGBYTES, FALSE); /* commpage, EVB */

#ifndef  __ARM_L1_PTW__
	FlushPoC_Dcache();
#endif
	flush_mmu_tlb();
}

/* used in the chosen/memory-map node, populated by iBoot. */
typedef struct MemoryMapFileInfo {
	vm_offset_t paddr;
	size_t length;
} MemoryMapFileInfo;


void
arm_vm_init(uint64_t memory_size, boot_args * args)
{
	vm_map_address_t va, off, off_end;
	tt_entry_t       *tte, *tte_limit;
	pmap_paddr_t     boot_ttep;
	tt_entry_t       *boot_tte;
	uint32_t         mem_segments;
	kernel_section_t *sectDCONST;

	/*
	 * Get the virtual and physical memory base from boot_args.
	 */
	gVirtBase = args->virtBase;
	gPhysBase = args->physBase;
	gPhysSize = args->memSize;
	mem_size = args->memSize;
	if ((memory_size != 0) && (mem_size > memory_size)) {
		mem_size = memory_size;
	}
	if (mem_size > MEM_SIZE_MAX) {
		mem_size = MEM_SIZE_MAX;
	}
	static_memory_end = gVirtBase + mem_size;

	/* Calculate the nubmer of ~256MB segments of memory */
	mem_segments = (mem_size + 0x0FFFFFFF) >> 28;

	/*
	 * Copy the boot mmu tt to create system mmu tt.
	 * System mmu tt start after the boot mmu tt.
	 * Determine translation table base virtual address: - aligned at end
	 * of executable.
	 */
	boot_ttep = args->topOfKernelData;
	boot_tte = (tt_entry_t *) phystokv(boot_ttep);

	cpu_ttep = boot_ttep + ARM_PGBYTES * 4;
	cpu_tte = (tt_entry_t *) phystokv(cpu_ttep);

	bcopy(boot_tte, cpu_tte, ARM_PGBYTES * 4);

	/*
	 * Clear out any V==P mappings that may have been established in e.g. start.s
	 */
	tte = &cpu_tte[ttenum(gPhysBase)];
	tte_limit = &cpu_tte[ttenum(gPhysBase + gPhysSize)];

	/* Hands off [gVirtBase, gVirtBase + gPhysSize) please. */
	if (gPhysBase < gVirtBase) {
		if (gPhysBase + gPhysSize > gVirtBase) {
			tte_limit = &cpu_tte[ttenum(gVirtBase)];
		}
	} else {
		if (gPhysBase < gVirtBase + gPhysSize) {
			tte = &cpu_tte[ttenum(gVirtBase + gPhysSize)];
		}
	}

	while (tte < tte_limit) {
		*tte = ARM_TTE_TYPE_FAULT;
		tte++;
	}

	/* Skip 6 pages (four L1 + two L2 entries) */
	avail_start = cpu_ttep + ARM_PGBYTES * 6;
	avail_end = gPhysBase + mem_size;

	/*
	 * Now retrieve addresses for end, edata, and etext
	 * from MACH-O headers for the currently running 32 bit kernel.
	 */
	segTEXTB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__TEXT", &segSizeTEXT);
	segLOWESTTEXT = segTEXTB;
	segDATAB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__DATA", &segSizeDATA);
	segLINKB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__LINKEDIT", &segSizeLINK);
	segKLDB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__KLD", &segSizeKLD);
	segLASTB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__LAST", &segSizeLAST);
	segPRELINKTEXTB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__PRELINK_TEXT", &segSizePRELINKTEXT);
	segPRELINKINFOB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__PRELINK_INFO", &segSizePRELINKINFO);
	segBOOTDATAB = (vm_offset_t) getsegdatafromheader(&_mh_execute_header, "__BOOTDATA", &segSizeBOOTDATA);

	segEXTRADATA = 0;
	segSizeEXTRADATA = 0;

	DTEntry memory_map;
	MemoryMapFileInfo *trustCacheRange;
	unsigned int trustCacheRangeSize;
	int err;

	err = DTLookupEntry(NULL, "chosen/memory-map", &memory_map);
	assert(err == kSuccess);

	err = DTGetProperty(memory_map, "TrustCache", (void**)&trustCacheRange, &trustCacheRangeSize);
	if (err == kSuccess) {
		assert(trustCacheRangeSize == sizeof(MemoryMapFileInfo));

		segEXTRADATA = phystokv(trustCacheRange->paddr);
		segSizeEXTRADATA = trustCacheRange->length;
	}

	etext = (vm_offset_t) segTEXTB + segSizeTEXT;
	sdata = (vm_offset_t) segDATAB;
	edata = (vm_offset_t) segDATAB + segSizeDATA;
	end_kern = round_page(getlastaddr());   /* Force end to next page */

	/*
	 * Special handling for the __DATA,__const *section*.
	 * A page of padding named lastkerneldataconst is at the end of the __DATA,__const
	 * so we can safely truncate the size. __DATA,__const is also aligned, but
	 * just in case we will round that to a page, too.
	 */
	segDATA = getsegbynamefromheader(&_mh_execute_header, "__DATA");
	sectDCONST = getsectbynamefromheader(&_mh_execute_header, "__DATA", "__const");
	sectCONSTB = sectDCONST->addr;
	sectSizeCONST = sectDCONST->size;

#if !SECURE_KERNEL
	/* doconstro is true by default, but we allow a boot-arg to disable it */
	(void) PE_parse_boot_argn("dataconstro", &doconstro, sizeof(doconstro));
#endif

	if (doconstro) {
		extern vm_offset_t _lastkerneldataconst;
		extern vm_size_t _lastkerneldataconst_padsize;
		vm_offset_t sdataconst = sectCONSTB;

		/* this should already be aligned, but so that we can protect we round */
		sectCONSTB = round_page(sectCONSTB);

		/* make sure lastkerneldataconst is really last and the right size */
		if ((_lastkerneldataconst == sdataconst + sectSizeCONST - _lastkerneldataconst_padsize) &&
		    (_lastkerneldataconst_padsize >= PAGE_SIZE)) {
			sectSizeCONST = trunc_page(sectSizeCONST);
		} else {
			/* otherwise see if next section is aligned then protect up to it */
			kernel_section_t *next_sect = nextsect(segDATA, sectDCONST);

			if (next_sect && ((next_sect->addr & PAGE_MASK) == 0)) {
				sectSizeCONST = next_sect->addr - sectCONSTB;
			} else {
				/* lastly just go ahead and truncate so we try to protect something */
				sectSizeCONST = trunc_page(sectSizeCONST);
			}
		}

		/* sanity check */
		if ((sectSizeCONST == 0) || (sectCONSTB < sdata) || (sectCONSTB + sectSizeCONST) >= edata) {
			doconstro = FALSE;
		}
	}

	vm_set_page_size();

#ifndef __ARM_L1_PTW__
	FlushPoC_Dcache();
#endif
	set_mmu_ttb(cpu_ttep);
	set_mmu_ttb_alternate(cpu_ttep);
	flush_mmu_tlb();
#if __arm__ && __ARM_USER_PROTECT__
	{
		unsigned int ttbr0_val, ttbr1_val, ttbcr_val;
		thread_t thread = current_thread();

		__asm__ volatile ("mrc p15,0,%0,c2,c0,0\n" : "=r"(ttbr0_val));
		__asm__ volatile ("mrc p15,0,%0,c2,c0,1\n" : "=r"(ttbr1_val));
		__asm__ volatile ("mrc p15,0,%0,c2,c0,2\n" : "=r"(ttbcr_val));
		thread->machine.uptw_ttb = ttbr0_val;
		thread->machine.kptw_ttb = ttbr1_val;
		thread->machine.uptw_ttc = ttbcr_val;
	}
#endif
	vm_prelink_stext = segPRELINKTEXTB;
	vm_prelink_etext = segPRELINKTEXTB + segSizePRELINKTEXT;
	vm_prelink_sinfo = segPRELINKINFOB;
	vm_prelink_einfo = segPRELINKINFOB + segSizePRELINKINFO;
	vm_slinkedit = segLINKB;
	vm_elinkedit = segLINKB + segSizeLINK;

	sane_size = mem_size - (avail_start - gPhysBase);
	max_mem = mem_size;
	vm_kernel_slide = gVirtBase - VM_KERNEL_LINK_ADDRESS;
	vm_kernel_stext = segTEXTB;
	vm_kernel_etext = segTEXTB + segSizeTEXT;
	vm_kernel_base = gVirtBase;
	vm_kernel_top = (vm_offset_t) &last_kernel_symbol;
	vm_kext_base = segPRELINKTEXTB;
	vm_kext_top = vm_kext_base + segSizePRELINKTEXT;
	vm_kernel_slid_base = segTEXTB;
	vm_kernel_slid_top = vm_kext_top;

	pmap_bootstrap((gVirtBase + MEM_SIZE_MAX + 0x3FFFFF) & 0xFFC00000);

	arm_vm_prot_init(args);

	/*
	 * To avoid recursing while trying to init the vm_page and object * mechanisms,
	 * pre-initialize kernel pmap page table pages to cover this address range:
	 *    2MB + FrameBuffer size + 3MB for each 256MB segment
	 */
	off_end = (2 + (mem_segments * 3)) << 20;
	off_end += (unsigned int) round_page(args->Video.v_height * args->Video.v_rowBytes);

	for (off = 0, va = (gVirtBase + MEM_SIZE_MAX + 0x3FFFFF) & 0xFFC00000; off < off_end; off += ARM_TT_L1_PT_SIZE) {
		pt_entry_t   *ptp;
		pmap_paddr_t ptp_phys;

		ptp = (pt_entry_t *) phystokv(avail_start);
		ptp_phys = (pmap_paddr_t)avail_start;
		avail_start += ARM_PGBYTES;
		pmap_init_pte_page(kernel_pmap, ptp, va + off, 2, TRUE);
		tte = &cpu_tte[ttenum(va + off)];
		*tte     = pa_to_tte((ptp_phys)) | ARM_TTE_TYPE_TABLE;;
		*(tte + 1) = pa_to_tte((ptp_phys + 0x400)) | ARM_TTE_TYPE_TABLE;;
		*(tte + 2) = pa_to_tte((ptp_phys + 0x800)) | ARM_TTE_TYPE_TABLE;;
		*(tte + 3) = pa_to_tte((ptp_phys + 0xC00)) | ARM_TTE_TYPE_TABLE;;
	}

	avail_start = (avail_start + PAGE_MASK) & ~PAGE_MASK;

	first_avail = avail_start;
	patch_low_glo_static_region(args->topOfKernelData, avail_start - args->topOfKernelData);
}