Tag Archives: macOS source code analysis

Source code analysis of macOS startup process

1. Sonic activation (osfmk \i386\i386_init.c)

void
vstart(vm_offset_t boot_args_start)
{
    boolean_t   is_boot_cpu = !(boot_args_start == 0);
    int     cpu;
    uint32_t    lphysfree;

    postcode(VSTART_ENTRY);

    if (is_boot_cpu) {
        /*
         * Get startup parameters.
         */
        kernelBootArgs = (boot_args *)boot_args_start;
        lphysfree = kernelBootArgs->kaddr + kernelBootArgs->ksize;
        physfree = (void *)(uintptr_t)((lphysfree + PAGE_SIZE - 1) &~ (PAGE_SIZE - 1));

#if DEVELOPMENT || DEBUG
        pal_serial_init();
#endif
        DBG("revision      0x%x\n", kernelBootArgs->Revision);
        DBG("version       0x%x\n", kernelBootArgs->Version);
        DBG("command line  %s\n", kernelBootArgs->CommandLine);
        DBG("memory map    0x%x\n", kernelBootArgs->MemoryMap);
        DBG("memory map sz 0x%x\n", kernelBootArgs->MemoryMapSize);
        DBG("kaddr         0x%x\n", kernelBootArgs->kaddr);
        DBG("ksize         0x%x\n", kernelBootArgs->ksize);
        DBG("physfree      %p\n", physfree);
        DBG("bootargs: %p, &ksize: %p &kaddr: %p\n",
            kernelBootArgs, 
            &kernelBootArgs->ksize,
            &kernelBootArgs->kaddr);
        DBG("SMBIOS mem sz 0x%llx\n", kernelBootArgs->PhysicalMemorySize);

        /*
         * Setup boot args given the physical start address.
         * Note: PE_init_platform needs to be called before Idle_PTs_init
         * because access to the DeviceTree is required to read the
         * random seed before generating a random physical map slide.
         */
        kernelBootArgs = (boot_args *)
            ml_static_ptovirt(boot_args_start);
        DBG("i386_init(0x%lx) kernelBootArgs=%p\n",
            (unsigned long)boot_args_start, kernelBootArgs);
        PE_init_platform(FALSE, kernelBootArgs);
        postcode(PE_INIT_PLATFORM_D);

        Idle_PTs_init();
        postcode(VSTART_IDLE_PTS_INIT);

        first_avail = (vm_offset_t)ID_MAP_VTOP(physfree);

        cpu = 0;
        cpu_data_alloc(TRUE);
    } else {
        /* Switch to kernel's page tables (from the Boot PTs) */
        set_cr3_raw((uintptr_t)ID_MAP_VTOP(IdlePML4));
        /* Find our logical cpu number */
        cpu = lapic_to_cpu[(LAPIC_READ(ID)>>LAPIC_ID_SHIFT) & LAPIC_ID_MASK];
        DBG("CPU: %d, GSBASE initial value: 0x%llx\n", cpu, rdmsr64(MSR_IA32_GS_BASE));
    }

    postcode(VSTART_CPU_DESC_INIT);
    if(is_boot_cpu)
        cpu_desc_init64(cpu_datap(cpu));
    cpu_desc_load64(cpu_datap(cpu));
    postcode(VSTART_CPU_MODE_INIT);
    if (is_boot_cpu)
        cpu_mode_init(current_cpu_datap()); /* cpu_mode_init() will be
                             * invoked on the APs
                             * via i386_init_slave()
                             */
    postcode(VSTART_EXIT);
    x86_init_wrapper(is_boot_cpu ?(uintptr_t) i386_init
                     : (uintptr_t) i386_init_slave,
             cpu_datap(cpu)->cpu_int_stack_top);
}

