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
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
/*
 * Copyright (c) 2000-2020 Apple 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@
 */
/*
 * @OSF_COPYRIGHT@
 */

#ifdef  XNU_KERNEL_PRIVATE

#ifndef _KERN_STARTUP_H_
#define _KERN_STARTUP_H_

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include <libkern/section_keywords.h>

__BEGIN_DECLS

__exported_push_hidden

/*!
 * @enum startup_subsystem_id_t
 *
 * @abstract
 * Represents a stage of kernel intialization, ubnd allows for subsystems
 * to register initializers for a specific stage.
 *
 * @discussion
 * Documentation of each subsystem initialization sequence exists in
 * @file doc/lifecycle/startup.md.
 */
__enum_decl(startup_subsystem_id_t, uint32_t, {
	STARTUP_SUB_NONE = 0,         /**< reserved for the startup subsystem  */

	STARTUP_SUB_TUNABLES,         /**< support for the tunables subsystem  */
	STARTUP_SUB_TIMEOUTS,         /**< configurable machine timeouts       */
	STARTUP_SUB_LOCKS,            /**< various subsystem locks             */
	STARTUP_SUB_KPRINTF,          /**< kprintf initialization              */

	STARTUP_SUB_PMAP_STEAL,       /**< to perform various pmap carveouts   */
	STARTUP_SUB_KMEM,             /**< once kmem_alloc is ready            */
	STARTUP_SUB_ZALLOC,           /**< initialize zalloc and kalloc        */
	STARTUP_SUB_KTRACE,           /**< initialize kernel trace             */
	STARTUP_SUB_PERCPU,           /**< initialize the percpu subsystem     */
	STARTUP_SUB_SCHED,            /**< initialize scheduler subsystem      */
	STARTUP_SUB_EVENT,            /**< initiailze the event subsystem      */

	STARTUP_SUB_CODESIGNING,      /**< codesigning subsystem               */
	STARTUP_SUB_OSLOG,            /**< oslog and kernel logging            */
	STARTUP_SUB_MACH_IPC,         /**< Mach IPC                            */
	STARTUP_SUB_THREAD_CALL,      /**< Thread calls                        */
	STARTUP_SUB_SYSCTL,           /**< registers sysctls                   */
	STARTUP_SUB_EXCLAVES,         /**< initialize exclaves                 */
	STARTUP_SUB_EARLY_BOOT,       /**< interrupts/preemption are turned on */

	STARTUP_SUB_LOCKDOWN = ~0u,   /**< reserved for the startup subsystem  */
});

/*!
 * Stores the last subsystem to have been fully initialized;
 */
extern startup_subsystem_id_t startup_phase;

/*!
 * @enum startup_debug_t
 *
 * @abstract
 * Flags set in the @c startup_debug global to configure startup debugging.
 */
__options_decl(startup_debug_t, uint32_t, {
	STARTUP_DEBUG_NONE    = 0x00000000,
	STARTUP_DEBUG_VERBOSE = 0x00000001,
});

/*!
 * @enum startup_source_t
 *
 * @abstract
 * The source from which a tunable was resolved.
 */
__options_decl(startup_source_t, uint32_t, {
	STARTUP_SOURCE_DEFAULT,
	STARTUP_SOURCE_DEVICETREE,
	STARTUP_SOURCE_BOOTPARAM,
});

extern startup_debug_t startup_debug;

/*!
 * @enum startup_rank
 *
 * @abstract
 * Specifies in which rank a given initializer runs within a given section
 * to register initializers for a specific rank within the subsystem.
 *
 * @description
 * A startup function, declared with @c STARTUP or @c STARTUP_ARG, can specify
 * a rank within the subsystem they initialize.
 *
 * @c STARTUP_RANK_NTH(n) will let callbacks be run at stage @c n (0-based).
 *
 * @c STARTUP_RANK_FIRST, @c STARTUP_RANK_SECOND, @c STARTUP_RANK_THIRD and
 * @c STARTUP_RANK_FOURTH are given as convenient names for these.
 *
 * @c STARTUP_RANK_MIDDLE is a reserved value that will let startup functions
 * run after all the @c STARTUP_RANK_NTH(n) ones have.
 *
 * @c STARTUP_RANK_NTH_LATE_NTH(n) will let callbacks be run then in @c n rank
 * after the @c STARTUP_RANK_MIDDLE ones (0-based).
 *
 * @c STARTUP_RANK_LAST callbacks will run absolutely last after everything
 * else did for this subsystem.
 */
