xref: /xnu-11215/iokit/bsddev/IOKitBSDInit.cpp (revision 8149afcc)
1 /*
2  * Copyright (c) 1998-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 #include <IOKit/IOBSD.h>
23 #include <IOKit/IOLib.h>
24 #include <IOKit/IOService.h>
25 #include <IOKit/IODeviceTreeSupport.h>
26 #include <IOKit/IOKitKeys.h>
27 #include <IOKit/IOPlatformExpert.h>
28 
29 #include <sys/disklabel.h>
30 
31 extern "C" {
32 
33 #include <pexpert/pexpert.h>
34 #include <kern/clock.h>
35 
36 // how long to wait for matching root device, secs
37 #define ROOTDEVICETIMEOUT	60
38 
39 
40 kern_return_t
41 IOKitBSDInit( void )
42 {
43     IOLog("IOKitBSDInit\n");
44 
45     IOService::publishResource("IOBSD");
46 
47     return( kIOReturnSuccess );
48 }
49 
50 OSDictionary * IOBSDNameMatching( const char * name )
51 {
52     OSDictionary *	dict;
53     const OSSymbol *	str = 0;
54 
55     do {
56 
57 	dict = IOService::serviceMatching( gIOServiceKey );
58 	if( !dict)
59 	    continue;
60         str = OSSymbol::withCString( name );
61 	if( !str)
62 	    continue;
63         dict->setObject( kIOBSDNameKey, (OSObject *) str );
64         str->release();
65 
66         return( dict );
67 
68     } while( false );
69 
70     if( dict)
71 	dict->release();
72     if( str)
73 	str->release();
74 
75     return( 0 );
76 }
77 
78 OSDictionary * IOCDMatching( const char * name )
79 {
80     OSDictionary *	dict;
81     const OSSymbol *	str;
82 
83 	dict = IOService::serviceMatching( "IOMedia" );
84 	if( dict == 0 ) {
85 	   IOLog("Unable to find IOMedia\n");
86 	   return 0;
87 	}
88 
89 	str = OSSymbol::withCString( "CD_ROM_Mode_1" );
90 	if( str == 0 ) {
91 	    dict->release();
92 	    return 0;
93 	}
94 
95 	dict->setObject( "Content", (OSObject *)str );
96 	str->release();
97         return( dict );
98 }
99 
100 OSDictionary * IONetworkMatching(  const char * path,
101 				   char * buf, int maxLen )
102 {
103     OSDictionary *	matching = 0;
104     OSDictionary *	dict;
105     OSString *		str;
106     char *		comp;
107     const char *	skip;
108     int			len;
109 
110     do {
111 
112 	len = strlen( kIODeviceTreePlane ":" );
113 	maxLen -= len;
114 	if( maxLen < 0)
115 	    continue;
116 
117 	strcpy( buf, kIODeviceTreePlane ":" );
118 	comp = buf + len;
119 
120         // remove parameters following ':' from the path
121         skip = strchr( path, ':');
122 	if( !skip)
123 	    continue;
124 
125         len = skip - path;
126 	maxLen -= len;
127 	if( maxLen < 0)
128 	    continue;
129         strncpy( comp, path, len );
130         comp[ len ] = 0;
131 
132 	matching = IOService::serviceMatching( "IONetworkInterface" );
133 	if( !matching)
134 	    continue;
135 	dict = IOService::addLocation( matching );
136 	if( !dict)
137 	    continue;
138 
139 	str = OSString::withCString( buf );
140 	if( !str)
141 	    continue;
142         dict->setObject( kIOPathMatchKey, str );
143 	str->release();
144 
145 	return( matching );
146 
147     } while( false );
148 
149     if( matching)
150         matching->release();
151 
152     return( 0 );
153 }
154 
155 OSDictionary * IONetworkNamePrefixMatching( const char * prefix )
156 {
157     OSDictionary *	 matching;
158     OSDictionary *   propDict = 0;
159     const OSSymbol * str      = 0;
160 
161     do {
162         matching = IOService::serviceMatching( "IONetworkInterface" );
163         if ( matching == 0 )
164             continue;
165 
166         propDict = OSDictionary::withCapacity(1);
167         if ( propDict == 0 )
168             continue;
169 
170         str = OSSymbol::withCString( prefix );
171         if ( str == 0 )
172             continue;
173 
174         propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str );
175         str->release();
176         str = 0;
177 
178         if ( matching->setObject( gIOPropertyMatchKey,
179                                   (OSObject *) propDict ) != true )
180             continue;
181 
182         propDict->release();
183         propDict = 0;
184 
185         return( matching );
186 
187     } while ( false );
188 
189     if ( matching ) matching->release();
190     if ( propDict ) propDict->release();
191     if ( str      ) str->release();
192 
193     return( 0 );
194 }
195 
196 static bool IORegisterNetworkInterface( IOService * netif )
197 {
198     // A network interface is typically named and registered
199     // with BSD after receiving a request from a user space
200     // "namer". However, for cases when the system needs to
201     // root from the network, this registration task must be
202     // done inside the kernel and completed before the root
203     // device is handed to BSD.
204 
205     IOService *    stack;
206     OSNumber *     zero    = 0;
207     OSString *     path    = 0;
208     OSDictionary * dict    = 0;
209     char *         pathBuf = 0;
210     int            len;
211     enum { kMaxPathLen = 512 };
212 
213     do {
214         stack = IOService::waitForService(
215                 IOService::serviceMatching("IONetworkStack") );
216         if ( stack == 0 ) break;
217 
218         dict = OSDictionary::withCapacity(3);
219         if ( dict == 0 ) break;
220 
221         zero = OSNumber::withNumber((UInt64) 0, 32);
222         if ( zero == 0 ) break;
223 
224         pathBuf = (char *) IOMalloc( kMaxPathLen );
225         if ( pathBuf == 0 ) break;
226 
227         len = kMaxPathLen;
228         if ( netif->getPath( pathBuf, &len, gIOServicePlane )
229              == false ) break;
230 
231         path = OSString::withCStringNoCopy( pathBuf );
232         if ( path == 0 ) break;
233 
234         dict->setObject( "IOInterfaceUnit", zero );
235         dict->setObject( kIOPathMatchKey,   path );
236 
237         stack->setProperties( dict );
238     }
239     while ( false );
240 
241     if ( zero ) zero->release();
242     if ( path ) path->release();
243     if ( dict ) dict->release();
244     if ( pathBuf ) IOFree(pathBuf, kMaxPathLen);
245 
246 	return ( netif->getProperty( kIOBSDNameKey ) != 0 );
247 }
248 
249 OSDictionary * IODiskMatching( const char * path, char * buf, int maxLen )
250 {
251     const char * look;
252     const char * alias;
253     char *       comp;
254     long         unit = -1;
255     long         partition = -1;
256     char         c;
257 
258     // scan the tail of the path for "@unit:partition"
259     do {
260         // Have to get the full path to the controller - an alias may
261         // tell us next to nothing, like "hd:8"
262         alias = IORegistryEntry::dealiasPath( &path, gIODTPlane );
263 
264         look = path + strlen( path);
265         c = ':';
266         while( look != path) {
267             if( *(--look) == c) {
268                 if( c == ':') {
269                     partition = strtol( look + 1, 0, 0 );
270                     c = '@';
271                 } else if( c == '@') {
272                     unit = strtol( look + 1, 0, 16 );
273                     c = '/';
274                 } else if( c == '/') {
275                     c = 0;
276                     break;
277                 }
278             }
279 
280 	        if( alias && (look == path)) {
281                 path = alias;
282                 look = path + strlen( path);
283                 alias = 0;
284             }
285         }
286         if( c || unit == -1 || partition == -1)
287             continue;
288 
289         maxLen -= strlen( "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" );
290         maxLen -= ( alias ? strlen( alias ) : 0 ) + (look - path);
291         maxLen -= strlen( "/@hhhhhhhh:dddddddddd';}" );
292 
293         if( maxLen > 0) {
294             sprintf( buf, "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" );
295             comp = buf + strlen( buf );
296 
297             if( alias) {
298                 strcpy( comp, alias );
299                 comp += strlen( alias );
300             }
301 
302             if ( (look - path)) {
303                 strncpy( comp, path, look - path);
304                 comp += look - path;
305             }
306 
307             sprintf( comp, "/@%lx:%ld';}", unit, partition );
308         } else
309             continue;
310 
311         return( OSDynamicCast(OSDictionary, OSUnserialize( buf, 0 )) );
312 
313     } while( false );
314 
315     return( 0 );
316 }
317 
318 OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen )
319 {
320     /* need to look up path, get device type,
321         call matching help based on device type */
322 
323     return( IODiskMatching( path, buf, maxLen ));
324 
325 }
326 
327 kern_return_t IOFindBSDRoot( char * rootName,
328 				dev_t * root, u_int32_t * oflags )
329 {
330     mach_timespec_t	t;
331     IOService *		service;
332     IORegistryEntry *	regEntry;
333     OSDictionary *	matching = 0;
334     OSString *		iostr;
335     OSNumber *		off;
336     OSData *		data = 0;
337 
338     UInt32		flags = 0;
339     int			minor, major;
340     char *		rdBootVar;
341     enum {		kMaxPathBuf = 512, kMaxBootVar = 128 };
342     char *		str;
343     const char *	look = 0;
344     int			len;
345     bool		forceNet = false;
346     bool		debugInfoPrintedOnce = false;
347 
348     static int		mountAttempts = 0;
349 
350     if( mountAttempts++)
351 	IOSleep( 5 * 1000 );
352 
353     str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar );
354     if( !str)
355 	return( kIOReturnNoMemory );
356     rdBootVar = str + kMaxPathBuf;
357 
358     if (!PE_parse_boot_arg("rd", rdBootVar )
359      && !PE_parse_boot_arg("rootdev", rdBootVar ))
360 	rdBootVar[0] = 0;
361 
362     do {
363         if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) {
364 	    data = (OSData *) regEntry->getProperty( "rootpath" );
365 	    regEntry->release();
366 	    if( data)
367 	    continue;
368 	}
369         if( (regEntry = IORegistryEntry::fromPath( "/options", gIODTPlane ))) {
370 	    data = (OSData *) regEntry->getProperty( "boot-file" );
371 	    regEntry->release();
372 	    if( data)
373 	    continue;
374 	}
375     } while( false );
376 
377     if( data)
378         look = (const char *) data->getBytesNoCopy();
379 
380     if( rdBootVar[0] == '*') {
381         look = rdBootVar + 1;
382 	forceNet = false;
383     } else {
384         if( (regEntry = IORegistryEntry::fromPath( "/", gIODTPlane ))) {
385             forceNet = (0 != regEntry->getProperty( "net-boot" ));
386 	    regEntry->release();
387 	}
388     }
389 
390     if( look) {
391 	// from OpenFirmware path
392 	IOLog("From path: \"%s\", ", look);
393 
394 	if( forceNet || (0 == strncmp( look, "enet", strlen( "enet" ))) ) {
395             matching = IONetworkMatching( look, str, kMaxPathBuf );
396         } else {
397             matching = IODiskMatching( look, str, kMaxPathBuf );
398         }
399     }
400 
401     if( (!matching) && rdBootVar[0] ) {
402 	// by BSD name
403 	look = rdBootVar;
404 	if( look[0] == '*')
405 	    look++;
406 
407 	if ( strncmp( look, "en", strlen( "en" )) == 0 ) {
408 	    matching = IONetworkNamePrefixMatching( "en" );
409 	} else if ( strncmp( look, "cdrom", strlen( "cdrom" )) == 0 ) {
410 	    matching = IOCDMatching( look );
411 	} else {
412 	    matching = IOBSDNameMatching( look );
413 	}
414     }
415 
416     if( !matching) {
417         OSString * astring;
418 	// any UFS
419         matching = IOService::serviceMatching( "IOMedia" );
420         astring = OSString::withCStringNoCopy("Apple_UFS");
421         if ( astring ) {
422             matching->setObject("Content", astring);
423             astring->release();
424         }
425     }
426 
427     if( true && matching) {
428         OSSerialize * s = OSSerialize::withCapacity( 5 );
429 
430         if( matching->serialize( s )) {
431             IOLog( "Waiting on %s\n", s->text() );
432             s->release();
433         }
434     }
435 
436     do {
437         t.tv_sec = ROOTDEVICETIMEOUT;
438         t.tv_nsec = 0;
439 	matching->retain();
440         service = IOService::waitForService( matching, &t );
441 	if( (!service) || (mountAttempts == 10)) {
442             PE_display_icon( 0, "noroot");
443             IOLog( "Still waiting for root device\n" );
444 
445             if( !debugInfoPrintedOnce) {
446                 debugInfoPrintedOnce = true;
447                 if( gIOKitDebug & kIOLogDTree) {
448                     IOLog("\nDT plane:\n");
449                     IOPrintPlane( gIODTPlane );
450                 }
451                 if( gIOKitDebug & kIOLogServiceTree) {
452                     IOLog("\nService plane:\n");
453                     IOPrintPlane( gIOServicePlane );
454                 }
455                 if( gIOKitDebug & kIOLogMemory)
456                     IOPrintMemory();
457             }
458 	}
459     } while( !service);
460     matching->release();
461 
462     major = 0;
463     minor = 0;
464 
465     // If the IOService we matched to is a subclass of IONetworkInterface,
466     // then make sure it has been registered with BSD and has a BSD name
467     // assigned.
468 
469     if ( service
470     &&   service->metaCast( "IONetworkInterface" )
471     &&   !IORegisterNetworkInterface( service ) )
472     {
473         service = 0;
474     }
475 
476     if( service) {
477 
478 	len = kMaxPathBuf;
479 	service->getPath( str, &len, gIOServicePlane );
480 	IOLog( "Got boot device = %s\n", str );
481 
482 	iostr = (OSString *) service->getProperty( kIOBSDNameKey );
483 	if( iostr)
484 	    strcpy( rootName, iostr->getCStringNoCopy() );
485 	off = (OSNumber *) service->getProperty( kIOBSDMajorKey );
486 	if( off)
487 	    major = off->unsigned32BitValue();
488 	off = (OSNumber *) service->getProperty( kIOBSDMinorKey );
489 	if( off)
490 	    minor = off->unsigned32BitValue();
491 
492 	if( service->metaCast( "IONetworkInterface" ))
493 	    flags |= 1;
494 
495     } else {
496 
497 	IOLog( "Wait for root failed\n" );
498         strcpy( rootName, "en0");
499         flags |= 1;
500     }
501 
502     IOLog( "BSD root: %s", rootName );
503     if( major)
504 	IOLog(", major %d, minor %d\n", major, minor );
505     else
506 	IOLog("\n");
507 
508     *root = makedev( major, minor );
509     *oflags = flags;
510 
511     IOFree( str,  kMaxPathBuf + kMaxBootVar );
512 
513     if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) {
514 
515 	IOService::getPlatform()->waitQuiet();
516         if( gIOKitDebug & kIOLogDTree) {
517             IOLog("\nDT plane:\n");
518             IOPrintPlane( gIODTPlane );
519         }
520         if( gIOKitDebug & kIOLogServiceTree) {
521             IOLog("\nService plane:\n");
522             IOPrintPlane( gIOServicePlane );
523         }
524         if( gIOKitDebug & kIOLogMemory)
525             IOPrintMemory();
526     }
527 
528     return( kIOReturnSuccess );
529 }
530 
531 } /* extern "C" */
532