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