__enum_decl(startup_rank_t, uint32_t, {
#define STARTUP_RANK_NTH(n)           ((startup_rank_t)(n - 1))
	STARTUP_RANK_FIRST          = 0,
	STARTUP_RANK_SECOND         = 1,
	STARTUP_RANK_THIRD          = 2,
	STARTUP_RANK_FOURTH         = 3,

	STARTUP_RANK_MIDDLE         = 0x7fffffff,

#define STARTUP_RANK_LATE_NTH(n) \
	((startup_rank_t)(STARTUP_RANK_MIDDLE + 1 + (n)))

	STARTUP_RANK_LAST           = 0xffffffff,
});

#if KASAN
/*
 * The use of weird sections that get unmapped confuse the hell out of kasan,
 * so for KASAN leave things in regular __TEXT/__DATA segments
 */
#define STARTUP_CODE_SEGSECT "__TEXT,__text"
#define STARTUP_CONST_SEGSECT "__DATA_CONST,__init"
#define STARTUP_DATA_SEGSECT "__DATA,__init"
#define STARTUP_HOOK_SEGMENT "__DATA"
#define STARTUP_HOOK_SECTION "__init_entry_set"
#elif defined(__x86_64__)
/* Intel doesn't have a __BOOTDATA but doesn't protect __KLD */
#define STARTUP_CODE_SEGSECT "__TEXT,__text"
#define STARTUP_CONST_SEGSECT "__KLDDATA,__const"
#define STARTUP_DATA_SEGSECT "__KLDDATA,__init"
#define STARTUP_HOOK_SEGMENT "__KLDDATA"
#define STARTUP_HOOK_SECTION "__init_entry_set"
#else
/* arm protects __KLD early, so use __BOOTDATA for data */
#define STARTUP_CODE_SEGSECT "__TEXT,__text"
#define STARTUP_CONST_SEGSECT "__KLDDATA,__const"
#define STARTUP_DATA_SEGSECT "__BOOTDATA,__init"
#define STARTUP_HOOK_SEGMENT "__BOOTDATA"
#define STARTUP_HOOK_SECTION "__init_entry_set"
#endif

/*!
 * @macro __startup_func
 *
 * @abstract
 * Attribute to place on functions used only during the kernel startup phase.
 *
 * @description
 * Code marked with this attribute will be unmapped after kernel lockdown.
 */
#ifndef __BUILDING_XNU_LIBRARY__
#define __startup_func \
	__PLACE_IN_SECTION(STARTUP_CODE_SEGSECT) \
	__attribute__((cold, visibility("hidden")))
#else
/* tester needs some startup function to be visible from outside the XNU library */
#define __startup_func __unused
#endif
/*!
 * @macro __startup_data
 *
 * @abstract
 * Attribute to place on globals used during the kernel startup phase.
 *
 * @description
 * Data marked with this attribute will be unmapped after kernel lockdown.
 */
#ifndef __BUILDING_XNU_LIBRARY__
#define __startup_data \
	__PLACE_IN_SECTION(STARTUP_DATA_SEGSECT)
#else
#define __startup_data
#endif

/*!
 * @macro __startup_const
 *
 * @abstract
 * Attribute to place on global constants used during the kernel startup phase.
 *
 * @description
 * Data marked with this attribute will be unmapped after kernel lockdown.
 *
 * __startup_const implies const. Be mindful that for pointers, the `const`
 * will end up on the wrong side of the star if you put __startup_const at the
 * start of the declaration.
 */
#define __startup_const \
	__PLACE_IN_SECTION(STARTUP_CONST_SEGSECT) const

/*!
 * @macro STARTUP
 *
 * @abstract
 * Declares a kernel startup callback.
 */
#define STARTUP(subsystem, rank, func) \
	__STARTUP(func, __LINE__, subsystem, rank, func)

/*!
 * @macro STARTUP_ARG
 *
 * @abstract
 * Declares a kernel startup callback that takes an argument.
 */
#define STARTUP_ARG(subsystem, rank, func, arg) \
	__STARTUP_ARG(func, __LINE__, subsystem, rank, func, arg)

/*!
 * @macro TUNABLE
 *
 * @abstract
 * Declares a read-only kernel tunable that is read from a boot-arg with
 * a default value, without further processing.
 *
 * @param type_t
 * Should be an integer type or bool.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param boot_arg
 * The name of the boot-arg to parse for initialization
 *
 * @param default_value
 * The default value for the tunable if the boot-arg is absent.
 */
