2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
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>
19 #include "dos_intern.h"
20 #include "../expansion/expansion_intern.h"
24 static void PRINT_DOSTYPE(ULONG dt
)
28 bug("Dos/CliInit: DosType is ");
30 for (i
= 0; i
< 4; i
++)
32 unsigned char c
= dt
>> (24 - i
* 8);
45 #define PRINT_DOSTYPE(dt)
49 static LONG
internalBootCliHandler(void);
51 /*****************************************************************************
54 #include <dos/dosextens.h>
55 #include <proto/dos.h>
57 AROS_LH1(IPTR
, CliInit
,
60 AROS_LHA(struct DosPacket
*, dp
, A0
),
63 struct DosLibrary
*, DOSBase
, 154, Dos
)
66 Set up the first shell process.
68 Currently, no DOS Packet arguments are used by this
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.
77 dp - startup arguments specified as a packet
80 RETURN_OK on success, ERROR_* (from dp_Res2) on failure.
83 This function is internal to AROS, and should never be
94 *****************************************************************************/
98 struct MsgPort
*mp
, *reply_mp
;
99 struct DosPacket
*my_dp
= NULL
;
103 /* Create a DOS Process to handle this, since
104 * we're probably being called from a Task context
107 /* This call does *not* require that
108 * we be a DOS Process. Luckily.
110 my_dp
= AllocDosObject(DOS_STDPKT
, NULL
);
114 reply_mp
= CreateMsgPort();
115 seg
= CreateSegList(internalBootCliHandler
);
116 if (dp
== NULL
|| reply_mp
== NULL
|| seg
== BNULL
) {
118 FreeDosObject(DOS_STDPKT
, my_dp
);
119 DeleteMsgPort(reply_mp
);
121 return ERROR_NO_FREE_STORE
;
124 mp
= CreateProc("Boot Mount", 0, seg
, AROS_STACKSIZE
);
126 DeleteMsgPort(reply_mp
);
128 FreeDosObject(DOS_STDPKT
, my_dp
);
129 /* A best guess... */
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 */
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()'
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? */
165 /* Make sure we return non-zero error code, 0 == RETURN_OK */
167 Res2
= ERROR_UNKNOWN
;
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
;
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)
197 else if (fse
->fse_DosType
== DosType
)
203 if (fse
->fse_PatchFlags
& (FSEF_HANDLER
| FSEF_SEGLIST
| FSEF_TASK
))
205 if (best_fse
== NULL
|| fse
->fse_Version
> best_fse
->fse_Version
)
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
;
226 struct FileSysEntry
*fse
;
228 /* If we already have a task installed,
231 if (dn
->dn_Task
!= NULL
)
234 /* If we already have a handler installed,
237 if (dn
->dn_SegList
!= BNULL
)
240 fssm
= BADDR(dn
->dn_Startup
);
244 de
= BADDR(fssm
->fssm_Environ
);
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;
257 /* If no FileSysResource, nothing to do */
259 D(bug("Dos/CliInit: No FileSystem.resource, not patching DeviceNode %p\n", dn
));
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
);
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
)
296 if ((dn
== NULL
) || (dn
->dn_Name
== BNULL
))
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
)
310 UnLockDosList(LDF_DEVICES
| LDF_READ
);
312 /* Found in DOS list? Do nothing. */
315 D(bug("Dos/CliInit: Found in DOS list, nothing to do\n"));
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"));
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
));
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.
359 static BPTR
internalBootLock(struct DosLibrary
*DOSBase
, struct ExpansionBase
*ExpansionBase
, struct FileSysResource
*fsr
)
362 struct DeviceNode
*dn
;
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
));
383 ReleaseSemaphore(&IntExpBase(ExpansionBase
)->BootSemaphore
);
384 SetIoErr(ERROR_OBJECT_NOT_FOUND
);
388 dn
= bn
->bn_DeviceNode
;
389 mp
= mountBootNode(dn
, fsr
, DOSBase
);
392 ReleaseSemaphore(&IntExpBase(ExpansionBase
)->BootSemaphore
);
393 SetIoErr(ERROR_OBJECT_WRONG_TYPE
);
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
);
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
)));
417 /* If we have a lock, check the per-platform conditional boot code. */
418 if (!__dos_IsBootable(DOSBase
, lock
))
422 err
= ERROR_OBJECT_WRONG_TYPE
; /* Something to more or less reflect "This disk is not bootable" */
425 /* Let everyone know a boot volume has been chosen */
426 ExpansionBase
->Flags
|= EBF_DOSFLAG
;
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
));
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
);
456 /* DoPkt() clobbered IoErr() */
462 ReleaseSemaphore(&IntExpBase(ExpansionBase
)->BootSemaphore
);
467 static void AddBootAssign(CONST_STRPTR path
, CONST_STRPTR assign
, APTR DOSBase
)
470 if (!(lock
= Lock(path
, SHARED_LOCK
)))
471 lock
= Lock("SYS:", SHARED_LOCK
);
473 AssignLock(assign
, lock
);
478 * This is what actually gets the Lock() for SYS:,
479 * sets up the boot assigns, and starts the
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
;
490 struct DosPacket
*dp
;
493 struct BootNode
*bn
, *tmpbn
;
494 struct FileSysResource
*fsr
;
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);
509 err
= ERROR_INVALID_RESIDENT_LIBRARY
;
511 /* Suppress "insert volume" prompts */
512 bootProc
->pr_WindowPtr
= (BPTR
)-1;
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
)));
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
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
;
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
);
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
);
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");
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
);