update copyright
[AROS.git] / rom / dos / cliinit.c
blob8a2de56a88f6a2043e1130c1af37bb444d4d0ab6
1 /*
2 Copyright © 1995-2014, 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"
20 #include "../expansion/expansion_intern.h"
22 #ifdef DEBUG_DOSTYPE
24 static void PRINT_DOSTYPE(ULONG dt)
26 unsigned int i;
28 bug("Dos/CliInit: DosType is ");
30 for (i = 0; i < 4; i++)
32 unsigned char c = dt >> (24 - i * 8);
34 if (isprint(c))
35 RawPutChar(c);
36 else
37 bug("\\%02X", c);
40 RawPutChar('\n');
43 #else
45 #define PRINT_DOSTYPE(dt)
47 #endif
49 static LONG internalBootCliHandler(void);
51 /*****************************************************************************
53 NAME */
54 #include <dos/dosextens.h>
55 #include <proto/dos.h>
57 AROS_LH1(IPTR, CliInit,
59 /* SYNOPSIS */
60 AROS_LHA(struct DosPacket *, dp, A0),
62 /* LOCATION */
63 struct DosLibrary *, DOSBase, 154, Dos)
65 /* 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 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
77 dp - startup arguments specified as a packet
79 RESULT
80 RETURN_OK on success, ERROR_* (from dp_Res2) on failure.
82 NOTES
83 This function is internal to AROS, and should never be
84 called by user space.
86 EXAMPLE
88 BUGS
90 SEE ALSO
92 INTERNALS
94 *****************************************************************************/
96 AROS_LIBFUNC_INIT
98 struct MsgPort *mp, *reply_mp;
99 struct DosPacket *my_dp = NULL;
100 SIPTR Res1, Res2;
101 BPTR seg;
103 /* Create a DOS Process to handle this, since
104 * we're probably being called from a Task context
106 if (dp == NULL) {
107 /* This call does *not* require that
108 * we be a DOS Process. Luckily.
110 my_dp = AllocDosObject(DOS_STDPKT, NULL);
111 dp = my_dp;
114 reply_mp = CreateMsgPort();
115 seg = CreateSegList(internalBootCliHandler);
116 if (dp == NULL || reply_mp == NULL || seg == BNULL) {
117 if (my_dp)
118 FreeDosObject(DOS_STDPKT, my_dp);
119 DeleteMsgPort(reply_mp);
120 UnLoadSeg(seg);
121 return ERROR_NO_FREE_STORE;
124 mp = CreateProc("Boot Mount", 0, seg, AROS_STACKSIZE);
125 if (mp == NULL) {
126 DeleteMsgPort(reply_mp);
127 if (my_dp)
128 FreeDosObject(DOS_STDPKT, my_dp);
129 /* A best guess... */
130 UnLoadSeg(seg);
131 return ERROR_NO_FREE_STORE;
134 /* Preload the reply with failure */
135 dp->dp_Res1 = DOSFALSE;
136 dp->dp_Res2 = ERROR_NOT_IMPLEMENTED;
138 /* Again, doesn't require this Task to be a Process */
139 SendPkt(dp, mp, reply_mp);
141 /* Wait for the message from the Boot Cli */
142 WaitPort(reply_mp);
143 GetMsg(reply_mp);
145 /* We know that if we've received a reply packet,
146 * that we've been able to execute the handler,
147 * therefore we can dispense with the 'CreateSegList()'
148 * stub.
150 UnLoadSeg(seg);
152 Res1 = dp->dp_Res1;
153 Res2 = dp->dp_Res2;
155 if (my_dp)
156 FreeDosObject(DOS_STDPKT, my_dp);
158 DeleteMsgPort(reply_mp);
160 D(bug("Dos/CliInit: Process returned Res1=%ld, Res2=%ld\n", Res1, Res2));
162 /* Did we succeed? */
163 if (Res1 == DOSTRUE)
164 return RETURN_OK;
165 /* Make sure we return non-zero error code, 0 == RETURN_OK */
166 if (Res2 == 0)
167 Res2 = ERROR_UNKNOWN;
169 return Res2;
171 AROS_LIBFUNC_EXIT
172 } /* CliInit */
174 /* Find the most recent version of the matching filesystem
175 * We are assured from the calling function that either
176 * DosType != 0, or Handler != BNULL.
177 * We attempt to match first on DosType, then on Handler.
179 static struct FileSysEntry *internalMatchFileSystemResourceHandler(struct FileSysResource *fsr, ULONG DosType, BPTR Handler)
181 struct FileSysEntry *fse, *best_fse = NULL;
183 ForeachNode(&fsr->fsr_FileSysEntries, fse)
185 BOOL try_patch = FALSE;
187 if (DosType == 0)
189 if (fse->fse_Handler != BNULL &&
190 (fse->fse_PatchFlags & FSEF_HANDLER))
192 try_patch = (strcmp(AROS_BSTR_ADDR(Handler),
193 AROS_BSTR_ADDR(fse->fse_Handler)) == 0)
194 ? TRUE : FALSE;
197 else if (fse->fse_DosType == DosType)
199 try_patch = TRUE;
202 if (try_patch) {
203 if (fse->fse_PatchFlags & (FSEF_HANDLER | FSEF_SEGLIST | FSEF_TASK))
205 if (best_fse == NULL || fse->fse_Version > best_fse->fse_Version)
207 best_fse = fse;
213 return best_fse;
216 /* See if the BootNode's DeviceNode needs to be patched by
217 * an entry in FileSysResource
219 * If de->de_DosType == 0, and no dn_SegList nor dn_Handler,
220 * then the node uses the 'defseg' handler.
222 static void internalPatchBootNode(struct FileSysResource *fsr, struct DeviceNode *dn, BPTR defseg)
224 struct FileSysStartupMsg *fssm;
225 struct DosEnvec *de;
226 struct FileSysEntry *fse;
228 /* If we already have a task installed,
229 * then we're done.
231 if (dn->dn_Task != NULL)
232 return;
234 /* If we already have a handler installed,
235 * then we're done.
237 if (dn->dn_SegList != BNULL)
238 return;
240 fssm = BADDR(dn->dn_Startup);
241 if (fssm == NULL)
242 return;
244 de = BADDR(fssm->fssm_Environ);
245 if (de == NULL)
246 return;
248 /* If the DosType is 0 and dn_Handler == BNULL, use the default handler */
249 if (de->de_DosType == 0 && dn->dn_Handler == BNULL)
251 D(bug("Dos/CliInit: Neither DosType nor Handler specified, using default filesystem\n"));
252 dn->dn_SegList = defseg;
253 dn->dn_GlobalVec = (BPTR)-1;
254 return;
257 /* If no FileSysResource, nothing to do */
258 if (fsr == NULL) {
259 D(bug("Dos/CliInit: No FileSystem.resource, not patching DeviceNode %p\n", dn));
260 return;
263 D(bug("Dos/CliInit: Looking for patches for DeviceNode %p\n", dn));
266 * internalMatchFileSystemResourceHandler looks up the filesystem
268 fse = internalMatchFileSystemResourceHandler(fsr, de->de_DosType, dn->dn_Handler);
269 if (fse != NULL)
271 D(bug("Dos/CliInit: found 0x%p in FileSystem.resource\n", fse));
272 PRINT_DOSTYPE(fse->fse_DosType);
274 dn->dn_SegList = fse->fse_SegList;
275 /* other fse_PatchFlags bits are quite pointless */
276 if (fse->fse_PatchFlags & FSEF_TASK)
277 dn->dn_Task = (APTR)fse->fse_Task;
278 if (fse->fse_PatchFlags & FSEF_LOCK)
279 dn->dn_Lock = fse->fse_Lock;
281 /* Adjust the stack size for 64-bits if needed.
283 if (fse->fse_PatchFlags & FSEF_STACKSIZE)
284 dn->dn_StackSize = (fse->fse_StackSize/sizeof(ULONG))*sizeof(IPTR);
285 if (fse->fse_PatchFlags & FSEF_PRIORITY)
286 dn->dn_Priority = fse->fse_Priority;
287 if (fse->fse_PatchFlags & FSEF_GLOBALVEC)
288 dn->dn_GlobalVec = fse->fse_GlobalVec;
292 static struct MsgPort *mountBootNode(struct DeviceNode *dn, struct FileSysResource *fsr, struct DosLibrary *DOSBase)
294 struct DosList *dl;
296 if ((dn == NULL) || (dn->dn_Name == BNULL))
297 return NULL;
299 D(bug("Dos/CliInit: Mounting 0x%p (%b)...\n", dn, dn->dn_Name));
301 /* Check if the device is already in DOS list */
302 dl = LockDosList(LDF_DEVICES | LDF_READ);
304 while ((dl = NextDosEntry(dl, LDF_DEVICES)))
306 if (dl == (struct DosList *)dn)
307 break;
310 UnLockDosList(LDF_DEVICES | LDF_READ);
312 /* Found in DOS list? Do nothing. */
313 if (dl)
315 D(bug("Dos/CliInit: Found in DOS list, nothing to do\n"));
316 return dl->dol_Task;
319 /* Patch it up, if needed */
320 internalPatchBootNode(fsr, dn, DOSBase->dl_Root->rn_FileHandlerSegment);
322 if (!dn->dn_Handler && !dn->dn_SegList)
324 /* Don't know how to mount? Error... */
325 D(bug("Dos/CliInit: Don't know how to mount\n"));
326 return NULL;
329 if (AddDosEntry((struct DosList *)dn) != DOSFALSE)
332 * Do not check for ADNF_STARTPROC because:
333 * a) On the Amiga ADNF_STARTPROC was not present in KS 1.3 and earlier, there was no deferred mount.
334 * b) In fact if we have something in ExpansionBase, we for sure want it to be mounted.
336 D(bug("Dos/CliInit: Added to DOS list, starting up handler...\n"));
338 if (RunHandler(dn, NULL, DOSBase))
340 D(bug("dn->dn_Task = 0x%p\n", dn->dn_Task));
341 return dn->dn_Task;
344 D(bug("Failed\n"));
345 RemDosEntry((struct DosList *)dn);
347 D(else bug("Dos/CliInit: AddDosEntry() failed\n"));
350 * TODO: AddDosEntry() can fail in case of duplicate name. In this case it would be useful
351 * to append some suffix. AmigaOS IIRC did the same.
352 * This appears to be needed if you have DH0:. DH1:, etc, on your hard drive, and want to
353 * connect a friend's hard drive which also has partitions with these names.
356 return NULL;
359 static BPTR internalBootLock(struct DosLibrary *DOSBase, struct ExpansionBase *ExpansionBase, struct FileSysResource *fsr)
361 struct BootNode *bn;
362 struct DeviceNode *dn;
363 struct MsgPort *mp;
364 BPTR lock = BNULL;
365 STRPTR name;
366 int name_len;
368 /* The first BootNode off of the MountList.
369 * The dosboot.resource strap routine will have
370 * made the desired boot node the first in this list.
372 * If this fails to mount, we will fail, which will
373 * cause dos.library to fail to initialize, and
374 * then dosboot.resource will handle checking the
375 * next device in the list.
377 ObtainSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
378 bn = (struct BootNode *)GetHead(&ExpansionBase->MountList);
379 D(bug("Dos/CliInit: MountList head: 0x%p\n", bn));
381 if (bn == NULL)
383 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
384 SetIoErr(ERROR_OBJECT_NOT_FOUND);
385 return BNULL;
388 dn = bn->bn_DeviceNode;
389 mp = mountBootNode(dn, fsr, DOSBase);
390 if (!mp)
392 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
393 SetIoErr(ERROR_OBJECT_WRONG_TYPE);
394 return BNULL;
397 D(bug("Dos/CliInit: %b (%d) appears usable\n", dn->dn_Name, bn->bn_Node.ln_Pri));
399 /* Try to find a Lock for 'name:' */
400 name_len = AROS_BSTR_strlen(dn->dn_Name);
401 name = AllocVec(name_len + 2, MEMF_ANY);
402 if (name != NULL)
404 SIPTR err = 0;
406 /* Make the volume name a volume: name */
407 CopyMem(AROS_BSTR_ADDR(dn->dn_Name), name, name_len);
408 name[name_len+0] = ':';
409 name[name_len+1] = 0;
410 D(bug("Dos/CliInit: Attempt to Lock(\"%s\")... ", name));
412 lock = Lock(name, SHARED_LOCK);
413 D(bug("=> 0x%p\n", BADDR(lock)));
415 if (lock != BNULL)
417 /* If we have a lock, check the per-platform conditional boot code. */
418 if (!__dos_IsBootable(DOSBase, lock))
420 UnLock(lock);
421 lock = BNULL;
422 err = ERROR_OBJECT_WRONG_TYPE; /* Something to more or less reflect "This disk is not bootable" */
424 else
425 /* Let everyone know a boot volume has been chosen */
426 ExpansionBase->Flags |= EBF_DOSFLAG;
428 else
430 err = IoErr();
433 if (!lock)
435 SIPTR dead;
437 /* Darn. Not bootable. Try to unmount it. */
438 D(bug("Dos/CliInit: Does not have a bootable filesystem, unmounting...\n"));
440 /* It's acceptable if this fails */
441 dead = DoPkt(mp, ACTION_DIE, 0, 0, 0, 0, 0);
442 D(bug("Dos/CliInit: ACTION_DIE returned %ld\n", dead));
444 if (dead)
447 * Handlers usually won't remove their DeviceNodes themselves.
448 * And even if they do (ACTION_DIE is poorly documented), RemDosEntry()
449 * on an already removed entry is safe due to nature of DOS list.
450 * What is really prohibited, is unloading own seglist. Well, resident
451 * handlers will never do it, they know...
453 RemDosEntry((struct DosList *)dn);
454 dn->dn_Task = NULL;
456 /* DoPkt() clobbered IoErr() */
457 SetIoErr(err);
460 FreeVec(name);
462 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
464 return lock;
467 static void AddBootAssign(CONST_STRPTR path, CONST_STRPTR assign, APTR DOSBase)
469 BPTR lock;
470 if (!(lock = Lock(path, SHARED_LOCK)))
471 lock = Lock("SYS:", SHARED_LOCK);
472 if (lock)
473 AssignLock(assign, lock);
478 * This is what actually gets the Lock() for SYS:,
479 * sets up the boot assigns, and starts the
480 * startup sequence.
482 static LONG internalBootCliHandler(void)
484 struct Process *bootProc = (struct Process *)FindTask(NULL);
485 BPTR bootWin = bootProc->pr_WindowPtr;
487 struct ExpansionBase *ExpansionBase;
488 struct DosLibrary *DOSBase;
489 BPTR lock;
490 struct DosPacket *dp;
491 ULONG BootFlags;
492 UBYTE Flags;
493 struct BootNode *bn, *tmpbn;
494 struct FileSysResource *fsr;
495 LONG err = 0;
497 /* Ah. A DOS Process context. At last! */
498 WaitPort(&bootProc->pr_MsgPort);
499 dp = (struct DosPacket *)(GetMsg(&bootProc->pr_MsgPort)->mn_Node.ln_Name);
501 DOSBase = (APTR)OpenLibrary("dos.library", 0);
502 if (DOSBase == NULL) {
503 D(bug("Dos/CliInit: Impossible! Where did dos.library go?\n"));
504 Alert(AT_DeadEnd | AG_OpenLib | AO_DOSLib);
507 ExpansionBase = (APTR)OpenLibrary("expansion.library", 0);
508 if (!ExpansionBase)
509 err = ERROR_INVALID_RESIDENT_LIBRARY;
511 /* Suppress "insert volume" prompts */
512 bootProc->pr_WindowPtr = (BPTR)-1;
514 if (err == 0)
516 /* It's perfectly fine if this fails. */
517 fsr = OpenResource("FileSystem.resource");
519 /* Find and Lock the proposed boot device */
520 lock = internalBootLock(DOSBase, ExpansionBase, fsr);
521 D(bug("Dos/CliInit: Proposed SYS: lock is: %p\n", BADDR(lock)));
522 if (lock == BNULL)
523 err = IoErr();
526 if (err != 0)
529 * We've failed. Inform our parent and exit.
530 * Immediately after we reply the packet, the parent (Boot Task) can expunge DOSBase.
531 * This is why we first cleanup, then use internal_ReplyPkt (DOSBase is considered
532 * invalid).
533 * Alternatively we could Forbid() before ReplyPkt(), but... Forbid() is so unpolite...
536 if (ExpansionBase != NULL)
537 CloseLibrary(&ExpansionBase->LibNode);
538 CloseLibrary(&DOSBase->dl_lib);
540 /* Immediately after ReplyPkt() DOSBase can be freed. */
541 internal_ReplyPkt(dp, &bootProc->pr_MsgPort, DOSFALSE, err);
543 bootProc->pr_WindowPtr = bootWin;
545 return err;
548 /* Ok, at this point we've succeeded. Inform our parent. */
549 ReplyPkt(dp, DOSTRUE, 0);
551 /* We're now at the point of no return. */
552 DOSBase->dl_Root->rn_BootProc = ((struct FileLock*)BADDR(lock))->fl_Task;
553 SetFileSysTask(DOSBase->dl_Root->rn_BootProc);
555 AssignLock("SYS", lock);
556 lock = Lock("SYS:", SHARED_LOCK);
557 if (lock == BNULL)
559 D(bug("DOS/CliInit: Impossible! The SYS: assign failed!\n"));
560 Alert(AT_DeadEnd | AG_BadParm | AN_DOSLib);
563 AddBootAssign("SYS:C", "C", DOSBase);
564 AddBootAssign("SYS:Libs", "LIBS", DOSBase);
565 AddBootAssign("SYS:Devs", "DEVS", DOSBase);
566 AddBootAssign("SYS:L", "L", DOSBase);
567 AddBootAssign("SYS:S", "S", DOSBase);
568 AddBootAssign("SYS:Fonts", "FONTS", DOSBase);
570 #if !(mc68000)
571 /* Let hidds in DRIVERS: directory be found by OpenLibrary */
572 if ((lock = Lock("DEVS:Drivers", SHARED_LOCK)) != BNULL) {
573 AssignLock("DRIVERS", lock);
574 AssignAdd("LIBS", lock);
577 * This early assignment prevents Poseidon from asking for ENV:
578 * when popup GUI process is initialized and opens muimaster.library.
579 * On m68k this harms, Workbench 1.x disks fail to boot correctly with
580 * "Can't cancel ENV:" warning.
581 * FIXME: Fix muimaster.library at last, and forget this hack.
583 AssignLate("ENV", "SYS:Prefs/Env-Archive");
584 #endif
585 AssignLate("ENVARC", "SYS:Prefs/Env-Archive");
588 * At this point we have only SYS:, nothing more. Mount the rest.
589 * We do it after assigning SYS: because in some cases we can have
590 * BootNodes with handler name but no seglist (Poseidon could add them for example).
591 * This means the handler needs to be loaded from disk (fat-handler for example).
592 * Here we can already do it.
594 D(bug("Dos/CliInit: Assigns done, mount remaining handlers...\n"));
596 BootFlags = IntExpBase(ExpansionBase)->BootFlags;
597 Flags = ExpansionBase->Flags;
598 D(bug("Dos/CliInit: BootFlags 0x%lx Flags 0x%x\n", BootFlags, Flags));
600 ForeachNodeSafe(&ExpansionBase->MountList, bn, tmpbn)
603 * Don't check for return code. Failed is failed.
604 * One of failure reasons can be missing handler specification for some DOSType.
605 * In this case the DeviceNode will not be mounted, but it will stay in
606 * ExpansionBase->MountList. It can be picked up by disk-based program which would
607 * read mappings for disk-based handlers from file.
608 * This way we could automount e.g. FAT, NTFS, EXT3/2, whatever else, partitions.
610 mountBootNode(bn->bn_DeviceNode, fsr, DOSBase);
613 CloseLibrary((APTR)ExpansionBase);
615 /* Enable prompts to insert missing volumes again .. */
616 bootProc->pr_WindowPtr = bootWin;
618 /* Init all the RTF_AFTERDOS code, since we now have SYS:, the dos devices, and all the other assigns */
619 D(bug("Dos/CliInit: Calling InitCode(RTF_AFTERDOS, 0)\n"));
620 InitCode(RTF_AFTERDOS, 0);
622 /* Call the platform-overridable portions */
623 D(bug("Dos/CliInit: Calling __dos_Boot(%p, 0x%lx, 0x%x)\n", DOSBase, BootFlags, Flags));
624 __dos_Boot(DOSBase, BootFlags, Flags);
626 D(bug("Dos/CliInit: Boot sequence exited\n"));
627 CloseLibrary((APTR)DOSBase);
629 /* And exit... */
630 return RETURN_OK;