#define TUNABLE(type_t, var, boot_arg, default_value) \
	SECURITY_READ_ONLY_LATE(type_t) var = default_value; \
	__TUNABLE(type_t, var, boot_arg)

/*!
 * @macro TUNABLE_WRITEABLE
 *
 * @abstract
 * Declares a writeable kernel tunable that is read from a boot-arg with
 * a default value, without further processing.
 *
 * @param type_t
 * Should be an integer type or bool.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param boot_arg
 * The name of the boot-arg to parse for initialization
 *
 * @param default_value
 * The default value for the tunable if the boot-arg is absent.
 */
#define TUNABLE_WRITEABLE(type_t, var, boot_arg, default_value) \
	type_t var = default_value; \
	__TUNABLE(type_t, var, boot_arg)

#if DEBUG || DEVELOPMENT
#define TUNABLE_DEV_WRITEABLE(type_t, var, boot_arg, default_value) \
	TUNABLE_WRITEABLE(type_t, var, boot_arg, default_value)
#else
#define TUNABLE_DEV_WRITEABLE(type_t, var, boot_arg, default_value) \
	TUNABLE(type_t, var, boot_arg, default_value)
#endif

/*!
 * @macro TUNABLE_STR
 *
 * @abstract
 * Declares a read-only kernel tunable that is read from a boot-arg with
 * a default value, without further processing.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param count
 * The number of bytes in the buffer.
 *
 * @param boot_arg
 * The name of the boot-arg to parse for initialization
 *
 * @param default_value
 * The default value for the tunable if the boot-arg is absent.
 */
#define TUNABLE_STR(var, count, boot_arg, default_value) \
	char __security_const_late var[count] = default_value; \
	__TUNABLE_STR(var, boot_arg)

/*!
 * @enum tunable_dt_flags_t
 *
 * @abstract
 * Flags used with the @c TUNABLE_DT* macros.
 *
 * @description
 * If TUNABLE_DT_CHECK_CHOSEN is set, a value in
 * /chosen/<dt_base>/<dt_name> takes precedence over any value in
 * /<dt_base>/<dt_name>. /chosen is by convention the area where
 * synthesized values not coming from the serialized device tree are
 * being added, so this provides a way for e.g. the boot-loader to
 * set/override tunables. Don't override with a boot-arg if
 * TUNABLE_DT_NO_BOOTARG is set.
 */
__options_decl(tunable_dt_flags_t, uint32_t, {
	TUNABLE_DT_NONE         = 0x00000000,
	TUNABLE_DT_CHECK_CHOSEN = 0x00000001,
	TUNABLE_DT_NO_BOOTARG   = 0x00000002,
});

/*!
 * @macro TUNABLE_DT
 *
 * @abstract
 * Like TUNABLE, but gets the initial value from both Device Tree and
 * boot-args. The order in which the initial value is resolved is as
 * follows, with later steps overriding previous ones (if they are
 * specified):
 *
 * 1. Device Tree Entry "/<dt_base>/<dt_name>",
 * 2. If TUNABLE_DT_CHECK_CHOSEN is set, Device Tree Entry
 *    "/chosen/<dt_base>/<dt_name>" (see the description for
 *    @c tunable_dt_flags_t),
 * 3. boot-args.
 *
 * @param type_t
 * Should be an integer type or bool.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param dt_base
 * The name of the DT node containing the property.
 *
 * @param dt_name
 * The name of the DT property containing the default value.
 *
 * @param boot_arg
 * The name of the boot-arg overriding the initial value from the DT.
 *
 * @param default_value
 * The default value for the tunable if both DT entry and boot-arg are
 * absent.
 *
 * @param flags
 * See the description for @c tunable_dt_flags_t.
 */
#define TUNABLE_DT(type_t, var, dt_base, dt_name, boot_arg, default_value, flags) \
	SECURITY_READ_ONLY_LATE(type_t) var = default_value; \
	__TUNABLE_DT(type_t, var, dt_base, dt_name, boot_arg, flags)

/*!
 * @macro TUNABLE_DT_WRITEABLE
 *
 * @abstract
 * Like TUNABLE_WRITEABLE, but gets the initial value from both Device
 * Tree and boot-args. The order in which the initial value is
 * resolved is as follows, with later steps overriding previous ones
 * (if they are specified):
 *
 * 1. Device Tree Entry "/<dt_base>/<dt_name>",
 * 2. If TUNABLE_DT_CHECK_CHOSEN is set, Device Tree Entry
 *    "/chosen/<dt_base>/<dt_name>" (see the description for
 *    @c tunable_dt_flags_t),
 * 3. boot-args.
 *
 * @param type_t
 * Should be an integer type or bool.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param dt_base
 * The name of the DT node containing the property.
 *
 * @param dt_name
 * The name of the DT property containing the default value.
 *
 * @param boot_arg
 * The name of the boot-arg overriding the initial value from the DT.
 *
 * @param default_value
 * The default value for the tunable if both DT entry and boot-arg are
 * absent.
 *
 * @param flags
 * See the description for @c tunable_dt_flags_t.
 */