Vstart is an i386/x64 kernel initialization function that marks the conversion from assembly code to C code, and is also a special function because this function is executed on both the main CPU (boot CPU) and all slave Cpus or cores on the machine. Is_boot_cpu determines whether it is a primary or secondary CPU. The boot_args_start pointer parameter passed in from the CPU is NULL.
For the main CPU:
PE_Init_Platfrom (Platform expert) : initializes the global variable PE_state, which contains a copy of the boot parameters, the video parameters, and other parameters. In addition, there are the following functions:
Graphics Building the device tree (as described above)Parse Certain boot argumentsIdentify the machine (including processor and bus clock speeds)Initialize a “user interface” to be used in case of kernel Panics
finally through x86_init_wrapper(is_boot_cpu?(uintptr_t) i386_init : (uintptr_t) i386_init_slave, cpu_datap(cpu)-> cpu_int_stack_top); The function executes different initializers on and from the CPU, respectively.
2. Main CPU runs i386_init()(OSFMK \i386\ i386_inits.c)
The first is a bunch of initializations

    pal_i386_init();
    tsc_init();
    rtclock_early_init();   /* mach_absolute_time() now functionsl */

    kernel_debug_string_simple("i386_init");
    pstate_trace();

#if CONFIG_MCA
    /* Initialize machine-check handling */
    mca_cpu_init();
#endif

    master_cpu = 0;
    cpu_init();

    postcode(CPU_INIT_D);

    printf_init();          /* Init this in case we need debugger */
    panic_init();           /* Init this in case we need debugger */

Pal_i386_init: call Platfrom Abstraction, is a simple call to initialize the EFI lock
(2) PE_Init_Platfrom: initialize the global variable PE_state, including the copy of boot parameters, video parameters and other parameters. This function call pe_identify_platform() sets gPEClockFrequency
(3) kernel_early_bootstrap: Lck_mod_init () and timer_call_initialize(), which are used for timer calls
(4) cpu_init to set the current CPU clock timer dealIne to “EndOfAllTime” (end of all times), and after the clock is set to run forever, cpu_init() calls i386_active_cpu()
(5) printf_init: to call for the debugger. If the debugger is connected. The message entered by the kernel call printf() is redirected to the debugger
(6) panic_init: the call initializes the kernel crash redirect so that the sending of the kernel crash can be intercepted by the connected debugger
(7) PE_init_kprintf: the call makes kprintf() output to the console
(8) check serial console: check the “serial” boot parameters. If it’s set. The switch_to_serial_console()
(9) PE_Init_printf: call enables kprintf() to be printed out to the console
(10) 64-bit processor check: if the cpu-supported feature includes the CPUID_EXTFEATURE_EM64T flag, then enable this feature unless the “-legal” parameter
(11) i386_vm_init: Take over virtual memory management from EFI. Call pmap_bootstrap to initialize kernel physical memory map
last call

    /*   
     * VM initialization, after this we're using page tables...
     * Thn maximum number of cpus must be set beforehand.
     */
    kernel_debug_string_simple("i386_vm_init");
    i386_vm_init(maxmemtouse, IA32e, kernelBootArgs);

    /* create the console for verbose or pretty mode */
    /* Note: doing this prior to tsc_init() allows for graceful panic! */
    PE_init_platform(TRUE, kernelBootArgs);
    PE_create_console();

    kernel_debug_string_simple("power_management_init");
    power_management_init();
    processor_bootstrap();
    thread_bootstrap();

    pstate_trace();
    kernel_debug_string_simple("machine_startup");
    machine_startup();
    pstate_trace();

where the function of power_management_init() is to initialize a function in the structure cstateInit:

/* * dispatch table function that gets the power on installation
* Manage KEXT load.
*
* pmDispatch_t is a tool that the kernel can use to send the
* A set of functions called to the power management KEXT.
*
* pmCallBacks_t is the power management kext.
* A set of functions that can be called to obtain a specific kernel function.
* /

Translated with www.DeepL.com/Translator (free version)

typedef struct
{
    kern_return_t   (*pmCPUStateInit)(void);
    void        (*cstateInit)(void);
    uint64_t        (*MachineIdle)(uint64_t maxIdleDuration);
    uint64_t        (*GetDeadline)(x86_lcpu_t *lcpu);
    uint64_t        (*SetDeadline)(x86_lcpu_t *lcpu, uint64_t);
    void        (*Deadline)(x86_lcpu_t *lcpu);
    boolean_t       (*exitIdle)(x86_lcpu_t *lcpu);
    void        (*markCPURunning)(x86_lcpu_t *lcpu);
    int         (*pmCPUControl)(uint32_t cmd, void *datap);
    void        (*pmCPUHalt)(void);
    uint64_t        (*getMaxSnoop)(void);
    void        (*setMaxBusDelay)(uint64_t time);
    uint64_t        (*getMaxBusDelay)(void);
    void        (*setMaxIntDelay)(uint64_t time);
    uint64_t        (*getMaxIntDelay)(void);
    void        (*pmCPUSafeMode)(x86_lcpu_t *lcpu, uint32_t flags);
    void        (*pmTimerStateSave)(void);
    void        (*pmTimerStateRestore)(void);
    kern_return_t   (*exitHalt)(x86_lcpu_t *lcpu);
    kern_return_t   (*exitHaltToOff)(x86_lcpu_t *lcpu);
    void        (*markAllCPUsOff)(void);
    void        (*pmSetRunCount)(uint32_t count);
    boolean_t       (*pmIsCPUUnAvailable)(x86_lcpu_t *lcpu);
    int         (*pmChooseCPU)(int startCPU, int endCPU, int preferredCPU);
    int         (*pmIPIHandler)(void *state);
    void        (*pmThreadTellUrgency)(int urgency, uint64_t rt_period, uint64_t rt_deadline);
    void        (*pmActiveRTThreads)(boolean_t active);
    boolean_t           (*pmInterruptPrewakeApplicable)(void);
} pmDispatch_t;

(15) Processor_bootstrap: Initializes the Processor subsystem for Mach. This function initializes three queues: Task, terminated, and threads, creates master_processor objects, calls processor_init(), processor_init(), sets the field values in the processor data structure, and adds itself to the default processor group, pset0.
(16) thread_bootstrap: sets the template for the Mach thread object. The Mach thread structure has a number of fields. This function fills in the default values for these fields, then sets the first system thread, init_thread, which integrates all the values from the template, and then calls machine_set_current_thread() to flag the thread as active on the current CPU.
(17) machine_startup: initializes the next stage, never returning.
3. machine_startup()(osfmk\i386\at386\Model_dep.c)

void
machine_startup(void)
{
    int boot_arg;

#if 0
    if( PE_get_hotkey( kPEControlKey ))
            halt_in_debugger = halt_in_debugger ?0 : 1;
#endif

    if (PE_parse_boot_argn("debug", &debug_boot_arg, sizeof (debug_boot_arg))) {
        panicDebugging = TRUE;
#if DEVELOPMENT || DEBUG
        if (debug_boot_arg & DB_HALT) halt_in_debugger=1;
#endif
        if (debug_boot_arg & DB_PRT) disable_debug_output=FALSE; 
        if (debug_boot_arg & DB_SLOG) systemLogDiags=TRUE; 
        if (debug_boot_arg & DB_LOG_PI_SCRN) logPanicDataToScreen=TRUE;
#if KDEBUG_MOJO_TRACE
        if (debug_boot_arg & DB_PRT_KDEBUG) {
            kdebug_serial = TRUE;
            disable_debug_output = FALSE;
        }
#endif
    } else {
        debug_boot_arg = 0;
    }

    if (!PE_parse_boot_argn("nvram_paniclog", &commit_paniclog_to_nvram, sizeof (commit_paniclog_to_nvram)))
        commit_paniclog_to_nvram = 1;

    /*
     * Entering the debugger will put the CPUs into a "safe"
     * power mode.
     */
    if (PE_parse_boot_argn("pmsafe_debug", &boot_arg, sizeof (boot_arg)))
        pmsafe_debug = boot_arg;

#if NOTYET
    hw_lock_init(&debugger_lock);   /* initialize debugger lock */
#endif
    hw_lock_init(&pbtlock);     /* initialize print backtrace lock */

    if (PE_parse_boot_argn("preempt", &boot_arg, sizeof (boot_arg))) {
        default_preemption_rate = boot_arg;
    }
    if (PE_parse_boot_argn("unsafe", &boot_arg, sizeof (boot_arg))) {
        max_unsafe_quanta = boot_arg;
    }
    if (PE_parse_boot_argn("poll", &boot_arg, sizeof (boot_arg))) {
        max_poll_quanta = boot_arg;
    }
    if (PE_parse_boot_argn("yield", &boot_arg, sizeof (boot_arg))) {
        sched_poll_yield_shift = boot_arg;
    }
