|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * Mach Operating System
27: * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
28: * All Rights Reserved.
29: *
30: * Permission to use, copy, modify and distribute this software and its
31: * documentation is hereby granted, provided that both the copyright
32: * notice and this permission notice appear in all copies of the
33: * software, derivative works or modified versions, and any portions
34: * thereof, and that both notices appear in supporting documentation.
35: *
36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
39: *
40: * Carnegie Mellon requests users of this software to return to
41: *
42: * Software Distribution Coordinator or [email protected]
43: * School of Computer Science
44: * Carnegie Mellon University
45: * Pittsburgh PA 15213-3890
46: *
47: * any improvements or extensions that they make and grant Carnegie Mellon
48: * the rights to redistribute these changes.
49: */
50: /*
51: */
52: /*
53: * File: kern/machine.c
54: * Author: Avadis Tevanian, Jr.
55: * Date: 1987
56: *
57: * Support for machine independent machine abstraction.
58: */
59:
60: #include <cpus.h>
61: #include <mach_host.h>
62:
63: #include <string.h>
64: #include <mach/boolean.h>
65: #include <mach/kern_return.h>
66: #include <mach/mach_types.h>
67: #include <mach/machine.h>
68: #include <mach/host_info.h>
69: #include <mach/host_reboot.h>
70: #include <kern/counters.h>
71: #include <kern/cpu_data.h>
72: #include <kern/ipc_host.h>
73: #include <kern/host.h>
74: #include <kern/lock.h>
75: #include <kern/machine.h>
76: #include <kern/processor.h>
77: #include <kern/queue.h>
78: #include <kern/sched.h>
79: #include <kern/task.h>
80: #include <kern/thread.h>
81: #include <kern/thread_swap.h>
82: #include <kern/misc_protos.h>
83:
84: #include <mach/mach_host_server.h>
85:
86:
87: /*
88: * Exported variables:
89: */
90:
91: struct machine_info machine_info;
92: struct machine_slot machine_slot[NCPUS];
93:
94: queue_head_t action_queue; /* assign/shutdown queue */
95: decl_simple_lock_data(,action_lock)
96:
97: static thread_call_t processor_action_call;
98:
99: /* Forwards */
100: processor_set_t processor_request_action(
101: processor_t processor,
102: processor_set_t new_pset);
103:
104: void processor_doaction(
105: processor_t processor);
106:
107: void processor_doshutdown(
108: processor_t processor);
109:
110: int thread_stop_freeze(
111: thread_t thread,
112: processor_set_t pset);
113:
114: /*
115: * cpu_up:
116: *
117: * Flag specified cpu as up and running. Called when a processor comes
118: * online.
119: */
120: void
121: cpu_up(
122: int cpu)
123: {
124: processor_t processor;
125: struct machine_slot *ms;
126: spl_t s;
127:
128: processor = cpu_to_processor(cpu);
129: /*
130: * Can't risk blocking with no current thread established.
131: * Just twiddle our thumbs; we've got nothing better to do
132: * yet, anyway.
133: */
134: while (!pset_lock_try(&default_pset))
135: continue;
136:
137: s = splsched();
138: processor_lock(processor);
139: #if NCPUS > 1
140: init_ast_check(processor);
141: #endif /* NCPUS > 1 */
142: ms = &machine_slot[cpu];
143: ms->running = TRUE;
144: machine_info.avail_cpus++;
145: pset_add_processor(&default_pset, processor);
146: processor->state = PROCESSOR_RUNNING;
147: processor_unlock(processor);
148: splx(s);
149: pset_unlock(&default_pset);
150: }
151:
152: /*
153: * cpu_down:
154: *
155: * Flag specified cpu as down. Called when a processor is about to
156: * go offline.
157: */
158:
159: void
160: cpu_down(
161: int cpu)
162: {
163: processor_t processor;
164: struct machine_slot *ms;
165: spl_t s;
166:
167: processor = cpu_to_processor(cpu);
168:
169: s = splsched();
170: processor_lock(processor);
171: ms = &machine_slot[cpu];
172: ms->running = FALSE;
173: machine_info.avail_cpus--;
174: /*
175: * processor has already been removed from pset.
176: */
177: processor->processor_set_next = PROCESSOR_SET_NULL;
178: processor->state = PROCESSOR_OFF_LINE;
179: processor_unlock(processor);
180: splx(s);
181: }
182:
183: kern_return_t
184: host_reboot(
185: host_t host,
186: int options)
187: {
188: if (host == HOST_NULL)
189: return (KERN_INVALID_HOST);
190:
191: if (options & HOST_REBOOT_DEBUGGER) {
192: Debugger("Debugger");
193: }
194: else
195: halt_all_cpus(!(options & HOST_REBOOT_HALT));
196:
197: return (KERN_SUCCESS);
198: }
199:
200: #if NCPUS > 1
201: /*
202: * processor_request_action - common internals of processor_assign
203: * and processor_shutdown. If new_pset is null, this is
204: * a shutdown, else it's an assign and caller must donate
205: * a reference. For assign operations, it returns
206: * an old pset that must be deallocated if it's not NULL. For
207: * shutdown operations, it always returns PROCESSOR_SET_NULL.
208: */
209: processor_set_t
210: processor_request_action(
211: processor_t processor,
212: processor_set_t new_pset)
213: {
214: register processor_set_t pset, old_next_pset;
215:
216: /*
217: * Processor must be in a processor set. Must lock its idle lock to
218: * get at processor state.
219: */
220: pset = processor->processor_set;
221: simple_lock(&pset->idle_lock);
222:
223: /*
224: * If the processor is dispatching, let it finish - it will set its
225: * state to running very soon.
226: */
227: while (*(volatile int *)&processor->state == PROCESSOR_DISPATCHING)
228: continue;
229:
230: /*
231: * Now lock the action queue and do the dirty work.
232: */
233: simple_lock(&action_lock);
234:
235: switch (processor->state) {
236:
237: case PROCESSOR_IDLE:
238: /*
239: * Remove from idle queue.
240: */
241: queue_remove(&pset->idle_queue,
242: processor, processor_t, processor_queue);
243: pset->idle_count--;
244:
245: /* fall through ... */
246: case PROCESSOR_RUNNING:
247: /*
248: * Put it on the action queue.
249: */
250: queue_enter(&action_queue,
251: processor, processor_t,processor_queue);
252:
253: /* fall through ... */
254: case PROCESSOR_ASSIGN:
255: /*
256: * And ask the action_thread to do the work.
257: */
258:
259: if (new_pset == PROCESSOR_SET_NULL) {
260: processor->state = PROCESSOR_SHUTDOWN;
261: old_next_pset = PROCESSOR_SET_NULL;
262: }
263: else {
264: processor->state = PROCESSOR_ASSIGN;
265: old_next_pset = processor->processor_set_next;
266: processor->processor_set_next = new_pset;
267: }
268: break;
269:
270: default:
271: printf("state: %d\n", processor->state);
272: panic("processor_request_action: bad state");
273: }
274:
275: simple_unlock(&action_lock);
276: simple_unlock(&pset->idle_lock);
277:
278: thread_call_enter(processor_action_call);
279:
280: return (old_next_pset);
281: }
282:
283: #if MACH_HOST
284: /*
285: * processor_assign() changes the processor set that a processor is
286: * assigned to. Any previous assignment in progress is overriden.
287: * Synchronizes with assignment completion if wait is TRUE.
288: */
289: kern_return_t
290: processor_assign(
291: processor_t processor,
292: processor_set_t new_pset,
293: boolean_t wait)
294: {
295: register processor_set_t old_next_pset;
296: spl_t s;
297:
298: /*
299: * Check for null arguments.
300: * XXX Can't assign master processor.
301: */
302: if ( processor == PROCESSOR_NULL ||
303: new_pset == PROCESSOR_SET_NULL ||
304: processor == master_processor)
305: return (KERN_FAILURE);
306:
307: /*
308: * Get pset reference to donate to processor_request_action.
309: */
310: pset_reference(new_pset);
311:
312: s = splsched();
313: processor_lock(processor);
314: if ( processor->state == PROCESSOR_OFF_LINE ||
315: processor->state == PROCESSOR_SHUTDOWN) {
316: /*
317: * Already shutdown or being shutdown -- Can't reassign.
318: */
319: processor_unlock(processor);
320: splx(s);
321: pset_deallocate(new_pset);
322:
323: return (KERN_FAILURE);
324: }
325:
326: old_next_pset = processor_request_action(processor, new_pset);
327:
328:
329: /*
330: * Synchronization with completion.
331: */
332: while ( wait && (processor->state == PROCESSOR_ASSIGN ||
333: processor->state == PROCESSOR_SHUTDOWN)) {
334: assert_wait((event_t)processor, THREAD_ABORTSAFE);
335: processor_unlock(processor);
336: splx(s);
337:
338: thread_block((void (*)(void)) 0);
339:
340: s = splsched();
341: processor_lock(processor);
342: }
343:
344: processor_unlock(processor);
345: splx(s);
346:
347: if (old_next_pset != PROCESSOR_SET_NULL)
348: pset_deallocate(old_next_pset);
349:
350: return (KERN_SUCCESS);
351: }
352:
353: #else /* MACH_HOST */
354:
355: kern_return_t
356: processor_assign(
357: processor_t processor,
358: processor_set_t new_pset,
359: boolean_t wait)
360: {
361: #ifdef lint
362: processor++; new_pset++; wait++;
363: #endif /* lint */
364: return (KERN_FAILURE);
365: }
366:
367: #endif /* MACH_HOST */
368:
369: /*
370: * processor_shutdown() queues a processor up for shutdown.
371: * Any assignment in progress is overriden. It does not synchronize
372: * with the shutdown (can be called from interrupt level).
373: */
374: kern_return_t
375: processor_shutdown(
376: processor_t processor)
377: {
378: spl_t s;
379:
380: s = splsched();
381: processor_lock(processor);
382: if ( processor->state == PROCESSOR_OFF_LINE ||
383: processor->state == PROCESSOR_SHUTDOWN) {
384: /*
385: * Already shutdown or being shutdown -- nothing to do.
386: */
387: processor_unlock(processor);
388: splx(s);
389:
390: return (KERN_SUCCESS);
391: }
392:
393: (void) processor_request_action(processor, PROCESSOR_SET_NULL);
394: processor_unlock(processor);
395: splx(s);
396:
397: return (KERN_SUCCESS);
398: }
399:
400: /*
401: * processor_action() shuts down processors or changes their assignment.
402: */
403:
404: static thread_call_data_t processor_action_call_data;
405:
406: void
407: processor_action(void)
408: {
409: register processor_t processor;
410: spl_t s;
411:
412: s = splsched();
413: simple_lock(&action_lock);
414:
415: if (processor_action_call == NULL) {
416: thread_call_setup(&processor_action_call_data,
417: processor_action, NULL);
418: processor_action_call = &processor_action_call_data;
419: }
420:
421: while (!queue_empty(&action_queue)) {
422: processor = (processor_t) queue_first(&action_queue);
423: queue_remove(&action_queue, processor, processor_t, processor_queue);
424: simple_unlock(&action_lock);
425: splx(s);
426:
427: processor_doaction(processor);
428:
429: s = splsched();
430: simple_lock(&action_lock);
431: }
432:
433: simple_unlock(&action_lock);
434: splx(s);
435: }
436:
437: /*
438: * thread_stop_freeze
439: * Block the thread in the kernel and freeze the processor set.
440: * return value:
441: * TRUE - the thread has blocked interruptibly, is stopped, and
442: * the processor set assignment is frozen
443: * FALSE - the thread is no longer in the processor set, so it
444: * isn't stopped, and the processor set assignment
445: * is released.
446: */
447: int
448: thread_stop_freeze(
449: thread_t thread,
450: processor_set_t pset)
451: {
452: #if 0 /* HACK ALERT!!!! (TEST/DEBUG) */
453: thread_act_t thr_act;
454: spl_t s;
455:
456: /*
457: * hold it, and wait for it to stop.
458: */
459: thr_act = thread_lock_act(thread);
460: thread_hold(thr_act);
461: act_unlock_thread(thr_act);
462:
463: thread_stop(thread);
464:
465: s = splsched();
466: wake_lock(thread);
467: while( thread->state & (TH_RUN|TH_UNINT) ) {
468: thread->wake_active = TRUE;
469: assert_wait((event_t)&thread->wake_active, THREAD_UNINT);
470: wake_unlock(thread);
471: splx(s);
472: thread_block( (void (*)(void)) 0 );
473: (void) splsched();
474: wake_lock(thread);
475: }
476:
477: /*
478: * Now, the thread has blocked uninterruptibly; freeze the
479: * assignment and make sure it's still part of the processor set.
480: */
481: wake_unlock(thread);
482: thread_freeze(thread);
483: thread_lock(thread);
484:
485: /*
486: * if the processor set has changed, release the freeze and
487: * then unstop it.
488: */
489: if( thread->processor_set != pset ) {
490: thread_unlock(thread);
491: splx(s);
492: thread_unfreeze(thread);
493: thread_unstop(thread);
494: return FALSE;
495: }
496: thread_unlock(thread);
497: splx(s);
498: #endif /* HACK ALERT!!!! */
499:
500: return TRUE;
501: }
502:
503: /*
504: * processor_doaction actually does the shutdown. The trick here
505: * is to schedule ourselves onto a cpu and then save our
506: * context back into the runqs before taking out the cpu.
507: */
508:
509: void
510: processor_doaction(
511: processor_t processor)
512: {
513: thread_t this_thread;
514: spl_t s;
515: register processor_set_t pset;
516: #if MACH_HOST
517: register processor_set_t new_pset;
518: register thread_t thread;
519: register thread_t prev_thread = THREAD_NULL;
520: thread_act_t thr_act;
521: boolean_t have_pset_ref = FALSE;
522: #endif /* MACH_HOST */
523:
524: /*
525: * Get onto the processor to shutdown
526: */
527: this_thread = current_thread();
528: thread_bind(this_thread, processor);
529: thread_block((void (*)(void)) 0);
530:
531: pset = processor->processor_set;
532: #if MACH_HOST
533: /*
534: * If this is the last processor in the processor_set,
535: * stop all the threads first.
536: */
537: pset_lock(pset);
538: if (pset->processor_count == 1) {
539: thread = (thread_t) queue_first(&pset->threads);
540: prev_thread = THREAD_NULL;
541: pset->ref_count++;
542: have_pset_ref = TRUE;
543: pset->empty = TRUE;
544:
545: /*
546: * loop through freezing the processor set assignment
547: * and reference counting the threads;
548: */
549: while (!queue_end(&pset->threads, (queue_entry_t) thread)) {
550: thread_reference(thread);
551: pset_unlock(pset);
552:
553: /*
554: * Freeze the thread on the processor set.
555: * If it's moved, just release the reference.
556: * Get the next thread in the processor set list
557: * from the last one which was frozen.
558: */
559: if (thread_stop_freeze(thread, pset))
560: prev_thread = thread;
561: else
562: thread_deallocate(thread);
563:
564: pset_lock(pset);
565: if (prev_thread != THREAD_NULL)
566: thread = (thread_t)queue_next(&prev_thread->pset_threads);
567: else
568: thread = (thread_t) queue_first(&pset->threads);
569: }
570:
571: /*
572: * Remove the processor from the set so that when the threads
573: * are unstopped below the ones blocked in the kernel don't
574: * start running again.
575: */
576: s = splsched();
577: processor_lock(processor);
578: pset_remove_processor(pset, processor);
579:
580: /*
581: * Prevent race with another processor being added to the set
582: * See code after Restart_pset:
583: * while(new_pset->empty && new_pset->processor_count > 0)
584: *
585: * ... it tests for the condition where a new processor is
586: * added to the set while the last one is still being removed.
587: */
588: pset->processor_count++; /* block new processors being added */
589: assert(pset->processor_count == 1);
590:
591: /*
592: * Release the thread assignment locks, unstop the threads and
593: * release the thread references which were taken above.
594: */
595: thread = (thread_t) queue_first(&pset->threads);
596: while(!queue_empty(&pset->threads) && (thread != THREAD_NULL)) {
597: prev_thread = thread;
598: if (queue_end(&pset->threads, (queue_entry_t) thread))
599: thread = THREAD_NULL;
600: else
601: thread = (thread_t) queue_next(&prev_thread->pset_threads);
602:
603: pset_unlock(pset);
604: thread_unfreeze(prev_thread);
605: thread_unstop(prev_thread);
606: thread_deallocate(prev_thread);
607: pset_lock(pset);
608: }
609: /*
610: * allow a processor to be added to the empty pset
611: */
612: pset->processor_count--;
613: }
614: else {
615: /* not last processor in set */
616: #endif /* MACH_HOST */
617: /*
618: * At this point, it is ok to rm the processor from the pset.
619: */
620: s = splsched();
621: processor_lock(processor);
622: pset_remove_processor(pset, processor);
623: #if MACH_HOST
624: }
625: pset_unlock(pset);
626:
627: /*
628: * Copy the next pset pointer into a local variable and clear
629: * it because we are taking over its reference.
630: */
631: new_pset = processor->processor_set_next;
632: processor->processor_set_next = PROCESSOR_SET_NULL;
633:
634: if (processor->state == PROCESSOR_ASSIGN) {
635:
636: Restart_pset:
637: /*
638: * Nasty problem: we want to lock the target pset, but
639: * we have to enable interrupts to do that which requires
640: * dropping the processor lock. While the processor
641: * is unlocked, it could be reassigned or shutdown.
642: */
643: processor_unlock(processor);
644: splx(s);
645:
646: /*
647: * Lock target pset and handle remove last / assign first race.
648: * Only happens if there is more than one action thread.
649: */
650: pset_lock(new_pset);
651: while (new_pset->empty && new_pset->processor_count > 0) {
652: pset_unlock(new_pset);
653: while ( *(volatile boolean_t *)&new_pset->empty &&
654: *(volatile int *)&new_pset->processor_count > 0)
655: continue;
656: pset_lock(new_pset);
657: }
658:
659: /*
660: * Finally relock the processor and see if something changed.
661: * The only possibilities are assignment to a different pset
662: * and shutdown.
663: */
664: s = splsched();
665: processor_lock(processor);
666:
667: if (processor->state == PROCESSOR_SHUTDOWN) {
668: pset_unlock(new_pset);
669: goto shutdown; /* will release pset reference */
670: }
671:
672: if (processor->processor_set_next != PROCESSOR_SET_NULL) {
673: /*
674: * Processor was reassigned. Drop the reference
675: * we have on the wrong new_pset, and get the
676: * right one. Involves lots of lock juggling.
677: */
678: processor_unlock(processor);
679: splx(s);
680: pset_unlock(new_pset);
681: pset_deallocate(new_pset);
682:
683: s = splsched();
684: processor_lock(processor);
685: new_pset = processor->processor_set_next;
686: processor->processor_set_next = PROCESSOR_SET_NULL;
687: goto Restart_pset;
688: }
689:
690: /*
691: * If the pset has been deactivated since the operation
692: * was requested, redirect to the default pset.
693: */
694: if (!new_pset->active) {
695: pset_unlock(new_pset);
696: pset_deallocate(new_pset);
697: new_pset = &default_pset;
698: apset_lock(new_pset);
699: new_pset->ref_count++;
700: }
701:
702: /*
703: * Do assignment, then wakeup anyone waiting for it.
704: * Finally context switch to have it take effect.
705: */
706: pset_add_processor(new_pset, processor);
707: if (new_pset->empty) {
708: /*
709: * Set all the threads loose
710: */
711: thread = (thread_t) queue_first(&new_pset->threads);
712: while (!queue_end(&new_pset->threads,(queue_entry_t)thread)) {
713: thr_act = thread_lock_act(thread);
714: thread_release(thread->top_act);
715: act_unlock_thread(thr_act);
716: thread = (thread_t) queue_next(&thread->pset_threads);
717: }
718:
719: new_pset->empty = FALSE;
720: }
721: processor->processor_set_next = PROCESSOR_SET_NULL;
722: processor->state = PROCESSOR_RUNNING;
723: thread_wakeup((event_t)processor);
724: processor_unlock(processor);
725: splx(s);
726: pset_unlock(new_pset);
727:
728: /*
729: * Clean up dangling references, and release our binding.
730: */
731: pset_deallocate(new_pset);
732: if (have_pset_ref)
733: pset_deallocate(pset);
734:
735: if (prev_thread != THREAD_NULL)
736: thread_deallocate(prev_thread);
737:
738: thread_bind(this_thread, PROCESSOR_NULL);
739:
740: thread_block((void (*)(void)) 0);
741: return;
742: }
743:
744: shutdown:
745: #endif /* MACH_HOST */
746:
747: /*
748: * Do shutdown, make sure we live when processor dies.
749: */
750: if (processor->state != PROCESSOR_SHUTDOWN) {
751: printf("state: %d\n", processor->state);
752: panic("action_thread -- bad processor state");
753: }
754: processor_unlock(processor);
755: /*
756: * Clean up dangling references, and release our binding.
757: */
758: #if MACH_HOST
759: if (new_pset != PROCESSOR_SET_NULL)
760: pset_deallocate(new_pset);
761: if (have_pset_ref)
762: pset_deallocate(pset);
763: if (prev_thread != THREAD_NULL)
764: thread_deallocate(prev_thread);
765: #endif /* MACH_HOST */
766:
767: thread_bind(this_thread, PROCESSOR_NULL);
768: switch_to_shutdown_context(this_thread, processor_doshutdown, processor);
769: splx(s);
770: }
771:
772: /*
773: * Actually do the processor shutdown. This is called at splsched,
774: * running on the processor's shutdown stack.
775: */
776:
777: void
778: processor_doshutdown(
779: processor_t processor)
780: {
781: register int cpu = processor->slot_num;
782:
783: timer_switch(&kernel_timer[cpu]);
784:
785: /*
786: * OK, now exit this cpu.
787: */
788: PMAP_DEACTIVATE_KERNEL(cpu);
789: cpu_data[cpu].active_thread = THREAD_NULL;
790: active_kloaded[cpu] = THR_ACT_NULL;
791: cpu_down(cpu);
792: thread_wakeup((event_t)processor);
793: halt_cpu();
794: panic("zombie processor");
795:
796: /*
797: * The action thread returns to life after the call to
798: * switch_to_shutdown_context above, on some other cpu.
799: */
800:
801: /*NOTREACHED*/
802: }
803: #else /* NCPUS > 1 */
804:
805: kern_return_t
806: processor_assign(
807: processor_t processor,
808: processor_set_t new_pset,
809: boolean_t wait)
810: {
811: #ifdef lint
812: processor++; new_pset++; wait++;
813: #endif /* lint */
814: return (KERN_FAILURE);
815: }
816:
817: #endif /* NCPUS > 1 */
818:
819: kern_return_t
820: host_get_boot_info(
821: host_t priv_host,
822: kernel_boot_info_t boot_info)
823: {
824: char *src = "";
825: extern char *machine_boot_info(
826: kernel_boot_info_t boot_info,
827: vm_size_t buf_len);
828:
829: if (priv_host == HOST_NULL)
830: return (KERN_INVALID_HOST);
831:
832: /*
833: * Copy first operator string terminated by '\0' followed by
834: * standardized strings generated from boot string.
835: */
836: src = machine_boot_info(boot_info, KERNEL_BOOT_INFO_MAX);
837: if (src != boot_info)
838: (void) strncpy(boot_info, src, KERNEL_BOOT_INFO_MAX);
839:
840: return (KERN_SUCCESS);
841: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.