#define TUNABLE_DT_WRITEABLE(type_t, var, dt_base, dt_name, boot_arg, default_value, flags) \
	type_t var = default_value; \
	__TUNABLE_DT(type_t, var, dt_base, dt_name, boot_arg, flags)

#if DEBUG || DEVELOPMENT
#define TUNABLE_DT_DEV_WRITEABLE(type_t, var, dt_base, dt_name, boot_arg, default_value, flags) \
	TUNABLE_DT_WRITEABLE(type_t, var, dt_base, dt_name, boot_arg, default_value, flags)
#else
#define TUNABLE_DT_DEV_WRITEABLE(type_t, var, dt_base, dt_name, boot_arg, default_value, flags) \
	TUNABLE_DT(type_t, var, dt_base, dt_name, boot_arg, default_value, flags)
#endif

/*!
 * @macro TUNABLE_DT_WRITEABLE_SOURCE
 *
 * @abstract
 * Like TUNABLE_DT_WRITEABLE, but records the source from which the value was resolved.
 *
 * @param type_t
 * Should be an integer type or bool.
 *
 * @param var
 * The name of the C variable to use for storage.
 *
 * @param source_var
 * The name of the C variable to record the source. It will be of type startup_source_t.
 *
 * @param dt_base
 * The name of the DT node containing the property.
 *
 * @param dt_name
 * The name of the DT property containing the default value.
 *
 * @param boot_arg
 * The name of the boot-arg overriding the initial value from the DT.
 *
 * @param default_value
 * The default value for the tunable if both DT entry and boot-arg are
 * absent.
 *
 * @param flags
 * See the description for @c tunable_dt_flags_t.
 */
#define TUNABLE_DT_WRITEABLE_SOURCE(type_t, var, source_var, dt_base, dt_name, boot_arg, default_value, flags) \
	type_t var = default_value; \
    startup_source_t source_var; \
	__TUNABLE_DT_SOURCE(type_t, var, source_var, dt_base, dt_name, boot_arg, flags)

/*
 * Machine Timeouts
 *
 * Machine Timeouts are timeouts for low level kernel code manifesting
 * as _Atomic uint64_t variables, whose default value can be
 * overridden and scaled via the device tree and boot-args.
 *
 * Each timeout has a name, looked up directly as the property name in
 * the device tree in both the "/machine-timeouts" and
 * "/chosen/machine-timeouts" nodes. The "chosen" property always
 * overrides the other one. This allows fixed per-device timeouts in
 * the device tree to be overridden by iBoot in "chosen".
 *
 * Additionally, the same name with "-scale" appended is looked up as
 * properties for optional scale factors. Scale factors are not
 * overridden by chosen, instead all scale factors (including global
 * and/or boot-arg scale factors) combine by multiplication.
 *
 * The special name "global-scale" provides a scale that applies to
 * every timeout.
 *
 * All property names can be used as boot-args by prefixing
 * "ml-timeout-", e.g. th global scale is available as the
 * "ml-timeout-global-scale" boot-arg.
 *
 * By convention, if the timeout value resolves to 0, the timeout
 * should be disabled.
 */

/*
 * Machine Timeouts types. See the next section for what unit
 * they are in.
 *
 * We use _Atomic, but only with relaxed ordering: This is just to
 * make sure all devices see consistent values all the time.  Since
 * the actual timeout value will be seen as 0 before initializaton,
 * relaxed ordering means that code that runs concurrently with
 * initialization only risks to see a disabled timeout during early
 * boot.
 */
typedef _Atomic uint64_t machine_timeout_t;

/*
 * Units
 *
 * Machine Timeouts are ALWAYS in picoseconds in the device tree or
 * boot-args, to avoid confusion when changing or comparing timeouts
 * as a user, but the actual storage value might contain the same
 * duration in another unit, calculated by the initialization code.
 *
 * This is done because otherwise we would likely introduce another
 * multiplication in potentially hot code paths, given that code that
 * actually uses the timeout storage variable is unlikely to work with
 * picosecond values when comparing against the timeout deadline.
 *
 * This unit scale is *only* applied during initialization at early
 * boot, and only if the timeout's default value was overridden
 * through the device tree or a boot-arg.
 */