/* The I/O port to issue a read from, in the event of a panic. Useful for
 * triggering logic analyzers.
 */
    if (PE_parse_boot_argn("panic_io_port", &boot_arg, sizeof (boot_arg))) {
        /*I/O ports range from 0 through 0xFFFF */
        panic_io_port = boot_arg & 0xffff;
    }

    machine_conf();

    panic_hooks_init();

    /*
     * Start the system.
     */
    kernel_bootstrap();
    /*NOTREACHED*/
}

This function is primarily responsible for parsing the command-line arguments (the PE_parse_boot_argn function provided through Platform Expert), most of which are the boot-arg for debugging to control the debugging at boot time.
4.kernel_bootstrap()(osfmk\kern\Startup.c)


void
kernel_bootstrap(void)
{
    kern_return_t   result;
    thread_t    thread;
    char        namep[16];

    printf("%s\n", version); /* log kernel version */

    if (PE_parse_boot_argn("-l", namep, sizeof (namep))) /* leaks logging */
        turn_on_log_leaks = 1;

    PE_parse_boot_argn("trace", &new_nkdbufs, sizeof (new_nkdbufs));
    PE_parse_boot_argn("trace_wake", &wake_nkdbufs, sizeof (wake_nkdbufs));
    PE_parse_boot_argn("trace_panic", &write_trace_on_panic, sizeof(write_trace_on_panic));
    PE_parse_boot_argn("trace_typefilter", &trace_typefilter, sizeof(trace_typefilter));

    scale_setup();

    kernel_bootstrap_log("vm_mem_bootstrap");
    vm_mem_bootstrap();

    kernel_bootstrap_log("cs_init");
    cs_init();

    kernel_bootstrap_log("vm_mem_init");
    vm_mem_init();

    machine_info.memory_size = (uint32_t)mem_size;
    machine_info.max_mem = max_mem;
    machine_info.major_version = version_major;
    machine_info.minor_version = version_minor;


#if CONFIG_TELEMETRY
    kernel_bootstrap_log("telemetry_init");
    telemetry_init();
#endif

#if CONFIG_CSR
    kernel_bootstrap_log("csr_init");
    csr_init();
#endif

    if (PE_i_can_has_debugger(NULL) &&
        PE_parse_boot_argn("-show_pointers", &namep, sizeof (namep))) {
        doprnt_hide_pointers = FALSE;
    }

    kernel_bootstrap_log("console_init");
    console_init();

    kernel_bootstrap_log("stackshot_lock_init");    
    stackshot_lock_init();

    kernel_bootstrap_log("sched_init");
    sched_init();

    kernel_bootstrap_log("waitq_bootstrap");
    waitq_bootstrap();

    kernel_bootstrap_log("ipc_bootstrap");
    ipc_bootstrap();

#if CONFIG_MACF
    kernel_bootstrap_log("mac_policy_init");
    mac_policy_init();
#endif

    kernel_bootstrap_log("ipc_init");
    ipc_init();

    /*
     * As soon as the virtual memory system is up, we record
     * that this CPU is using the kernel pmap.
     */
    kernel_bootstrap_log("PMAP_ACTIVATE_KERNEL");
    PMAP_ACTIVATE_KERNEL(master_cpu);

    kernel_bootstrap_log("mapping_free_prime");
    mapping_free_prime();                       /* Load up with temporary mapping blocks */

    kernel_bootstrap_log("machine_init");
    machine_init();

    kernel_bootstrap_log("clock_init");
    clock_init();

    ledger_init();

    /*
     *  Initialize the IPC, task, and thread subsystems.
     */
#if CONFIG_COALITIONS
    kernel_bootstrap_log("coalitions_init");
    coalitions_init();
#endif

    kernel_bootstrap_log("task_init");
    task_init();

    kernel_bootstrap_log("thread_init");
    thread_init();

#if CONFIG_ATM
    /* Initialize the Activity Trace Resource Manager. */
    kernel_bootstrap_log("atm_init");
    atm_init();
#endif

#if CONFIG_BANK
    /* Initialize the BANK Manager. */
    kernel_bootstrap_log("bank_init");
    bank_init();
#endif

    /* initialize the corpse config based on boot-args */
    corpses_init();

    /*
     *  Create a kernel thread to execute the kernel bootstrap.
     */
    kernel_bootstrap_log("kernel_thread_create");
    result = kernel_thread_create((thread_continue_t)kernel_bootstrap_thread, NULL, MAXPRI_KERNEL, &thread);

    if (result != KERN_SUCCESS) panic("kernel_bootstrap: result = %08X\n", result);

    thread->state = TH_RUN;
    thread->last_made_runnable_time = mach_absolute_time();
    thread_deallocate(thread);

    kernel_bootstrap_log("load_context - done");
    load_context(thread);
    /*NOTREACHED*/
}

