650e70aa56e6d9b97433f12de2f7953a21c95695
[AROS.git] / rom / dos / cliinit.c
blob650e70aa56e6d9b97433f12de2f7953a21c95695
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: English
7 */
9 #include <aros/debug.h>
10 #include <exec/resident.h>
11 #include <proto/exec.h>
12 #include <proto/arossupport.h>
13 #include <libraries/expansion.h>
14 #include <libraries/expansionbase.h>
15 #include <resources/filesysres.h>
17 #include <ctype.h>
19 #include "dos_intern.h"
21 #ifdef DEBUG_DOSTYPE
23 static void PRINT_DOSTYPE(ULONG dt)
25 unsigned int i;
27 bug("Dos/CliInit: DosType is ");
29 for (i = 0; i < 4; i++)
31 unsigned char c = dt >> (24 - i * 8);
33 if (isprint(c))
34 RawPutChar(c);
35 else
36 bug("\\%02X", c);
39 RawPutChar('\n');
42 #else
44 #define PRINT_DOSTYPE(dt)
46 #endif
48 static long internalBootCliHandler(void);
50 /*****************************************************************************
52 NAME */
53 #include <dos/dosextens.h>
54 #include <proto/dos.h>
56 AROS_LH1(IPTR, CliInit,
58 /* SYNOPSIS */
59 AROS_LHA(struct DosPacket *, dp, A0),
61 /* LOCATION */
62 struct DosLibrary *, DOSBase, 154, Dos)
64 /* FUNCTION
66 Set up the first shell process.
68 Currently, no DOS Packet arguments are used by this
69 routine.
71 A new Boot Cli is process is created, and 'dp' is
72 sent to it. If the boot shell succeeds, then 'dp'
73 is returned with dp_Res1 = DOSTRUE.
74 has started.
76 INPUTS
78 dp -- startup arguments specified as a packet
80 RESULT
82 RETURN_OK on success, ERROR_* (from dp_Res2) on failure.
84 NOTES
86 This function is internal to AROS, and should never be
87 called by user space.
89 EXAMPLE
91 BUGS
93 SEE ALSO
95 INTERNALS
97 *****************************************************************************/
99 AROS_LIBFUNC_INIT
101 struct MsgPort *mp, *reply_mp;
102 struct DosPacket *my_dp = NULL;
103 SIPTR Res1, Res2;
104 BPTR seg;
106 /* Create a DOS Process to handle this, since
107 * we're probably being called from a Task context
109 if (dp == NULL) {
110 /* This call does *not* require that
111 * we be a DOS Process. Luckily.
113 my_dp = AllocDosObject(DOS_STDPKT, NULL);
114 dp = my_dp;
117 if (dp == NULL)
118 return ERROR_NO_FREE_STORE;
120 reply_mp = CreateMsgPort();
121 if (reply_mp == NULL) {
122 if (my_dp)
123 FreeDosObject(DOS_STDPKT, my_dp);
124 return ERROR_NO_FREE_STORE;
127 seg = CreateSegList(internalBootCliHandler);
129 mp = CreateProc("Boot Mount", 0, seg, AROS_STACKSIZE);
130 if (mp == NULL) {
131 DeleteMsgPort(reply_mp);
132 if (my_dp)
133 FreeDosObject(DOS_STDPKT, my_dp);
134 /* A best guess... */
135 UnLoadSeg(seg);
136 return ERROR_NO_FREE_STORE;
139 /* Preload the reply with failure */
140 dp->dp_Res1 = DOSFALSE;
141 dp->dp_Res2 = ERROR_NOT_IMPLEMENTED;
143 /* Again, doesn't require this Task to be a Process */
144 SendPkt(dp, mp, reply_mp);
146 /* Wait for the message from the Boot Cli */
147 WaitPort(reply_mp);
148 GetMsg(reply_mp);
150 /* We know that if we're received a reply packet,
151 * that we've been able to execute the handler,
152 * therefore we can dispense with the 'CreateSegment'
153 * stub.
155 UnLoadSeg(seg);
157 Res1 = dp->dp_Res1;
158 Res2 = dp->dp_Res2;
160 if (my_dp)
161 FreeDosObject(DOS_STDPKT, my_dp);
163 DeleteMsgPort(reply_mp);
165 D(bug("Dos/CliInit: Task returned Res1=%ld, Res2=%ld\n", Res1, Res2));
167 /* Did we succeed? */
168 if (Res1 == DOSTRUE)
169 return RETURN_OK;
170 /* Make sure we return non-zero error code, 0 == RETURN_OK */
171 if (Res2 == 0)
172 Res2 = ERROR_UNKNOWN;
174 return Res2;
176 AROS_LIBFUNC_EXIT
177 } /* CliInit */
179 /* Find the most recent version of the matching filesystem */
180 static struct FileSysEntry *internalMatchFileSystemResourceHandler(struct FileSysResource *fsr, ULONG DosType)
182 struct FileSysEntry *fse, *best_fse = NULL;
184 ForeachNode(&fsr->fsr_FileSysEntries, fse)
186 if (fse->fse_DosType == DosType)
188 if (fse->fse_PatchFlags & (FSEF_HANDLER | FSEF_SEGLIST | FSEF_TASK))
190 if (best_fse == NULL || fse->fse_Version > best_fse->fse_Version)
192 best_fse = fse;
198 return best_fse;
201 /* See if the BootNode's DeviceNode needs to be patched by
202 * an entry in FileSysResource
204 * If de->de_DosType == 0, and no dn_SegList nor dn_Handler,
205 * then the node uses the 'defseg' handler.
207 static void internalPatchBootNode(struct FileSysResource *fsr, struct DeviceNode *dn, BPTR defseg)
209 struct FileSysStartupMsg *fssm;
210 struct DosEnvec *de;
211 struct FileSysEntry *fse;
213 /* If we already have a task installed,
214 * then we're done.
216 if (dn->dn_Task != NULL)
217 return;
219 /* If we already have a handler installed,
220 * then we're done.
222 if (dn->dn_SegList != BNULL)
223 return;
225 fssm = BADDR(dn->dn_Startup);
226 if (fssm == NULL)
227 return;
229 de = BADDR(fssm->fssm_Environ);
230 if (de == NULL)
231 return;
233 /* If the DosType is 0 and dn_Handler == BNULL, use the default handler */
234 if (de->de_DosType == 0 && dn->dn_Handler == BNULL)
236 D(bug("Dos/CliInit: Neither DosType nor Handler specified, using default filesystem\n"));
237 dn->dn_SegList = defseg;
238 dn->dn_GlobalVec = (BPTR)-1;
239 return;
242 /* If no FileSysResource, nothing to do */
243 if (fsr == NULL) {
244 D(bug("Dos/CliInit: No FileSystem.resource, not patching DeviceNode %p\n", dn));
245 return;
248 D(bug("Dos/CliInit: Looking for patches for DeviceNode %p\n", dn));
251 * internalMatchFileSystemResourceHandler looks up the filesystem
253 fse = internalMatchFileSystemResourceHandler(fsr, de->de_DosType);
254 if (fse != NULL)
256 D(bug("Dos/CliInit: found 0x%p in FileSystem.resource\n", fse));
257 PRINT_DOSTYPE(fse->fse_DosType);
259 dn->dn_SegList = fse->fse_SegList;
260 /* other fse_PatchFlags bits are quite pointless */
261 if (fse->fse_PatchFlags & FSEF_TASK)
262 dn->dn_Task = (APTR)fse->fse_Task;
263 if (fse->fse_PatchFlags & FSEF_LOCK)
264 dn->dn_Lock = fse->fse_Lock;
266 /* Adjust the stack size for 64-bits if needed.
268 if (fse->fse_PatchFlags & FSEF_STACKSIZE)
269 dn->dn_StackSize = (fse->fse_StackSize/sizeof(ULONG))*sizeof(IPTR);
270 if (fse->fse_PatchFlags & FSEF_PRIORITY)
271 dn->dn_Priority = fse->fse_Priority;
272 if (fse->fse_PatchFlags & FSEF_GLOBALVEC)
273 dn->dn_GlobalVec = fse->fse_GlobalVec;
277 static struct MsgPort *mountBootNode(struct DeviceNode *dn, struct FileSysResource *fsr, struct DosLibrary *DOSBase)
279 struct DosList *dl;
281 if ((dn == NULL) || (dn->dn_Name == BNULL))
282 return NULL;
284 D(bug("Dos/CliInit: Mounting 0x%p (%b)...\n", dn, dn->dn_Name));
286 /* Check if the device is already in DOS list */
287 dl = LockDosList(LDF_DEVICES | LDF_READ);
289 while ((dl = NextDosEntry(dl, LDF_DEVICES)))
291 if (dl == (struct DosList *)dn)
292 break;
295 UnLockDosList(LDF_DEVICES | LDF_READ);
297 /* Found in DOS list? Do nothing. */
298 if (dl)
300 return dl->dol_Task;
303 /* Patch it up, if needed */
304 internalPatchBootNode(fsr, dn, DOSBase->dl_Root->rn_FileHandlerSegment);
306 if (!dn->dn_Handler && !dn->dn_SegList)
308 /* Don't know how to mount? Error... */
309 return NULL;
312 if (AddDosEntry((struct DosList *)dn) != DOSFALSE)
315 * Do not check for ANDF_STARTPROC because:
316 * a) On the Amiga ADNF_STARTPROC was not present in KS 1.3 and earlier, there was no deferred mount.
317 * b) In fact if we have something in ExpansionBase, we for sure want it to be mounted.
319 D(bug("Dos/CliInit: Added to DOS list, starting up handler... "));
321 if (RunHandler(dn, NULL, DOSBase))
323 D(bug("dn->dn_Task = 0x%p\n", dn->dn_Task));
324 return dn->dn_Task;
327 D(bug("Failed\n"));
328 RemDosEntry((struct DosList *)dn);
332 * TODO: AddDosEntry() can fail in case of duplicate name. In this case it would be useful
333 * to append some suffix. AmigaOS IIRC did the same.
334 * This appears to be needed if you have DH0:. DH1:, etc, on your hard drive, and want to
335 * connect a friend's hard drive which also has partitions with these names.
338 return NULL;
341 static BPTR internalBootLock(struct DosLibrary *DOSBase, struct ExpansionBase *ExpansionBase, struct FileSysResource *fsr)
343 struct BootNode *bn;
344 struct DeviceNode *dn;
345 struct MsgPort *mp;
346 BPTR lock = BNULL;
347 STRPTR name;
348 int name_len;
350 /* The first BootNode off of the MountList.
351 * The dosboot.resource strap routine will have
352 * made the desired boot node the first in this list.
354 * If this fails to mount, we will fail, which will
355 * cause dos.library to fail to initialize, and
356 * then dosboot.resource will handle checking the
357 * next device in the list.
359 bn = (struct BootNode *)GetHead(&ExpansionBase->MountList);
360 D(bug("Dos/CliInit: MountList head: 0x%p\n", bn));
362 if (bn == NULL)
363 return BNULL;
365 dn = bn->bn_DeviceNode;
366 mp = mountBootNode(dn, fsr, DOSBase);
367 if (!mp)
368 return BNULL;
370 D(bug("Dos/CliInit: %b (%d) appears usable\n", dn->dn_Name, bn->bn_Node.ln_Pri));
372 /* Try to find a Lock for 'name:' */
373 name_len = AROS_BSTR_strlen(dn->dn_Name);
374 name = AllocVec(name_len + 2, MEMF_ANY);
375 if (name != NULL)
377 SIPTR err = 0;
379 /* Make the volume name a volume: name */
380 CopyMem(AROS_BSTR_ADDR(dn->dn_Name), name, name_len);
381 name[name_len+0] = ':';
382 name[name_len+1] = 0;
383 D(bug("Dos/CliInit: Attempt to Lock(\"%s\")... ", name));
385 lock = Lock(name, SHARED_LOCK);
386 D(bug("=> 0x%p\n", BADDR(lock)));
388 if (lock != BNULL)
390 /* If we have a lock, check the per-platform conditional boot code. */
391 if (!__dos_IsBootable(DOSBase, lock))
393 UnLock(lock);
394 lock = BNULL;
395 err = ERROR_OBJECT_WRONG_TYPE; /* Something to more or less reflect "This disk is not bootable" */
398 else
400 err = IoErr();
403 if (!lock)
405 SIPTR dead;
407 /* Darn. Not bootable. Try to unmount it. */
408 D(bug("Dos/CliInit: Does not have a bootable filesystem, unmounting...\n"));
410 /* It's acceptable if this fails */
411 dead = DoPkt(mp, ACTION_DIE, 0, 0, 0, 0, 0);
412 D(bug("Dos/CliInit: ACTION_DIE returned %ld\n", dead));
414 if (dead)
417 * Handlers usually won't remove their DeviceNoces themselves.
418 * And even if they do (ACTION_DIE is poorly documented), RemDosEntry()
419 * on an already removed entry is safe due to nature of DOS list.
420 * What is really prohibited, it's unloading own seglist. Well, resident
421 * handlers will never do it, they know...
423 RemDosEntry((struct DosList *)dn);
424 dn->dn_Task = NULL;
426 /* DoPkt() clobbered IoErr() */
427 SetIoErr(err);
430 FreeVec(name);
433 return lock;
436 static void AddBootAssign(CONST_STRPTR path, CONST_STRPTR assign, APTR DOSBase)
438 BPTR lock;
439 if (!(lock = Lock(path, SHARED_LOCK)))
440 lock = Lock("SYS:", SHARED_LOCK);
441 if (lock)
442 AssignLock(assign, lock);
447 * This is what actually gets the Lock() for SYS:,
448 * sets up the boot assigns, and starts the
449 * startup sequence.
451 static long internalBootCliHandler(void)
453 struct ExpansionBase *ExpansionBase;
454 struct DosLibrary *DOSBase;
455 struct MsgPort *mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
456 BPTR lock;
457 struct DosPacket *dp;
458 IPTR BootFlags;
459 UBYTE Flags;
460 struct BootNode *bn, *tmpbn;
461 struct FileSysResource *fsr;
463 /* Ah. A DOS Process context. At last! */
464 WaitPort(mp);
465 dp = (struct DosPacket *)(GetMsg(mp)->mn_Node.ln_Name);
467 DOSBase = (APTR)OpenLibrary("dos.library", 0);
468 if (DOSBase == NULL) {
469 D(bug("Dos/CliInit: Impossible! Where did dos.library go?\n"));
470 Alert(AT_DeadEnd | AG_OpenLib | AO_DOSLib);
473 ExpansionBase = (APTR)OpenLibrary("expansion.library", 0);
474 if (!ExpansionBase)
475 return ERROR_INVALID_RESIDENT_LIBRARY;
477 /* It's perfectly fine if this fails. */
478 fsr = OpenResource("FileSystem.resource");
480 /* Find and Lock the proposed boot device */
481 lock = internalBootLock(DOSBase, ExpansionBase, fsr);
482 D(bug("Dos/CliInit: Proposed SYS: lock is: %p\n", BADDR(lock)));
484 if (lock == BNULL)
487 * We've failed. Inform our parent and exit.
488 * Immediately after we reply the packet, the parent (Boot Task) can expunge DOSBase.
489 * This is why we first cleanup, then use internal_ReplyPkt (DOSBase is considered
490 * invalid).
491 * Alternatively we could Forbid() before ReplyPkt(), but... Forbid() is so unpolite...
493 IPTR err = IoErr();
495 CloseLibrary(&ExpansionBase->LibNode);
496 CloseLibrary(&DOSBase->dl_lib);
498 /* Immediately after ReplyPkt() DOSBase can be freed. So Forbid() until we really quit. */
499 internal_ReplyPkt(dp, mp, DOSFALSE, err);
500 return err;
503 /* Ok, at this point we've succeeded. Inform our parent. */
504 ReplyPkt(dp, DOSTRUE, 0);
506 /* We're now at the point of no return. */
507 DOSBase->dl_Root->rn_BootProc = ((struct FileLock*)BADDR(lock))->fl_Task;
508 SetFileSysTask(DOSBase->dl_Root->rn_BootProc);
510 AssignLock("SYS", lock);
511 lock = Lock("SYS:", SHARED_LOCK);
512 if (lock == BNULL)
514 D(bug("DOS/CliInit: Impossible! The SYS: assign failed!\n"));
515 Alert(AT_DeadEnd | AG_BadParm | AN_DOSLib);
518 AddBootAssign("SYS:C", "C", DOSBase);
519 AddBootAssign("SYS:Libs", "LIBS", DOSBase);
520 AddBootAssign("SYS:Devs", "DEVS", DOSBase);
521 AddBootAssign("SYS:L", "L", DOSBase);
522 AddBootAssign("SYS:S", "S", DOSBase);
523 AddBootAssign("SYS:Fonts", "FONTS", DOSBase);
525 #if !(mc68000)
526 /* Let hidds in DRIVERS: directory be found by OpenLibrary */
527 if ((lock = Lock("DEVS:Drivers", SHARED_LOCK)) != BNULL) {
528 AssignLock("DRIVERS", lock);
529 AssignAdd("LIBS", lock);
532 * This early assignment prevents Poseidon from asking for ENV:
533 * when popup GUI process is initialized and opens muimaster.library.
534 * On m68k this harms, Workbench 1.x disks fail to boot correctly with
535 * "Can't cancel ENV:" warning.
536 * FIXME: Fix muimaster.library at last, and forget this hack.
538 AssignLate("ENV", "SYS:Prefs/Env-Archive");
539 #endif
540 AssignLate("ENVARC", "SYS:Prefs/Env-Archive");
543 * At this point we have only SYS:, nothing more. Mount the rest.
544 * We do it after assigning SYS: because in some cases we can have
545 * BootNodes with handler name but no seglist (Poseidon could add them for example).
546 * This means the handler needs to be loaded from disk (fat-handler for example).
547 * Here we can already do it.
549 D(bug("Dos/CliInit: Assigns done, mount remaining handlers...\n"));
551 BootFlags = ExpansionBase->eb_BootFlags;
552 Flags = ExpansionBase->Flags;
553 D(bug("Dos/CliInit: BootFlags 0x%x Flags 0x%x\n", BootFlags, Flags));
555 ForeachNodeSafe(&ExpansionBase->MountList, bn, tmpbn)
558 * Don't check for return code. Failed is failed.
559 * One of failure reasons can be missing handler specification for some DOSType.
560 * In this case the DeviceNode will not be mounted, but it will stay in
561 * ExpansionBase->MountList. It can be picked up by disk-based program which would
562 * read mappings for disk-based handlers from file.
563 * This way we could automount e. g. FAT, NTFS, EXT3/2, whatever else, partitions.
565 mountBootNode(bn->bn_DeviceNode, fsr, DOSBase);
568 CloseLibrary((APTR)ExpansionBase);
570 /* Init all the RTF_AFTERDOS code, since we now have SYS:, the dos devices, and all the other assigns */
571 D(bug("Dos/CliInit: Calling InitCode(RTF_AFTERDOS, 0)\n"));
572 InitCode(RTF_AFTERDOS, 0);
574 /* Call the platform-overridable portions */
575 D(bug("Dos/CliInit: Calling __dos_Boot(%p, 0x%x, 0x%x)\n", DOSBase, BootFlags, Flags));
576 __dos_Boot(DOSBase, BootFlags, Flags);
578 D(bug("Dos/CliInit: Boot sequence exited\n"));
579 CloseLibrary((APTR)DOSBase);
581 /* And exit... */
582 return RETURN_OK;