#define MACHINE_TIMEOUT_UNIT_PSEC 1
#define MACHINE_TIMEOUT_UNIT_NSEC 1000
#define MACHINE_TIMEOUT_UNIT_USEC (1000*1000)
#define MACHINE_TIMEOUT_UNIT_MSEC (1000*1000*1000)
// Special unit for timebase ticks (usually 1/24MHz)
#define MACHINE_TIMEOUT_UNIT_TIMEBASE 0

// DT property names are limited to 31 chars, minus "-global" suffix
#define MACHINE_TIMEOUT_MAX_NAME_LEN 25
struct machine_timeout_spec {
	void *ptr;
	uint64_t default_value;
	uint64_t unit_scale;
	char name[MACHINE_TIMEOUT_MAX_NAME_LEN + 1];
	bool (*skip_predicate)(struct machine_timeout_spec const *);
};

extern void
machine_timeout_init_with_suffix(const struct machine_timeout_spec *spec, char const *phase_suffix, bool always_enabled);

extern void
machine_timeout_init(const struct machine_timeout_spec *spec);

extern void
machine_timeout_init_always_enabled(const struct machine_timeout_spec *spec);

#if DEVELOPMENT || DEBUG
// Late timeout (re-)initialization, at the end of bsd_init()
extern void
machine_timeout_bsd_init(void);
#endif /* DEVELOPMENT || DEBUG */

/*!
 * @macro MACHINE_TIMEOUT, MACHINE_TIMEOUT_ALWAYS_ENABLED, and MACHINE_TIMEOUT_DEV_WRITEABLE
 *
 * @abstract
 * Defines a Machine Timeout that can be overridden and
 * scaled through the device tree and boot-args.
 *
 * The variant with the _DEV_WRITEABLE suffix does not mark the timeout as
 * SECURITY_READ_ONLY_LATE on DEVELOPMENT kernels, so that e.g.
 * machine_timeout_init_with_suffix or sysctls can change it after lockdown.
 *
 * @param var
 * The name of the C variable to use for storage. If the storage value
 * contains 0, the timeout is considered disabled by convention.
 *
 * @param timeout_name
 * The name of the timeout, used for property and boot-arg names. See
 * the general description of Machine Timeouts above for how this name
 * ends up being used.
 *
 * @param timeout_default
 * The default value for the timeout if not specified through device
 * tree or boot-arg. Will still be scaled if a scale factor exists.
 *
 * @param var_unit
 * The unit that the storage variable is in. Note that timeout values
 * must always be specified as picoseconds in the device tree and
 * boot-args, but timeout initialization will convert the value to the
 * unit specified here before writing it to the storage variable.
 *
 * @param skip_predicate
 * Optionally, a function to call to decide whether the timeout should
 * be set or not.  If NULL, the timeout will always be set (if
 * specified anywhere). A predicate has the following signature:
 *     bool skip_predicate (struct machine_timeout_spec const *)
 */