The kernel_bootstrap function continues to set and initialize the various core subsystems of the Mach kernel, establishing the necessary infrastructure on which BSD depends. In addition to virtual memory, kernel_bootstrap also initializes some key Mach abstractions:
IPC: IPC(interprocess communication) was the foundation of the Mach build, and IPC required some important resources, such as memory, synchronization objects, and the Mach Interface Generator (MIG)
clock: alarm (system alarm) and time (” calendar “)
thread: the thread was the actual unit of execution. A task is nothing more than a resource container, a thread that is actually scheduled and executed.
kernel_bootstrap function does not return. Kernel_bootstrap finally loads the context of the kernel_bootstrap_thread thread, which is the first active thread of the system. This thread will take over the initialization and handle more complex subsystems.
5.kernel_bootstrap_thread(void)(osfmk\kern\Startup.c)

/*
 * Now running in a thread.  Kick off other services,
 * invoke user bootstrap, enter pageout loop.
 */
static void
kernel_bootstrap_thread(void)
{
    processor_t     processor = current_processor();

#define kernel_bootstrap_thread_kprintf(x...) /* kprintf("kernel_bootstrap_thread: " x) */
    kernel_bootstrap_thread_log("idle_thread_create");
    /*
     * Create the idle processor thread.
     */
    idle_thread_create(processor);

    /*
     * N.B. Do not stick anything else
     * before this point.
     *
     * Start up the scheduler services.
     */
    kernel_bootstrap_thread_log("sched_startup");
    sched_startup();

    /*
     * Thread lifecycle maintenance (teardown, stack allocation)
     */
    kernel_bootstrap_thread_log("thread_daemon_init");
    thread_daemon_init();

    /* Create kernel map entry reserve */
    vm_kernel_reserved_entry_init();

    /*
     * Thread callout service.
     */
    kernel_bootstrap_thread_log("thread_call_initialize");
    thread_call_initialize();

    /*
     * Remain on current processor as
     * additional processors come online.
     */
    kernel_bootstrap_thread_log("thread_bind");
    thread_bind(processor);

    /*
     * Initialize ipc thread call support.
     */
    kernel_bootstrap_thread_log("ipc_thread_call_init");
    ipc_thread_call_init();

    /*
     * Kick off memory mapping adjustments.
     */
    kernel_bootstrap_thread_log("mapping_adjust");
    mapping_adjust();

    /*
     *  Create the clock service.
     */
    kernel_bootstrap_thread_log("clock_service_create");
    clock_service_create();

    /*
     *  Create the device service.
     */
    device_service_create();

    kth_started = 1;

#if (defined(__i386__) || defined(__x86_64__)) && NCOPY_WINDOWS > 0
    /*
     * Create and initialize the physical copy window for processor 0
     * This is required before starting kicking off  IOKit.
     */
    cpu_physwindow_init(0);
#endif



#if MACH_KDP 
    kernel_bootstrap_log("kdp_init");
    kdp_init();
#endif

#if ALTERNATE_DEBUGGER
    alternate_debugger_init();
#endif

#if KPC
    kpc_init();
#endif

#if CONFIG_ECC_LOGGING
    ecc_log_init();
#endif 

#if KPERF
    kperf_bootstrap();
#endif

#if HYPERVISOR
    hv_support_init();
#endif

#if CONFIG_TELEMETRY
    kernel_bootstrap_log("bootprofile_init");
    bootprofile_init();
#endif

#if (defined(__i386__) || defined(__x86_64__)) && CONFIG_VMX
    vmx_init();
#endif

#if (defined(__i386__) || defined(__x86_64__))
    if (kdebug_serial) {
        new_nkdbufs = 1;
        if (trace_typefilter == 0)
            trace_typefilter = 1;
    }
    if (turn_on_log_leaks && !new_nkdbufs)
        new_nkdbufs = 200000;
    if (trace_typefilter)
        start_kern_tracing_with_typefilter(new_nkdbufs,
                           FALSE,
                           trace_typefilter);
    else
        start_kern_tracing(new_nkdbufs, FALSE);
    if (turn_on_log_leaks)
        log_leaks = 1;

#endif

    kernel_bootstrap_log("prng_init");
    prng_cpu_init(master_cpu);

#ifdef  IOKIT
    PE_init_iokit();
#endif

    assert(ml_get_interrupts_enabled() == FALSE);
    (void) spllo();     /* Allow interruptions */

#if (defined(__i386__) || defined(__x86_64__)) && NCOPY_WINDOWS > 0
    /*
     * Create and initialize the copy window for processor 0
     * This also allocates window space for all other processors.
     * However, this is dependent on the number of processors - so this call
     * must be after IOKit has been started because IOKit performs processor
     * discovery.
     */
    cpu_userwindow_init(0);
#endif

#if (!defined(__i386__) && !defined(__x86_64__))
    if (turn_on_log_leaks && !new_nkdbufs)
        new_nkdbufs = 200000;
    if (trace_typefilter)
        start_kern_tracing_with_typefilter(new_nkdbufs, FALSE, trace_typefilter);
    else
        start_kern_tracing(new_nkdbufs, FALSE);
    if (turn_on_log_leaks)
        log_leaks = 1;
#endif

    /*
     *  Initialize the shared region module.
     */
    vm_shared_region_init();
    vm_commpage_init();
    vm_commpage_text_init();

#if CONFIG_MACF
    kernel_bootstrap_log("mac_policy_initmach");
    mac_policy_initmach();
#endif


#if CONFIG_SCHED_SFI
    kernel_bootstrap_log("sfi_init");
    sfi_init();
#endif

    /*
     * Initialize the globals used for permuting kernel
     * addresses that may be exported to userland as tokens
     * using VM_KERNEL_ADDRPERM()/VM_KERNEL_ADDRPERM_EXTERNAL().
     * Force the random number to be odd to avoid mapping a non-zero
     * word-aligned address to zero via addition.
     * Note: at this stage we can use the cryptographically secure PRNG
     * rather than early_random().
     */
    read_random(&vm_kernel_addrperm, sizeof(vm_kernel_addrperm));
    vm_kernel_addrperm |= 1;
    read_random(&buf_kernel_addrperm, sizeof(buf_kernel_addrperm));
    buf_kernel_addrperm |= 1;
    read_random(&vm_kernel_addrperm_ext, sizeof(vm_kernel_addrperm_ext));
    vm_kernel_addrperm_ext |= 1;

    vm_set_restrictions();



    /*
     *  Start the user bootstrap.
     */
#ifdef  MACH_BSD
    bsd_init();
#endif

    /*
     * Get rid of segments used to bootstrap kext loading. This removes
     * the KLD, PRELINK symtab, LINKEDIT, and symtab segments/load commands.
     */
    OSKextRemoveKextBootstrap();

    serial_keyboard_init();     /* Start serial keyboard if wanted */

    vm_page_init_local_q();

    thread_bind(PROCESSOR_NULL);

    /*
     *  Become the pageout daemon.
     */
    vm_pageout();
    /*NOTREACHED*/
}

