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