#define _MACHINE_TIMEOUT(var, timeout_name, timeout_default, var_unit, skip_pred, init_fn) \
	struct machine_timeout_spec \
	__machine_timeout_spec_ ## var = { \
	        .ptr = &var, \
	        .default_value = timeout_default, \
	        .unit_scale = var_unit, \
	        .name = timeout_name, \
	        .skip_predicate = skip_pred, \
	}; \
	__STARTUP_ARG(var, __LINE__, TIMEOUTS, STARTUP_RANK_FIRST, \
	    init_fn, &__machine_timeout_spec_ ## var)

#define MACHINE_TIMEOUT(var, name, default, unit, skip_predicate)       \
	SECURITY_READ_ONLY_LATE(machine_timeout_t) var = 0;                                     \
	_MACHINE_TIMEOUT(var, name, default, unit, skip_predicate, machine_timeout_init)

/*
 * Variant of MACHINE_TIMEOUT that does not get zeroed if wdt == -1 boot arg is set
 */
#define MACHINE_TIMEOUT_ALWAYS_ENABLED(var, name, default, unit)       \
	SECURITY_READ_ONLY_LATE(machine_timeout_t) var = 0;                                     \
	_MACHINE_TIMEOUT(var, name, default, unit, NULL, machine_timeout_init_always_enabled)

#if DEVELOPMENT || DEBUG
#define MACHINE_TIMEOUT_DEV_WRITEABLE(var, name, default, unit, skip_predicate)       \
	machine_timeout_t var = 0; \
	_MACHINE_TIMEOUT(var, name, default, unit, skip_predicate, machine_timeout_init)
#else
#define MACHINE_TIMEOUT_DEV_WRITEABLE(var, name, default, unit, skip_predicate) \
	MACHINE_TIMEOUT(var, name, default, unit, skip_predicate)
#endif /* DEVELOPMENT || DEBUG */

/*!
 * @macro MACHINE_TIMEOUT_SPEC_REF
 *
 * @abstract
 * References a previously defined MACHINE_TIMEOUT.
 *
 * This is primarily useful for overriding individual timeouts
 * at arbitrary times (even after boot), by manually calling
 * machine_timeout_init_with_suffix() with this macro
 * as first argument, and a suffix to apply to both device tree and
 * boot-arg as second argument.
 *
 * @param var
 * The name of the C variable used for storage, as it was specified
 * in MACHINE_TIMEOUT.
 */
#define MACHINE_TIMEOUT_SPEC_REF(var) (&__machine_timeout_spec_ ## var)

/*!
 * @macro MACHINE_TIMEOUT_SPEC_DECL
 *
 * @abstract
 * Declaration of machine timeout spec, mostly useful to make it known
 * for MACHINE_TIMEOUT_SPEC_REF.
 *
 * @param var
 * The name of the C variable used for storage, as it was specified
 * in MACHINE_TIMEOUT.
 */
#define MACHINE_TIMEOUT_SPEC_DECL(var) extern struct machine_timeout_spec __machine_timeout_spec_ ## var

/*
 * Event subsystem
 *
 * This allows to define a few system-wide events that allow for loose coupling
 * between subsystems interested in those events and the emitter.
 */

/*!
 * @macro EVENT_DECLARE()
 *
 * @brief
 * Declares an event namespace with a given callback type.
 *
 * @param name          The name for the event (typically in all caps).
 * @param cb_type_t     A function type for the callbacks.
 */
#define EVENT_DECLARE(name, cb_type_t) \
	struct name##_event {                                                   \
	        struct event_hdr        evt_link;                               \
	        cb_type_t              *evt_cb;                                 \
	};                                                                      \
	extern struct event_hdr name##_HEAD

/*!
 * @macro EVENT_DEFINE()
 *
 * @brief
 * Defines the head for the event corresponding to an EVENT_DECLARE()
 */
#define EVENT_DEFINE(name) \
	__security_const_late struct event_hdr name##_HEAD

/*!
 * @macro EVENT_REGISTER_HANDLER()
 *
 * @brief
 * Registers a handler for a given event.
 *
 * @param name          The name for the event as declared in EVENT_DECLARE()
 * @param handler       The handler to register for this event.
 */
#define EVENT_REGISTER_HANDLER(name, handler) \
	__EVENT_REGISTER(name, __LINE__, handler)


/*!
 * @macro EVENT_INVOKE()
 *
 * @brief
 * Call all the events handlers with the specified arguments.
 *
 * @param name          The name for the event as declared in EVENT_DECLARE()
 * @param handler       The handler to register for this event.
 */
#define EVENT_INVOKE(name, ...) \
	for (struct event_hdr *__e = &name##_HEAD; (__e = __e->next);) {        \
	        __container_of(__e, struct name##_event,                        \
	            evt_link)->evt_cb(__VA_ARGS__);                             \
	}


#if DEBUG || DEVELOPMENT

/*!
 * @macro SYSCTL_TEST_REGISTER
 *
 * @abstract
 * Declares a test that will appear under @c debug.test.${name}.
 *
 * @param name
 * An identifier that will be stringified to form the sysctl test name.
 *
 * @param cb
 * The callback to run, of type:
 * <code>
 *     int (callback *)(int64_t value, int64_t *);
 * </code>
 */