The main thread starts running as the kernel_bootstrap_thread thread, whose job is still to initialize the various subsystems.
6.bsd_init()(bsd\kern\Bsd_init.c)


    /* Initialize signal state for process 0. */
    bsd_init_kprintf("calling siginit\n");
    siginit(kernproc);

    bsd_init_kprintf("calling bsd_utaskbootstrap\n");
    bsd_utaskbootstrap();

#if defined(__LP64__)
    kernproc->p_flag |= P_LP64;
#endif

The entire BSD layer of XNU is initialized by a function called bSD_init (). A lot of work is done in this function. The function’s job is to initialize each subsystem at once.
7.bsd_utaskbootstrap()(bsd\kern\Bsd_init.c)

void
bsd_utaskbootstrap(void)
{
    thread_t thread;
    struct uthread *ut;

    /*
     * Clone the bootstrap process from the kernel process, without
     * inheriting either task characteristics or memory from the kernel;
     */
    thread = cloneproc(TASK_NULL, COALITION_NULL, kernproc, FALSE, TRUE);

    /* Hold the reference as it will be dropped during shutdown */
    initproc = proc_find(1);                
#if __PROC_INTERNAL_DEBUG
    if (initproc == PROC_NULL)
        panic("bsd_utaskbootstrap: initproc not set\n");
#endif
    /*
     * Since we aren't going back out the normal way to our parent,
     * we have to drop the transition locks explicitly.
     */
    proc_signalend(initproc, 0);
    proc_transend(initproc, 0);

    ut = (struct uthread *)get_bsdthread_info(thread);
    ut->uu_sigmask = 0;
    act_set_astbsd(thread);
    proc_clear_return_wait(initproc, thread);
}

