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
== NULL
) {
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
);
387 dn
= bn
->bn_DeviceNode
;
388 mp
= mountBootNode(dn
, fsr
, DOSBase
);
391 ReleaseSemaphore(&IntExpBase(ExpansionBase
)->BootSemaphore
);
395 D(bug("Dos/CliInit: %b (%d) appears usable\n", dn
->dn_Name
, bn
->bn_Node
.ln_Pri
));
397 /* Try to find a Lock for 'name:' */
398 name_len
= AROS_BSTR_strlen(dn
->dn_Name
);
399 name
= AllocVec(name_len
+ 2, MEMF_ANY
);
404 /* Make the volume name a volume: name */
405 CopyMem(AROS_BSTR_ADDR(dn
->dn_Name
), name
, name_len
);
406 name
[name_len
+0] = ':';
407 name
[name_len
+1] = 0;
408 D(bug("Dos/CliInit: Attempt to Lock(\"%s\")... ", name
));
410 lock
= Lock(name
, SHARED_LOCK
);
411 D(bug("=> 0x%p\n", BADDR(lock
)));
415 /* If we have a lock, check the per-platform conditional boot code. */
416 if (!__dos_IsBootable(DOSBase
, lock
))
420 err
= ERROR_OBJECT_WRONG_TYPE
; /* Something to more or less reflect "This disk is not bootable" */
423 /* Let everyone know a boot volume has been chosen */
424 ExpansionBase
->Flags
|= EBF_DOSFLAG
;
435 /* Darn. Not bootable. Try to unmount it. */
436 D(bug("Dos/CliInit: Does not have a bootable filesystem, unmounting...\n"));
438 /* It's acceptable if this fails */
439 dead
= DoPkt(mp
, ACTION_DIE
, 0, 0, 0, 0, 0);
440 D(bug("Dos/CliInit: ACTION_DIE returned %ld\n", dead
));
445 * Handlers usually won't remove their DeviceNodes themselves.
446 * And even if they do (ACTION_DIE is poorly documented), RemDosEntry()
447 * on an already removed entry is safe due to nature of DOS list.
448 * What is really prohibited, is unloading own seglist. Well, resident
449 * handlers will never do it, they know...
451 RemDosEntry((struct DosList
*)dn
);
454 /* DoPkt() clobbered IoErr() */
460 ReleaseSemaphore(&IntExpBase(ExpansionBase
)->BootSemaphore
);
465 static void AddBootAssign(CONST_STRPTR path
, CONST_STRPTR assign
, APTR DOSBase
)
468 if (!(lock
= Lock(path
, SHARED_LOCK
)))
469 lock
= Lock("SYS:", SHARED_LOCK
);
471 AssignLock(assign
, lock
);
476 * This is what actually gets the Lock() for SYS:,
477 * sets up the boot assigns, and starts the
480 static LONG
internalBootCliHandler(void)
482 struct ExpansionBase
*ExpansionBase
;
483 struct DosLibrary
*DOSBase
;
484 struct MsgPort
*mp
= &((struct Process
*)FindTask(NULL
))->pr_MsgPort
;
486 struct DosPacket
*dp
;
489 struct BootNode
*bn
, *tmpbn
;
490 struct FileSysResource
*fsr
;
493 /* Ah. A DOS Process context. At last! */
495 dp
= (struct DosPacket
*)(GetMsg(mp
)->mn_Node
.ln_Name
);
497 DOSBase
= (APTR
)OpenLibrary("dos.library", 0);
498 if (DOSBase
== NULL
) {
499 D(bug("Dos/CliInit: Impossible! Where did dos.library go?\n"));
500 Alert(AT_DeadEnd
| AG_OpenLib
| AO_DOSLib
);
503 ExpansionBase
= (APTR
)OpenLibrary("expansion.library", 0);
505 err
= ERROR_INVALID_RESIDENT_LIBRARY
;
509 /* It's perfectly fine if this fails. */
510 fsr
= OpenResource("FileSystem.resource");
512 /* Find and Lock the proposed boot device */
513 lock
= internalBootLock(DOSBase
, ExpansionBase
, fsr
);
514 D(bug("Dos/CliInit: Proposed SYS: lock is: %p\n", BADDR(lock
)));
522 * We've failed. Inform our parent and exit.
523 * Immediately after we reply the packet, the parent (Boot Task) can expunge DOSBase.
524 * This is why we first cleanup, then use internal_ReplyPkt (DOSBase is considered
526 * Alternatively we could Forbid() before ReplyPkt(), but... Forbid() is so unpolite...
529 if (ExpansionBase
!= NULL
)
530 CloseLibrary(&ExpansionBase
->LibNode
);
531 CloseLibrary(&DOSBase
->dl_lib
);
533 /* Immediately after ReplyPkt() DOSBase can be freed. */
534 internal_ReplyPkt(dp
, mp
, DOSFALSE
, err
);
538 /* Ok, at this point we've succeeded. Inform our parent. */
539 ReplyPkt(dp
, DOSTRUE
, 0);
541 /* We're now at the point of no return. */
542 DOSBase
->dl_Root
->rn_BootProc
= ((struct FileLock
*)BADDR(lock
))->fl_Task
;
543 SetFileSysTask(DOSBase
->dl_Root
->rn_BootProc
);
545 AssignLock("SYS", lock
);
546 lock
= Lock("SYS:", SHARED_LOCK
);
549 D(bug("DOS/CliInit: Impossible! The SYS: assign failed!\n"));
550 Alert(AT_DeadEnd
| AG_BadParm
| AN_DOSLib
);
553 AddBootAssign("SYS:C", "C", DOSBase
);
554 AddBootAssign("SYS:Libs", "LIBS", DOSBase
);
555 AddBootAssign("SYS:Devs", "DEVS", DOSBase
);
556 AddBootAssign("SYS:L", "L", DOSBase
);
557 AddBootAssign("SYS:S", "S", DOSBase
);
558 AddBootAssign("SYS:Fonts", "FONTS", DOSBase
);
561 /* Let hidds in DRIVERS: directory be found by OpenLibrary */
562 if ((lock
= Lock("DEVS:Drivers", SHARED_LOCK
)) != BNULL
) {
563 AssignLock("DRIVERS", lock
);
564 AssignAdd("LIBS", lock
);
567 * This early assignment prevents Poseidon from asking for ENV:
568 * when popup GUI process is initialized and opens muimaster.library.
569 * On m68k this harms, Workbench 1.x disks fail to boot correctly with
570 * "Can't cancel ENV:" warning.
571 * FIXME: Fix muimaster.library at last, and forget this hack.
573 AssignLate("ENV", "SYS:Prefs/Env-Archive");
575 AssignLate("ENVARC", "SYS:Prefs/Env-Archive");
578 * At this point we have only SYS:, nothing more. Mount the rest.
579 * We do it after assigning SYS: because in some cases we can have
580 * BootNodes with handler name but no seglist (Poseidon could add them for example).
581 * This means the handler needs to be loaded from disk (fat-handler for example).
582 * Here we can already do it.
584 D(bug("Dos/CliInit: Assigns done, mount remaining handlers...\n"));
586 BootFlags
= IntExpBase(ExpansionBase
)->BootFlags
;
587 Flags
= ExpansionBase
->Flags
;
588 D(bug("Dos/CliInit: BootFlags 0x%lx Flags 0x%x\n", BootFlags
, Flags
));
590 ForeachNodeSafe(&ExpansionBase
->MountList
, bn
, tmpbn
)
593 * Don't check for return code. Failed is failed.
594 * One of failure reasons can be missing handler specification for some DOSType.
595 * In this case the DeviceNode will not be mounted, but it will stay in
596 * ExpansionBase->MountList. It can be picked up by disk-based program which would
597 * read mappings for disk-based handlers from file.
598 * This way we could automount e.g. FAT, NTFS, EXT3/2, whatever else, partitions.
600 mountBootNode(bn
->bn_DeviceNode
, fsr
, DOSBase
);
603 CloseLibrary((APTR
)ExpansionBase
);
605 /* Init all the RTF_AFTERDOS code, since we now have SYS:, the dos devices, and all the other assigns */
606 D(bug("Dos/CliInit: Calling InitCode(RTF_AFTERDOS, 0)\n"));
607 InitCode(RTF_AFTERDOS
, 0);
609 /* Call the platform-overridable portions */
610 D(bug("Dos/CliInit: Calling __dos_Boot(%p, 0x%lx, 0x%x)\n", DOSBase
, BootFlags
, Flags
));
611 __dos_Boot(DOSBase
, BootFlags
, Flags
);
613 D(bug("Dos/CliInit: Boot sequence exited\n"));
614 CloseLibrary((APTR
)DOSBase
);