#define SYSCTL_TEST_REGISTER(name, cb) \
	static __startup_data struct sysctl_test_setup_spec \
	__startup_SYSCTL_TEST_ ## name = { \
	        .st_name = #name, \
	        .st_func = &cb, \
	}; \
	STARTUP_ARG(SYSCTL, STARTUP_RANK_MIDDLE, \
	    sysctl_register_test_startup, &__startup_SYSCTL_TEST_ ## name)

#endif /* DEBUG || DEVELOPMENT */
#pragma mark - internals

__END_DECLS

#ifdef __cplusplus
template <typename T>
struct __startup_tunable {
	static const bool value  = false;
};

template <>
struct __startup_tunable <bool>{
	static const bool value = true;
};
#define __startup_type_is_bool(type_t) __startup_tunable<type_t>::value
#else
#define __startup_type_is_bool(type_t) __builtin_types_compatible_p(bool, type_t)
#endif

__BEGIN_DECLS

#define __TUNABLE(type_t, var, key) \
	static __startup_const char __startup_TUNABLES_name_ ## var[] = key; \
	static __startup_const struct startup_tunable_spec \
	__startup_TUNABLES_spec_ ## var = { \
	        .name = __startup_TUNABLES_name_ ## var, \
	        .var_addr = (void *)&var, \
	        .var_len = sizeof(type_t), \
	        .var_is_bool = __startup_type_is_bool(type_t), \
	}; \
	__STARTUP_ARG(var, __LINE__, TUNABLES, STARTUP_RANK_FIRST, \
	    kernel_startup_tunable_init, &__startup_TUNABLES_spec_ ## var)

#define __TUNABLE_STR(var, key) \
	static __startup_const char __startup_TUNABLES_name_ ## var[] = key; \
	static __startup_const struct startup_tunable_spec \
	__startup_TUNABLES_spec_ ## var = { \
	        .name = __startup_TUNABLES_name_ ## var, \
	        .var_addr = (void *)&var, \
	        .var_len = sizeof(var), \
	        .var_is_str = true, \
	}; \
	__STARTUP_ARG(var, __LINE__, TUNABLES, STARTUP_RANK_FIRST, \
	    kernel_startup_tunable_init, &__startup_TUNABLES_spec_ ## var)

#define __TUNABLE_DT(type_t, var, dt_base_key, dt_name_key, boot_arg_key, flags) \
	static __startup_const char __startup_TUNABLES_dt_base_ ## var[] = dt_base_key; \
	static __startup_const char __startup_TUNABLES_dt_name_ ## var[] = dt_name_key; \
	static __startup_const char __startup_TUNABLES_name_ ## var[] = boot_arg_key; \
	static __startup_const struct startup_tunable_dt_spec \
	__startup_TUNABLES_DT_spec_ ## var = { \
	        .dt_base = __startup_TUNABLES_dt_base_ ## var, \
	        .dt_name = __startup_TUNABLES_dt_name_ ## var, \
	        .dt_chosen_override = (bool)((flags) & TUNABLE_DT_CHECK_CHOSEN), \
	        .boot_arg_name = (flags & TUNABLE_DT_NO_BOOTARG) ? NULL : __startup_TUNABLES_name_ ## var, \
	        .var_addr = (void *)&var, \
	        .var_len = sizeof(type_t), \
	        .var_is_bool = __startup_type_is_bool(type_t), \
	}; \
	__STARTUP_ARG(var, __LINE__, TUNABLES, STARTUP_RANK_FIRST, \
	    kernel_startup_tunable_dt_init, &__startup_TUNABLES_DT_spec_ ## var)

#define __TUNABLE_DT_SOURCE(type_t, var, source_var, dt_base_key, dt_name_key, boot_arg_key, flags) \
	static __startup_const char __startup_TUNABLES_dt_base_ ## var[] = dt_base_key; \
	static __startup_const char __startup_TUNABLES_dt_name_ ## var[] = dt_name_key; \
	static __startup_const char __startup_TUNABLES_name_ ## var[] = boot_arg_key; \
	static __startup_const struct startup_tunable_dt_source_spec \
	__startup_TUNABLES_DT_spec_ ## var = { \
	        .dt_base = __startup_TUNABLES_dt_base_ ## var, \
	        .dt_name = __startup_TUNABLES_dt_name_ ## var, \
	        .dt_chosen_override = (bool)((flags) & TUNABLE_DT_CHECK_CHOSEN), \
	        .boot_arg_name = (flags & TUNABLE_DT_NO_BOOTARG) ? NULL : __startup_TUNABLES_name_ ## var, \
	        .var_addr = (void *)&var, \
	        .var_len = sizeof(type_t), \
	        .var_is_bool = __startup_type_is_bool(type_t), \
	    .source_addr = &source_var, \
	}; \
	__STARTUP_ARG(var, __LINE__, TUNABLES, STARTUP_RANK_FIRST, \
	    kernel_startup_tunable_dt_source_init, &__startup_TUNABLES_DT_spec_ ## var)

#ifdef __cplusplus
#define __STARTUP_FUNC_CAST(func, a) \
	    (void(*)(const void *))func
#else
#define __STARTUP_FUNC_CAST(func, a) \
	    (typeof(func(a))(*)(const void *))func
#endif

#ifdef __BUILDING_XNU_LIB_UNITTEST__
#define __STARTUP_SET_FUNC_NAME(name) .func_name = #name,
#else
#define __STARTUP_SET_FUNC_NAME(name)
#endif

#define __STARTUP1(name_v, line, subsystem_v, rank_v, func_v, a, b) \
	__PLACE_IN_SECTION(STARTUP_HOOK_SEGMENT "," STARTUP_HOOK_SECTION) \
	static const struct startup_entry \
	__startup_ ## subsystem_v ## _entry_ ## name_v ## _ ## line = { \
	    .subsystem = STARTUP_SUB_ ## subsystem_v, \
	    .rank = rank_v, \
	        .func = __STARTUP_FUNC_CAST(func_v, a),  \
	        .arg = b, \
	    __STARTUP_SET_FUNC_NAME(name_v) \
	}