This function is responsible for indirectly starting PID 1, the first process to enter user mode. To do this, bSD_utaskBootstrap () first calls CloneProc (), creating a new Mach task. To actually create a new task, utaskbootstrap() calls act_set_astbsd() to the created thread, generates an asynchronous system trap(AST), then calls thread_resume(), and utaskbootstrap() returns to bsd_init().
8. i386_astintr()(osfmk\i386\Trap.c)

/*
 * Handle AST traps for i386.
 */

extern void     log_thread_action (thread_t, char *);

void
i386_astintr(int preemption)
{
    ast_t       mask = AST_ALL;
    spl_t       s;

    if (preemption)
            mask = AST_PREEMPTION;

    s = splsched();

    ast_taken(mask, s);

    splx(s);
}

Ast_taken () function :(osfmk\kern\ ast.c)

        /*
         * The kernel preempt traps
         * skip all other ASTs.
         */
        if (!preempt_trap) {
            ml_set_interrupts_enabled(enable);

#ifdef  MACH_BSD
            /*
             * Handle BSD hook.
             */
            if (reasons & AST_BSD) {
                thread_ast_clear(thread, AST_BSD);
                bsd_ast(thread);
            }
#endif
#if CONFIG_MACF
            /*
             * Handle MACF hook.
             */
            if (reasons & AST_MACF) {
                thread_ast_clear(thread, AST_MACF);
                mac_thread_userret(thread);
            }
#endif

Bsd_ast () function (OSFMK \kern\ kern_sig.c)

    if (!bsd_init_done) {
        bsd_init_done = 1;
        bsdinit_task();
    }

9. bsdinit_task()(bsd\kern\Bsd_init.c)

void
bsdinit_task(void)
{
    proc_t p = current_proc();
    struct uthread *ut;
    thread_t thread;

    process_name("init", p);

    ux_handler_init();

    thread = current_thread();
    (void) host_set_exception_ports(host_priv_self(),
                    EXC_MASK_ALL & ~(EXC_MASK_RPC_ALERT),//pilotfish (shark) needs this port
                    (mach_port_t) ux_exception_port,
                    EXCEPTION_DEFAULT| MACH_EXCEPTION_CODES,
                    0);

    ut = (uthread_t)get_bsdthread_info(thread);

    bsd_init_task = get_threadtask(thread);
    init_task_died = FALSE;

#if CONFIG_MACF
    mac_cred_label_associate_user(p->p_ucred);
#endif
    load_init_program(p);
    lock_trace = 1;
}

The name of the initial process is set to init, followed by a call to un_handler_init(), which creates a separate kernel thread, un_handler, that handles UNIX exceptions. Finally, load_init_program() is called. Load_init_program () is responsible for turning a process with PID 1 into what is known as Launchd. The flow of this thread is now fully in user mode.
10. load_init_program()(bsd\kern\Kern_exec.c)

/*
 * load_init_program
 *
 * Description: Load the "init" program; in most cases, this will be "launchd"
 *
 * Parameters:  p           Process to call execve() to create
 *                  the "init" program
 *
 * Returns: (void)
 *
 * Notes:   The process that is passed in is the first manufactured
 *      process on the system, and gets here via bsd_ast() firing
 *      for the first time.  This is done to ensure that bsd_init()
 *      has run to completion.
 *
 *      In DEBUG & DEVELOPMENT builds, the launchdsuffix boot-arg
 *      may be used to select a specific launchd executable. As with
 *      the kcsuffix boot-arg, setting launchdsuffix to "" or "release"
 *      will force /sbin/launchd to be selected.
 *
 *      The DEBUG kernel will continue to check for a .development
 *      version until <rdar://problem/17931977> is fixed.
 *
 *              Search order by build:
 *
 * DEBUG    DEVELOPMENT RELEASE     PATH
 * ----------------------------------------------------------------------------------
 * 1        1       NA      /usr/local/sbin/launchd.$LAUNCHDSUFFIX
 * 2        NA      NA      /usr/local/sbin/launchd.debug
 * 3        2       NA      /usr/local/sbin/launchd.development
 * 4        3       1       /sbin/launchd
 */
void
load_init_program(proc_t p)
{
    uint32_t i;
    int error;
    vm_offset_t scratch_addr = VM_MIN_ADDRESS;

    (void) vm_allocate(current_map(), &scratch_addr, PAGE_SIZE, VM_FLAGS_ANYWHERE);
#if CONFIG_MEMORYSTATUS && CONFIG_JETSAM
    (void) memorystatus_init_at_boot_snapshot();
#endif /* CONFIG_MEMORYSTATUS && CONFIG_JETSAM */

#if DEBUG || DEVELOPMENT
    /* Check for boot-arg suffix first */
    char launchd_suffix[64];
    if (PE_parse_boot_argn("launchdsuffix", launchd_suffix, sizeof(launchd_suffix))) {
        char launchd_path[128];
        boolean_t is_release_suffix = ((launchd_suffix[0] == 0) ||
                           (strcmp(launchd_suffix, "release") == 0));

        if (is_release_suffix) {
            error = load_init_program_at_path(p, CAST_USER_ADDR_T(scratch_addr), "/sbin/launchd");
            if (!error)
                return;

            panic("Process 1 exec of launchd.release failed, errno %d", error);
        } else {
            strlcpy(launchd_path, "/usr/local/sbin/launchd.", sizeof(launchd_path));
            strlcat(launchd_path, launchd_suffix, sizeof(launchd_path));

            /* All the error data is lost in the loop below, don't
             * attempt to save it. */
            if (!load_init_program_at_path(p, CAST_USER_ADDR_T(scratch_addr), launchd_path)) {
                return;
            }
        }
    }
#endif

    error = ENOENT;
    for (i = 0; i < sizeof(init_programs)/sizeof(init_programs[0]); i++) {
        error = load_init_program_at_path(p, CAST_USER_ADDR_T(scratch_addr), init_programs[i]);
        if (!error)
            return;
    }

    panic("Process 1 exec of %s failed, errno %d", ((i == 0) ?"<null>" : init_programs[i-1]), error);
}

Start these programs:

static const char * init_programs[] = {
#if DEBUG
    "/usr/local/sbin/launchd.debug",
#endif
#if DEVELOPMENT || DEBUG
    /* Remove DEBUG conditional when <rdar://problem/17931977> is fixed */
    "/usr/local/sbin/launchd.development",
#endif
    "/sbin/launchd",
};

Also:
XNU has many boot parameters, and there are usually two ways to pass parameters to the kernel
Through the NVRAM, use the boot – args parameter passing (parameters can be set by a command NVRAM)
through/Library/Prefrences/SystemConfihguration/com. Apple. The boot. The file. This is a standard property list file where parameters can be specified in the kernel_flags element