#define __STARTUP(name, line, subsystem, rank, func) \
	__STARTUP1(name, line, subsystem, rank, func, , NULL)

#define __STARTUP_ARG(name, line, subsystem, rank, func, arg) \
	__STARTUP1(name, line, subsystem, rank, func, arg, arg)

struct startup_entry {
	startup_subsystem_id_t subsystem;
	startup_rank_t         rank;
	void                 (*func)(const void *);
	const void            *arg;
#ifdef __BUILDING_XNU_LIB_UNITTEST__
	const char            *func_name;
#endif
};

struct startup_tunable_spec {
	const char *name;
	void       *var_addr;
	int         var_len;
	bool        var_is_bool;
	bool        var_is_str;
};

struct startup_tunable_dt_spec {
	const char *dt_base;
	const char *dt_name;
	bool        dt_chosen_override;
	const char *boot_arg_name;
	void       *var_addr;
	int         var_len;
	bool        var_is_bool;
};

struct startup_tunable_dt_source_spec {
	const char       *dt_base;
	const char       *dt_name;
	bool              dt_chosen_override;
	const char       *boot_arg_name;
	void             *var_addr;
	int               var_len;
	bool              var_is_bool;
	startup_source_t *source_addr;
};

#if DEBUG || DEVELOPMENT
struct sysctl_test_setup_spec {
	const char *st_name;
	int (*st_func)(int64_t, int64_t *);
};

extern void sysctl_register_test_startup(
	struct sysctl_test_setup_spec *spec);
#endif /* DEBUG || DEVELOPMENT */

/*
 * Kernel and machine startup declarations
 */

/* Initialize kernel */
extern void kernel_startup_bootstrap(void);
extern void kernel_startup_initialize_upto(startup_subsystem_id_t upto);
extern void kernel_startup_tunable_init(const struct startup_tunable_spec *);
extern void kernel_startup_tunable_dt_init(const struct startup_tunable_dt_spec *);
extern void kernel_startup_tunable_dt_source_init(const struct startup_tunable_dt_source_spec *);
extern void kernel_bootstrap(void);

/* Initialize machine dependent stuff */
extern void machine_init(void);

extern void secondary_cpu_main(void *machine_param);

/* Secondary cpu initialization */
extern void machine_cpu_reinit(void *machine_param);
extern void processor_cpu_reinit(void* machine_param, bool wait_for_cpu_signal, bool is_final_system_sleep);

/* Device subsystem initialization */
extern void device_service_create(void);

struct event_hdr {
	struct event_hdr *next;
};

extern void event_register_handler(struct event_hdr *event_hdr);

#define __EVENT_REGISTER(name, lno, handler) \
	static __security_const_late struct name##_event name##_event_##lno = { \
	        .evt_link.next = &name##_HEAD,                                  \
	        .evt_cb = (handler),                                            \
	};                                                                      \
	__STARTUP_ARG(name, lno, EVENT, STARTUP_RANK_FIRST,                     \
	    event_register_handler, &name##_event_##lno.evt_link)

#ifdef  MACH_BSD

/* BSD subsystem initialization */
extern void bsd_init(void);

extern int serverperfmode;

#if defined(XNU_TARGET_OS_OSX)
static inline bool
kernel_is_macos_or_server(void)
{
	return true;
}
#else /* XNU_TARGET_OS_OSX */
static inline bool
kernel_is_macos_or_server(void)
{
	return serverperfmode != 0;
}
#endif /* XNU_TARGET_OS_OSX */

#endif  /* MACH_BSD */

__exported_pop

__END_DECLS

#endif  /* _KERN_STARTUP_H_ */

#endif  /* XNU_KERNEL_PRIVATE */