use the override options if specified
[AROS.git] / arch / all-pc / boot / grub2-tools / Install-grub2.c
blobdd6fc31410e46e46785a93426dcb46fcfa5b0a78
1 /*
2 Copyright © 1995-2019, The AROS Development Team. All rights reserved.
3 $Id$
4 */
5 /******************************************************************************
8 NAME
10 Install-grub2
12 SYNOPSIS
14 DEVICE/A, UNIT/N/K/A, PARTITIONNUMBER=PN/K/N, GRUB/K/A, FORCELBA/S
16 LOCATION
20 FUNCTION
22 Installs the GRUB 2 bootloader to the boot block of the specified
23 disk or partition.
25 INPUTS
27 DEVICE -- Device name (e.g. ata.device)
28 UNIT -- Unit number
29 PN -- Specifies a partition number. If specified, GRUB is installed
30 to this partition's boot block. Otherwise, GRUB is installed to
31 the disk's boot block.
32 GRUB -- Path to GRUB directory.
33 FORCELBA -- Force use of LBA mode.
35 RESULT
37 NOTES
39 EFI machines don't need this utility because EFI doesn't need a bootblock.
40 It is capable of loading files directly from the filesystem.
42 EXAMPLE
44 Install-grub2 DEVICE ata.device UNIT 0 GRUB DH0:boot/grub
46 BUGS
48 SEE ALSO
50 Partition, SYS:System/Format
52 INTERNALS
54 ******************************************************************************/
56 #define DEBUG 0
57 #include <aros/debug.h>
59 #include <string.h>
60 #include <proto/debug.h>
61 #include <proto/exec.h>
62 #include <proto/dos.h>
63 #include <proto/partition.h>
64 #include <proto/utility.h>
65 #include <aros/macros.h>
66 #include <devices/hardblocks.h>
67 #include <devices/newstyle.h>
68 #include <exec/errors.h>
69 #include <exec/memory.h>
70 #include <libraries/partition.h>
74 /* Defines for grub2 data */
75 /* boot.img pointers */
76 #define GRUB_BOOT_MACHINE_BPB_START 0x03
77 #define GRUB_BOOT_MACHINE_BPB_END 0x5a
78 #define GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC 0x01b8 /* Following grub2 grub-setup sources */
79 #define GRUB_BOOT_MACHINE_PART_START 0x01be
80 #define GRUB_BOOT_MACHINE_PART_END 0x01fe
81 #define GRUB_BOOT_MACHINE_KERNEL_SECTOR 0x5c
82 #define GRUB_BOOT_MACHINE_BOOT_DRIVE 0x64
83 #define GRUB_BOOT_MACHINE_DRIVE_CHECK 0x66
85 /* core.img pointers */
86 #define GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE 0x18
88 /* BIOS drive flag */
89 #define BIOS_HDISK_FLAG 0x80
91 #define MBR_MAX_PARTITIONS 4
92 #define MBRT_EXTENDED 0x05
93 #define MBRT_EXTENDED2 0x0f
94 #define BLCKLIST_ELEMENTS 14
96 struct Volume
98 struct MsgPort *mp;
99 struct IOExtTD *iotd;
100 ULONG readcmd;
101 ULONG writecmd;
102 ULONG startblock;
103 ULONG countblock;
104 CONST_STRPTR device;
105 ULONG unitnum;
106 UWORD SizeBlock;
107 UBYTE flags;
108 BYTE partnum;
109 ULONG *blockbuffer;
110 ULONG dos_id;
113 #define VF_IS_TRACKDISK (1<<0)
114 #define VF_IS_RDB (1<<1)
116 struct BlockNode
118 ULONG sector_lo;
119 ULONG sector_hi;
120 UWORD count;
121 UWORD seg_adr;
124 const TEXT version[] = "$VER: Install-grub2-i386-pc 41.4 (15.9.2012)";
126 CONST_STRPTR CORE_IMG_FILE_NAME = "i386-pc/core.img";
128 STRPTR template =
129 (STRPTR) ("DEVICE/A," "UNIT/N/K/A," "PARTITIONNUMBER=PN/K/N," "GRUB/K/A,"
130 "FORCELBA/S");
132 IPTR myargs[7] = { 0, 0, 0, 0, 0, 0 };
134 struct FileSysStartupMsg *getDiskFSSM(CONST_STRPTR path)
136 struct DosList *dl;
137 struct DeviceNode *dn;
138 TEXT dname[32];
139 UBYTE i;
141 D(bug("[install] getDiskFSSM('%s')\n", path));
143 for (i = 0; (path[i]) && (path[i] != ':'); i++)
144 dname[i] = path[i];
145 if (path[i] == ':')
147 dname[i] = 0;
148 dl = LockDosList(LDF_READ);
149 if (dl)
151 dn = (struct DeviceNode *) FindDosEntry(dl, dname, LDF_DEVICES);
152 UnLockDosList(LDF_READ);
153 if (dn)
155 dname[i] = ':';
156 dname[i + 1] = '\0';
157 if (IsFileSystem(dname))
159 return (struct FileSysStartupMsg *) BADDR(dn->dn_Startup);
161 else
162 Printf("device '%s' doesn't contain a file system\n",
163 dname);
165 else
166 PrintFault(ERROR_OBJECT_NOT_FOUND, dname);
169 else
170 Printf("'%s' doesn't contain a device name\n", path);
171 return 0;
174 void fillGeometry(struct Volume *volume, struct DosEnvec *de)
176 ULONG spc;
178 D(bug("[install] fillGeometry(%x)\n", volume));
180 spc = de->de_Surfaces * de->de_BlocksPerTrack;
181 volume->SizeBlock = de->de_SizeBlock;
182 volume->startblock = de->de_LowCyl * spc;
183 volume->countblock =
184 ((de->de_HighCyl - de->de_LowCyl + 1) * spc) - 1 + de->de_Reserved;
187 void nsdCheck(struct Volume *volume)
189 struct NSDeviceQueryResult nsdq;
190 UWORD *cmdcheck;
192 D(bug("[install] nsdCheck(%x)\n", volume));
194 if (((volume->startblock + volume->countblock) * /* last block */
195 ((volume->SizeBlock << 2) / 512) /* 1 portion (block) equals 512 (bytes) */
196 ) > 8388608)
198 nsdq.SizeAvailable = 0;
199 nsdq.DevQueryFormat = 0;
200 volume->iotd->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
201 volume->iotd->iotd_Req.io_Data = &nsdq;
202 volume->iotd->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
203 if (DoIO((struct IORequest *) &volume->iotd->iotd_Req) == IOERR_NOCMD)
205 Printf("Device doesn't understand NSD-Query\n");
207 else
209 if ((volume->iotd->iotd_Req.io_Actual >
210 sizeof(struct NSDeviceQueryResult))
211 || (volume->iotd->iotd_Req.io_Actual == 0)
212 || (volume->iotd->iotd_Req.io_Actual != nsdq.SizeAvailable))
214 Printf("WARNING wrong io_Actual using NSD\n");
216 else
218 if (nsdq.DeviceType != NSDEVTYPE_TRACKDISK)
219 Printf("WARNING no trackdisk type\n");
220 for (cmdcheck = nsdq.SupportedCommands; *cmdcheck; cmdcheck++)
222 if (*cmdcheck == NSCMD_TD_READ64)
223 volume->readcmd = NSCMD_TD_READ64;
224 if (*cmdcheck == NSCMD_TD_WRITE64);
225 volume->writecmd = NSCMD_TD_WRITE64;
227 if ((volume->readcmd != NSCMD_TD_READ64) ||
228 (volume->writecmd != NSCMD_TD_WRITE64))
229 Printf("WARNING no READ64/WRITE64\n");
235 struct Volume *initVolume(CONST_STRPTR device, ULONG unit, ULONG flags,
236 struct DosEnvec *de)
238 struct Volume *volume;
239 LONG error = 0;
241 D(bug("[install] initVolume(%s:%d)\n", device, unit));
243 volume = AllocVec(sizeof(struct Volume), MEMF_PUBLIC | MEMF_CLEAR);
244 if (volume)
246 volume->mp = CreateMsgPort();
247 if (volume->mp)
249 volume->iotd =
250 (struct IOExtTD *) CreateIORequest(volume->mp,
251 sizeof(struct IOExtTD));
252 if (volume->iotd)
254 volume->blockbuffer =
255 AllocVec(de->de_SizeBlock << 2, MEMF_PUBLIC | MEMF_CLEAR);
256 if (volume->blockbuffer)
258 if (OpenDevice
259 (device,
260 unit, (struct IORequest *) volume->iotd, flags) == 0)
262 if (strcmp((const char *) device, TD_NAME) == 0)
263 volume->flags |= VF_IS_TRACKDISK;
264 else
265 volume->flags |= VF_IS_RDB; /* just assume we have RDB */
266 volume->readcmd = CMD_READ;
267 volume->writecmd = CMD_WRITE;
268 volume->device = device;
269 volume->unitnum = unit;
270 volume->dos_id = 0;
271 fillGeometry(volume, de);
272 nsdCheck(volume);
273 return volume;
275 else
276 error = ERROR_NO_FREE_STORE;
277 FreeVec(volume->blockbuffer);
279 else
280 error = ERROR_NO_FREE_STORE;
281 DeleteIORequest((struct IORequest *) volume->iotd);
283 else
284 error = ERROR_NO_FREE_STORE;
285 DeleteMsgPort(volume->mp);
287 else
288 error = ERROR_NO_FREE_STORE;
289 FreeVec(volume);
291 else
292 error = ERROR_NO_FREE_STORE;
294 PrintFault(error, NULL);
295 return NULL;
298 void uninitVolume(struct Volume *volume)
300 D(bug("[install] uninitVolume(%x)\n", volume));
302 CloseDevice((struct IORequest *) volume->iotd);
303 FreeVec(volume->blockbuffer);
304 DeleteIORequest((struct IORequest *) volume->iotd);
305 DeleteMsgPort(volume->mp);
306 FreeVec(volume);
309 static ULONG _readwriteBlock(struct Volume *volume,
310 ULONG block, APTR buffer, ULONG length,
311 ULONG command)
313 UQUAD offset;
314 ULONG retval = 0;
316 volume->iotd->iotd_Req.io_Command = command;
317 volume->iotd->iotd_Req.io_Length = length;
318 volume->iotd->iotd_Req.io_Data = buffer;
319 offset = (UQUAD) (volume->startblock + block) * (volume->SizeBlock << 2);
320 volume->iotd->iotd_Req.io_Offset = offset & 0xFFFFFFFF;
321 volume->iotd->iotd_Req.io_Actual = offset >> 32;
322 retval = DoIO((struct IORequest *) &volume->iotd->iotd_Req);
323 if (volume->flags & VF_IS_TRACKDISK)
325 volume->iotd->iotd_Req.io_Command = TD_MOTOR;
326 volume->iotd->iotd_Req.io_Length = 0;
327 DoIO((struct IORequest *) &volume->iotd->iotd_Req);
329 return retval;
332 ULONG readBlock(struct Volume * volume, ULONG block, APTR buffer, ULONG size)
334 D(bug("[install] readBlock(vol:%x, block:%d, %d bytes)\n",
335 volume, block, size));
337 return _readwriteBlock(volume, block, buffer, size, volume->readcmd);
340 ULONG writeBlock(struct Volume * volume, ULONG block, APTR buffer, ULONG size)
342 D(bug("[install] writeBlock(vol:%x, block:%d, %d bytes)\n",
343 volume, block, size));
345 return _readwriteBlock(volume, block, buffer, size, volume->writecmd);
348 static BOOL isKnownFs(ULONG dos_id)
350 switch (dos_id)
352 case ID_FFS_DISK:
353 case ID_INTER_DOS_DISK:
354 case ID_INTER_FFS_DISK:
355 case ID_FASTDIR_DOS_DISK:
356 case ID_FASTDIR_FFS_DISK:
357 case ID_SFS_BE_DISK:
358 case ID_SFS_LE_DISK:
359 return TRUE;
362 return FALSE;
365 BOOL isvalidFileSystem(struct Volume * volume, CONST_STRPTR device,
366 ULONG unit)
368 BOOL retval = FALSE;
369 struct PartitionBase *PartitionBase;
370 struct PartitionHandle *ph;
371 ULONG dos_id;
373 D(bug("[install] isvalidFileSystem(%x, %s, %d)\n", volume, device, unit));
375 if (readBlock(volume, 0, volume->blockbuffer, 512))
377 Printf("Read Error\n");
378 return FALSE;
381 dos_id = AROS_BE2LONG(volume->blockbuffer[0]);
383 if (!isKnownFs(dos_id))
385 /* first block has no DOS\x so we don't have RDB for sure */
386 volume->flags &= ~VF_IS_RDB;
387 if (readBlock(volume, 1, volume->blockbuffer, 512))
389 Printf("Read Error\n");
390 return FALSE;
393 dos_id = AROS_BE2LONG(volume->blockbuffer[0]);
395 if (!isKnownFs(dos_id))
396 return FALSE;
397 else
398 volume->dos_id = dos_id;
400 else
401 volume->dos_id = dos_id;
403 volume->partnum = -1;
405 PartitionBase =
406 (struct PartitionBase *) OpenLibrary((CONST_STRPTR)
407 "partition.library", 1);
408 if (PartitionBase)
410 ph = OpenRootPartition(device, unit);
411 if (ph)
413 if (OpenPartitionTable(ph) == 0)
415 struct TagItem tags[3];
416 IPTR type;
418 tags[1].ti_Tag = TAG_DONE;
419 tags[0].ti_Tag = PTT_TYPE;
420 tags[0].ti_Data = (STACKIPTR) & type;
421 GetPartitionTableAttrs(ph, tags);
422 if (type == PHPTT_MBR)
424 struct PartitionHandle *pn;
425 struct DosEnvec de;
426 struct PartitionHandle *extph = NULL;
427 struct PartitionType ptype = { };
429 tags[0].ti_Tag = PT_DOSENVEC;
430 tags[0].ti_Data = (STACKIPTR) & de;
431 tags[1].ti_Tag = PT_TYPE;
432 tags[1].ti_Data = (STACKIPTR) & ptype;
433 tags[2].ti_Tag = TAG_DONE;
434 pn = (struct PartitionHandle *) ph->table->list.lh_Head;
435 while (pn->ln.ln_Succ)
437 ULONG scp;
439 GetPartitionAttrs(pn, tags);
440 if (ptype.id[0] == MBRT_EXTENDED
441 || ptype.id[0] == MBRT_EXTENDED2)
442 extph = pn;
443 else
445 scp = de.de_Surfaces * de.de_BlocksPerTrack;
446 if ((volume->startblock >= (de.de_LowCyl * scp))
447 && (volume->startblock <=
448 (((de.de_HighCyl + 1) * scp) - 1)))
449 break;
451 pn = (struct PartitionHandle *) pn->ln.ln_Succ;
453 if (pn->ln.ln_Succ)
455 tags[0].ti_Tag = PT_POSITION;
456 tags[0].ti_Data = (STACKIPTR) & type;
457 tags[1].ti_Tag = TAG_DONE;
458 GetPartitionAttrs(pn, tags);
459 volume->partnum = (UBYTE) type;
460 retval = TRUE;
461 D(bug
462 ("[install] Primary partition found: partnum=%d\n",
463 volume->partnum));
465 else if (extph != NULL)
467 if (OpenPartitionTable(extph) == 0)
469 tags[0].ti_Tag = PTT_TYPE;
470 tags[0].ti_Data = (STACKIPTR) & type;
471 tags[1].ti_Tag = TAG_DONE;
472 GetPartitionTableAttrs(extph, tags);
473 if (type == PHPTT_EBR)
475 tags[0].ti_Tag = PT_DOSENVEC;
476 tags[0].ti_Data = (STACKIPTR) & de;
477 tags[1].ti_Tag = TAG_DONE;
478 pn = (struct PartitionHandle *) extph->table->
479 list.lh_Head;
480 while (pn->ln.ln_Succ)
482 ULONG offset, scp;
484 offset = extph->de.de_LowCyl
485 * extph->de.de_Surfaces
486 * extph->de.de_BlocksPerTrack;
487 GetPartitionAttrs(pn, tags);
488 scp =
489 de.de_Surfaces * de.de_BlocksPerTrack;
490 if ((volume->startblock >=
491 offset + (de.de_LowCyl * scp))
492 && (volume->startblock <=
493 offset +
494 (((de.de_HighCyl + 1) * scp) -
495 1)))
496 break;
497 pn = (struct PartitionHandle *) pn->ln.
498 ln_Succ;
500 if (pn->ln.ln_Succ)
502 tags[0].ti_Tag = PT_POSITION;
503 tags[0].ti_Data = (STACKIPTR) & type;
504 GetPartitionAttrs(pn, tags);
505 volume->partnum =
506 MBR_MAX_PARTITIONS + (UBYTE) type;
507 retval = TRUE;
508 D(bug
509 ("[install] Logical partition found: partnum=%d\n",
510 (int) volume->partnum));
513 ClosePartitionTable(extph);
517 else
519 if (type == PHPTT_RDB)
521 /* just use whole hard disk */
522 retval = TRUE;
524 else
525 Printf
526 ("only MBR and RDB partition tables are supported\n");
528 ClosePartitionTable(ph);
530 else
532 /* just use whole hard disk */
533 retval = TRUE;
535 CloseRootPartition(ph);
537 else
538 Printf("Error OpenRootPartition(%s,%lu)\n", device, (long)unit);
539 CloseLibrary((struct Library *) PartitionBase);
541 else
542 Printf("Couldn't open partition.library\n");
543 return retval;
546 struct Volume *getGrubStageVolume(CONST_STRPTR device, ULONG unit,
547 ULONG flags, struct DosEnvec *de)
549 struct Volume *volume;
551 volume = initVolume(device, unit, flags, de);
553 D(bug("[install] getGrubStageVolume(): volume=%x\n", volume));
555 if (volume)
557 if (isvalidFileSystem(volume, device, unit))
558 return volume;
559 else
561 Printf("stage2 is on an unsupported file system\n");
562 PrintFault(ERROR_OBJECT_WRONG_TYPE, NULL);
564 uninitVolume(volume);
566 return 0;
569 BOOL isvalidPartition(CONST_STRPTR device, ULONG unit, LONG * pnum,
570 struct DosEnvec * de)
572 struct PartitionBase *PartitionBase;
573 struct PartitionHandle *ph;
574 ULONG type;
575 BOOL retval = FALSE;
577 D(bug
578 ("[install] isvalidPartition(%s:%d, part:%d)\n", device, unit, pnum));
580 PartitionBase =
581 (struct PartitionBase *) OpenLibrary((CONST_STRPTR)
582 "partition.library", 1);
583 if (PartitionBase)
585 ph = OpenRootPartition(device, unit);
586 if (ph)
588 struct TagItem tags[2];
590 tags[1].ti_Tag = TAG_DONE;
591 /* is there a partition table? */
592 if (OpenPartitionTable(ph) == 0)
594 if (pnum)
596 /* install into partition bootblock */
597 tags[0].ti_Tag = PTT_TYPE;
598 tags[0].ti_Data = (STACKIPTR) & type;
599 GetPartitionTableAttrs(ph, tags);
600 if (type == PHPTT_MBR)
602 struct PartitionHandle *pn;
604 /* search for partition */
605 tags[0].ti_Tag = PT_POSITION;
606 tags[0].ti_Data = (STACKIPTR) & type;
607 pn = (struct PartitionHandle *) ph->table->list.
608 lh_Head;
609 while (pn->ln.ln_Succ)
611 GetPartitionAttrs(pn, tags);
612 if (type == *pnum)
613 break;
614 pn = (struct PartitionHandle *) pn->ln.ln_Succ;
616 if (pn->ln.ln_Succ)
618 struct PartitionType ptype;
620 /* is it an AROS partition? */
621 tags[0].ti_Tag = PT_TYPE;
622 tags[0].ti_Data = (STACKIPTR) & ptype;
623 GetPartitionAttrs(pn, tags);
624 if (ptype.id[0] == 0x30)
626 tags[0].ti_Tag = PT_DOSENVEC;
627 tags[0].ti_Data = (STACKIPTR) de;
628 GetPartitionAttrs(pn, tags);
629 retval = TRUE;
631 else
632 Printf
633 ("partition is not of type AROS (0x30)\n");
635 else
637 Printf
638 ("partition %ld not found on device %s unit %lu\n",
639 (long)*pnum, device, (long)unit);
642 else
643 Printf
644 ("you can only install in partitions which are MBR partitioned\n");
646 else
648 /* install into MBR */
649 tags[0].ti_Tag = PTT_TYPE;
650 tags[0].ti_Data = (STACKIPTR) & type;
651 GetPartitionTableAttrs(ph, tags);
652 if ((type == PHPTT_MBR) || (type == PHPTT_RDB))
654 tags[0].ti_Tag = PT_DOSENVEC;
655 tags[0].ti_Data = (STACKIPTR) de;
656 GetPartitionAttrs(ph, tags);
657 retval = TRUE;
659 else
660 Printf
661 ("partition table type must be either MBR or RDB\n");
663 ClosePartitionTable(ph);
665 else
667 /* FIXME: GetPartitionAttr() should always work for root partition */
668 CopyMem(&ph->de, de, sizeof(struct DosEnvec));
669 retval = TRUE;
671 CloseRootPartition(ph);
673 else
674 Printf("Error OpenRootPartition(%s,%lu)\n", device, (long)unit);
675 CloseLibrary((struct Library *) PartitionBase);
677 else
678 Printf("Couldn't open partition.library\n");
679 return retval;
682 struct Volume *getBBVolume(CONST_STRPTR device, ULONG unit, LONG * partnum)
684 struct Volume *volume;
685 struct DosEnvec de;
687 D(bug("[install] getBBVolume(%s:%d, %d)\n", device, unit, partnum));
689 if (isvalidPartition(device, unit, partnum, &de))
691 volume = initVolume(device, unit, 0, &de);
692 volume->partnum = partnum ? *partnum : -1;
693 readBlock(volume, 0, volume->blockbuffer, 512);
694 if (AROS_BE2LONG(volume->blockbuffer[0]) != IDNAME_RIGIDDISK)
696 /* Clear the boot sector region! */
697 memset(volume->blockbuffer, 0x00, 446);
698 return volume;
700 else
701 Printf("no space for bootblock (RDB is on block 0)\n");
703 return NULL;
706 /* Convert a unit number into a drive number as understood by GRUB */
707 UWORD getDriveNumber(CONST_STRPTR device, ULONG unit)
709 struct PartitionHandle *ph;
710 ULONG i;
711 UWORD hd_count = 0;
713 for (i = 0; i < unit; i++)
715 ph = OpenRootPartition(device, i);
716 if (ph != NULL)
718 hd_count++;
719 CloseRootPartition(ph);
723 return hd_count;
726 BOOL writeBootIMG(STRPTR bootimgpath, struct Volume * bootimgvol, struct Volume * coreimgvol,
727 ULONG block /* first block of core.img file */, ULONG unit)
729 BOOL retval = FALSE;
730 LONG error = 0;
731 BPTR fh;
733 D(bug("[install] writeBootIMG(%x)\n", bootimgvol));
735 fh = Open(bootimgpath, MODE_OLDFILE);
736 if (fh)
738 if (Read(fh, bootimgvol->blockbuffer, 512) == 512)
740 /* install into MBR ? */
741 if ((bootimgvol->startblock == 0)
742 && (!(bootimgvol->flags & VF_IS_TRACKDISK)))
744 APTR boot_img = bootimgvol->blockbuffer;
746 UBYTE *boot_drive =
747 (UBYTE *) (boot_img + GRUB_BOOT_MACHINE_BOOT_DRIVE);
748 UWORD *boot_drive_check =
749 (UWORD *) (boot_img + GRUB_BOOT_MACHINE_DRIVE_CHECK);
751 if (unit == bootimgvol->unitnum)
752 *boot_drive = 0xFF;
753 else
754 *boot_drive = getDriveNumber(coreimgvol->device, unit)
755 | BIOS_HDISK_FLAG;
756 *boot_drive_check = 0x9090;
758 D(bug("[install] writeBootIMG: Install to HARDDISK\n"));
760 /* read old MBR */
761 error = readBlock(bootimgvol, 0, coreimgvol->blockbuffer, 512);
763 D(bug("[install] writeBootIMG: MBR Buffer @ %x\n", bootimgvol->blockbuffer));
764 D(bug("[install] writeBootIMG: Copying MBR BPB to %x\n",
765 (char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START));
766 /* copy BPB (BIOS Parameter Block) */
767 CopyMem
768 ((APTR) ((char *) coreimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START),
769 (APTR) ((char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_BPB_START),
770 (GRUB_BOOT_MACHINE_BPB_END - GRUB_BOOT_MACHINE_BPB_START));
772 /* copy partition table - [Overwrites Floppy boot code] */
773 D(bug("[install] writeBootIMG: Copying MBR Partitions to %x\n",
774 (char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC));
775 CopyMem((APTR) ((char *) coreimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC),
776 (APTR) ((char *) bootimgvol->blockbuffer + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC),
777 (GRUB_BOOT_MACHINE_PART_END - GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC));
779 /* Store the core.img pointer .. */
780 ULONG * coreimg_sector_start = (ULONG *) (boot_img
781 + GRUB_BOOT_MACHINE_KERNEL_SECTOR);
782 coreimg_sector_start[0] = block;
783 D(bug("[install] writeBootIMG: core.img pointer = %ld\n", block));
785 else
787 D(bug("[install] writeBootIMG: Install to FLOPPY\n"));
790 if (error == 0)
792 error = writeBlock(bootimgvol, 0, bootimgvol->blockbuffer, 512);
794 if (error)
795 Printf("WriteError %lu\n", (long)error);
796 else
797 retval = TRUE;
799 else
800 Printf("WriteError %lu\n", (long)error);
802 else
803 Printf("%s: Read Error\n", bootimgpath);
804 Close(fh);
806 else
807 PrintFault(IoErr(), bootimgpath);
809 return retval;
812 /* Collects the list of blocks that a file occupies on FFS filesystem */
813 ULONG collectBlockListFFS(struct Volume *volume, ULONG block, struct BlockNode *blocklist)
815 ULONG retval, first_block;
816 WORD blk_count,count;
817 UWORD i;
819 D(bug("[install] collectBlockListFFS(%x, %ld, %x)\n", volume, block, blocklist));
822 /* Clear the core.img sector pointers region! */
823 memset((UBYTE*)&blocklist[-BLCKLIST_ELEMENTS],0x00, BLCKLIST_ELEMENTS*sizeof(struct BlockNode));
826 The number of first block of core.img will be stored in boot.img
827 so skip the first filekey in the first loop
830 retval = _readwriteBlock(volume, block, volume->blockbuffer, volume->SizeBlock<<2,
831 volume->readcmd);
833 if (retval)
835 D(bug("[install] collectBlockListFFS: ERROR reading block (error: %ld\n", retval));
836 Printf("ReadError %lu\n", (long)retval);
837 return 0;
840 i = volume->SizeBlock - 52;
841 first_block = AROS_BE2LONG(volume->blockbuffer[volume->SizeBlock-51]);
842 blk_count=0;
844 D(bug("[install] collectBlockListFFS: First block @ %ld, i:%d\n", first_block, i));
849 retval = _readwriteBlock(volume, block, volume->blockbuffer, volume->SizeBlock<<2,
850 volume->readcmd);
851 if (retval)
853 D(bug("[install] collectBlockListFFS: ERROR reading block (error: %ld)\n", retval));
854 Printf("ReadError %lu\n", (long)retval);
855 return 0;
858 D(bug("[install] collectBlockListFFS: read block %ld, i = %d\n", block, i));
859 while ((i>=6) && (volume->blockbuffer[i]))
861 D(bug("[install] collectBlockListFFS: i = %d\n", i));
863 if current sector follows right after last sector
864 then we don't need a new element
866 if ((blocklist[blk_count].sector_lo) &&
867 ((blocklist[blk_count].sector_lo+blocklist[blk_count].count)==
868 AROS_BE2LONG(volume->blockbuffer[i])))
870 blocklist[blk_count].count += 1;
871 D(bug("[install] collectBlockListFFS: sector %d follows previous - increasing count of block %d to %d\n",
872 i, blk_count, blocklist[blk_count].count));
874 else
876 blk_count--; /* decrement first */
877 D(bug("[install] collectBlockListFFS: store new block (%d)\n", blk_count));
879 if ((blk_count-1) <= -BLCKLIST_ELEMENTS)
881 D(bug("[install] collectBlockListFFS: ERROR: out of block space at sector %d, block %d\n",
882 i, blk_count));
883 Printf("There is no more space to save blocklist in core.img\n");
884 return 0;
886 D(bug("[install] collectBlockListFFS: storing sector pointer for %d in block %d\n",
887 i, blk_count));
888 blocklist[blk_count].sector_lo = AROS_BE2LONG(volume->blockbuffer[i]);
889 blocklist[blk_count].sector_hi = 0;
890 blocklist[blk_count].count = 1;
892 i--;
894 i = volume->SizeBlock - 51;
895 block = AROS_BE2LONG(volume->blockbuffer[volume->SizeBlock - 2]);
896 D(bug("[install] collectBlockListFFS: next block %d, i = %d\n", block, i));
897 } while (block);
901 blocks in blocklist are relative to the first
902 sector of the HD (not partition)
905 D(bug("[install] collectBlockListFFS: successfully updated pointers for %d blocks\n", blk_count));
907 i = 0;
908 for (count=-1;count>=blk_count;count--)
910 blocklist[count].sector_lo += volume->startblock;
911 blocklist[count].seg_adr = 0x820 + (i*32);
912 i += blocklist[count].count;
913 D(bug("[install] collectBlockListFFS: correcting block %d for partition start\n", count));
914 D(bug("[install] collectBlockListFFS: sector : %ld seg_adr : %x\n",
915 blocklist[count].sector_lo, blocklist[count].seg_adr));
918 first_block += volume->startblock;
919 D(bug("[install] collectBlockListFFS: corrected first block for partition start: %ld\n", first_block));
921 return first_block;
924 /* Collects the list of blocks that a file occupies on SFS filesystem */
925 ULONG collectBlockListSFS(struct Volume *volume, ULONG objectnode, struct BlockNode *blocklist)
927 ULONG retval, first_block = 0;
928 WORD blk_count = 0, count = 0;
929 ULONG block_objectnoderoot = 0, block_sfsobjectcontainer = 0, block_extentbnoderoot = 0;
930 ULONG nextblock = 0, searchedblock = 0;
931 WORD i = 0;
932 UBYTE * tmpBytePtr = NULL;
934 D(bug("[install] collectBlockListSFS(startblock: %ld, objectnode: %ld)\n", volume->startblock, objectnode));
935 D(bug("[install] collectBlockListSFS(%ld, %d, %d)\n", volume->countblock, volume->SizeBlock, volume->partnum));
937 /* Clear the core.img sector pointers region! */
938 memset((UBYTE*)&blocklist[-BLCKLIST_ELEMENTS],0x00, BLCKLIST_ELEMENTS*sizeof(struct BlockNode));
940 /* Description of actions:
941 * 1. Load SFS root block
942 * 2. From root block find the block containing root of objectnodes
943 * 3. Traverse the tree of objectnodes until block of objectdescriptor is found
944 * 4. Search the objectdescriptor for entry matching given objectnode from entry read the
945 * first block of file
946 * 5. Having first file block, find the extentbnode for that block and read number
947 * of blocks. Put first block and number of blocks into BlockList.
948 * 6. If the file has more blocks than this exntentbnode hold, find first file
949 * block in next extentbnode. Go to step 5.
950 * Use the SFS source codes for reference. They operate on structures not pointers
951 * and are much easier to understand.
954 /* Read root block */
955 retval = _readwriteBlock(volume, 0, volume->blockbuffer, volume->SizeBlock<<2,
956 volume->readcmd);
958 if (retval)
960 D(bug("[install] collectBlockListSFS: ERROR reading root block (error: %ld)\n", retval));
961 Printf("ReadError %lu\n", (long)retval);
962 return 0;
965 /* Get block pointers from root block */
966 block_objectnoderoot = AROS_BE2LONG(volume->blockbuffer[28]); /* objectnoderoot - 29th ULONG */
967 block_extentbnoderoot = AROS_BE2LONG(volume->blockbuffer[27]); /* extentbnoderoot - 28th ULONG */
969 D(bug("[install] collectBlockListSFS: objectnoderoot: %ld, extentbnoderoot %ld\n",
970 block_objectnoderoot, block_extentbnoderoot));
974 /* Find the SFSObjectContainer block for given objectnode */
975 /* Reference: SFS, nodes.c, function findnode */
976 nextblock = block_objectnoderoot;
977 D(bug("[install] collectBlockListSFS: searching in nextblock %d for sfsobjectcontainer for objectnode %ld\n",
978 nextblock, objectnode));
979 while(1)
981 _readwriteBlock(volume, nextblock, volume->blockbuffer, volume->SizeBlock<<2,
982 volume->readcmd);
984 /* If nodes == 1, we are at the correct nodecontainer, else go to next nodecontainer */
985 if (AROS_BE2LONG(volume->blockbuffer[4]) == 1)
987 /* read entry from position: be_node + sizeof(fsObjectNode) * (objectnode - be_nodenumber) */
988 tmpBytePtr = (UBYTE*)volume->blockbuffer;
989 ULONG index = 20 + 10 * (objectnode - AROS_BE2LONG(volume->blockbuffer[3]));
990 block_sfsobjectcontainer = AROS_BE2LONG(((ULONG*)(tmpBytePtr + index))[0]);
991 D(bug("[install] collectBlockListSFS: leaf found in nextblock %ld, sfsobjectcontainer block is %ld \n",
992 nextblock, block_sfsobjectcontainer));
993 break;
995 else
997 UWORD containerentry =
998 (objectnode - AROS_BE2LONG(volume->blockbuffer[3]))/AROS_BE2LONG(volume->blockbuffer[4]);
999 nextblock = AROS_BE2LONG(volume->blockbuffer[containerentry + 5]) >> 4; /* 9-5 (2^9 = 512) */;
1000 D(bug("[install] collectBlockListSFS: check next block %ld\n", nextblock));
1004 if (block_sfsobjectcontainer == 0)
1006 D(bug("[install] collectBlockListSFS: SFSObjectContainer not found\n"));
1007 Printf("SFSObjectContainer not found\n");
1008 return 0;
1013 /* Find the SFSObject in SFSObjectContainer for given objectnode */
1014 first_block = 0;
1015 while((block_sfsobjectcontainer != 0) && (first_block == 0))
1017 /* Read next SFS container block */
1018 retval = _readwriteBlock(volume, block_sfsobjectcontainer, volume->blockbuffer, volume->SizeBlock<<2,
1019 volume->readcmd);
1021 if (retval)
1023 D(bug("[install] collectBlockListSFS: ERROR reading block (error: %ld)\n", retval));
1024 Printf("ReadError %lu\n", (long)retval);
1025 return 0;
1028 /* Iterate over SFS objects and match the objectnode */
1030 * The first offset comes from :
1031 * sizeof(sfsblockheader) = uint32 + uint32 + uint32 (field of sfsobjectcontainer)
1032 * parent, next, previous = uint32 + uint32 + uint32 (fields of sfsobjectcontainers)
1034 tmpBytePtr = ((UBYTE*)volume->blockbuffer) + 12 + 12; /* tmpBytePtr points to first object in container */
1036 while (AROS_BE2LONG(((ULONG*)(tmpBytePtr + 4))[0]) > 0) /* check on the objectnode field */
1039 /* Compare objectnode */
1040 if (AROS_BE2LONG(((ULONG*)(tmpBytePtr + 4))[0]) == objectnode)
1042 /* Found! */
1043 first_block = AROS_BE2LONG(((ULONG*)(tmpBytePtr + 12))[0]); /* data */
1044 D(bug("[install] collectBlockListSFS: first block is %ld\n", first_block));
1045 break;
1048 /* Move to next object */
1049 /* Find end of name and end of comment */
1050 tmpBytePtr += 25; /* Point to name */
1051 count = 0;
1052 for (i = 2; i > 0; tmpBytePtr++, count++)
1053 if (*tmpBytePtr == '\0')
1054 i--;
1056 /* Correction for aligment */
1057 if ((count & 0x01) == 0 )
1058 tmpBytePtr++;
1061 /* Move to next sfs object container block */
1062 block_sfsobjectcontainer = AROS_BE2LONG(volume->blockbuffer[4]); /* next field */
1066 if (first_block == 0)
1068 D(bug("[install] collectBlockListSFS: First block not found\n"));
1069 Printf("First block not found\n");
1070 return 0;
1075 /* First file block found. Find all blocks of file */
1076 searchedblock = first_block;
1077 blk_count = 0;
1079 while(1)
1081 nextblock = block_extentbnoderoot;
1082 UBYTE * BNodePtr = NULL;
1084 while(1)
1086 /* Find the extentbnode for this block */
1088 D(bug("[install] collectBlockListSFS: searching in nextblock %d for extentbnode for block %ld\n",
1089 nextblock, searchedblock));
1091 UBYTE * BTreeContainerPtr = NULL;
1092 BNodePtr = NULL;
1094 _readwriteBlock(volume, nextblock, volume->blockbuffer, volume->SizeBlock<<2,
1095 volume->readcmd);
1097 BTreeContainerPtr = (UBYTE*)(volume->blockbuffer + 3); /* Starts right after the header */
1099 D(bug("[install] collectBlockListSFS: tree container nodecount: %d\n",
1100 AROS_BE2WORD(((UWORD*)BTreeContainerPtr)[0])));
1102 for (i = AROS_BE2WORD(((UWORD*)BTreeContainerPtr)[0]) - 1; i >=0; i--) /* Start from last element */
1104 /* Read the BNode */
1105 tmpBytePtr = BTreeContainerPtr + 4 + i * BTreeContainerPtr[3];
1107 if (AROS_BE2LONG(((ULONG*)(tmpBytePtr))[0]) <= searchedblock) /* Check on the key field */
1109 BNodePtr = tmpBytePtr;
1110 break;
1114 /* Fail if BNodePtr still NULL */
1115 if (BNodePtr == NULL)
1117 D(bug("[install] collectBlockListSFS: Failed to travers extentbnode tree.\n"));
1118 Printf("Failed to travers extentbnode tree.\n");
1119 return 0;
1122 /* If we are at the leaf, stop */
1123 if (BTreeContainerPtr[2])
1124 break;
1126 /* Else search further */
1127 nextblock = AROS_BE2LONG(((ULONG*)(BNodePtr))[1]); /* data / next field */
1130 /* Found. Add BlockList entry */
1131 D(bug("[install] collectBlockListSFS: extentbnode for block %ld found. Block count: %d\n",
1132 searchedblock, AROS_BE2WORD(((UWORD*)(BNodePtr + 12))[0])));
1134 /* Add blocklist entry */
1135 blk_count--;
1137 /* Check if we still have spece left to add data to BlockList */
1138 if ((blk_count-1) <= -BLCKLIST_ELEMENTS)
1140 D(bug("[install] collectBlockListSFS: ERROR: out of block space\n"));
1141 Printf("There is no more space to save blocklist in core.img\n");
1142 return 0;
1145 blocklist[blk_count].sector_lo = searchedblock;
1146 blocklist[blk_count].sector_hi = 0;
1147 blocklist[blk_count].count = AROS_BE2WORD(((UWORD*)(BNodePtr + 12))[0]);
1149 /* Handling of special situations */
1150 if (searchedblock == first_block)
1152 /* Writting first pack of blocks. Pointer needs to point to second file block */
1153 blocklist[blk_count].sector_lo++;
1154 blocklist[blk_count].count--;
1155 if (blocklist[blk_count].count == 0)
1157 /* This means that the first pack of blocks contained only one block - first block */
1158 /* Since the first blocklist needs to start at second file block, 'reset' the blk_count */
1159 /* so that next iteration will overwrite the current results */
1160 blk_count++;
1164 /* Are there more blocks to read? */
1165 if (AROS_BE2LONG(((ULONG*)(BNodePtr))[1]) == 0)
1167 D(bug("[install] collectBlockListSFS: All core.img blocks found!\n"));
1168 break;
1170 else
1171 searchedblock = AROS_BE2LONG(((ULONG*)(BNodePtr))[1]); /* data / next field */
1175 /* Correct blocks for volume start */
1177 /* Blocks in blocklist are relative to the first sector of the HD (not partition) */
1178 i = 0;
1179 for (count=-1;count>=blk_count;count--)
1181 blocklist[count].sector_lo += volume->startblock;
1182 blocklist[count].seg_adr = 0x820 + (i*32);
1183 i += blocklist[count].count;
1184 D(bug("[install] collectBlockListFFS: correcting block %d for partition start\n", count));
1185 D(bug("[install] collectBlockListFFS: sector : %ld seg_adr : %x\n",
1186 blocklist[count].sector_lo, blocklist[count].seg_adr));
1189 first_block += volume->startblock;
1191 return first_block;
1194 /* Flushes the cache on the volume containing the specified path. */
1195 VOID flushFS(CONST_STRPTR path)
1197 TEXT devname[256];
1198 UWORD i;
1200 for (i = 0; path[i] != ':'; i++)
1201 devname[i] = path[i];
1202 devname[i++] = ':';
1203 devname[i] = '\0';
1205 /* Try to flush 10 times. 5 seconds total */
1207 /* Failsafe in case first Inhibit fails in some way (was needed
1208 * for SFS because non flushed data was failing Inhibit) */
1210 for (i = 0; i < 10; i++)
1212 if (Inhibit(devname, DOSTRUE))
1214 Inhibit(devname, DOSFALSE);
1215 break;
1217 else
1218 Delay(25);
1222 BOOL writeCoreIMG(BPTR fh, UBYTE *buffer, struct Volume *volume)
1224 BOOL retval = FALSE;
1225 LONG error;
1227 D(bug("[install] writeCoreIMG(%x)\n", volume));
1229 if (Seek(fh, 0, OFFSET_BEGINNING) != -1)
1231 D(bug("[install] writeCoreIMG - write first block\n"));
1233 /* write back first block */
1234 if (Write(fh, buffer, 512) == 512)
1238 /* read second core.img block */
1239 if (Read(fh, buffer, 512) == 512)
1241 /* set partition number where core.img is on */
1242 /* FIXME: set RDB part number of DH? */
1243 UBYTE *install_boot_device =
1244 buffer + GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE;
1246 D(bug("[install] set dos part = %d\n", volume->partnum));
1248 install_boot_device[0] = 0;
1249 install_boot_device[1] = 0;
1250 install_boot_device[2] = volume->partnum;
1252 /* write second core.img block back */
1253 if (Seek(fh, -512, OFFSET_CURRENT) != -1)
1255 if (Write(fh, buffer, 512) == 512)
1257 retval = TRUE;
1259 else
1260 Printf("Write Error\n");
1262 else
1263 Printf("Seek Error\n");
1265 else
1266 Printf("Read Error\n");
1268 else
1269 Printf("Write Error\n");
1271 else
1273 error = IoErr();
1274 Printf("Seek Error\n");
1275 PrintFault(error, NULL);
1277 return retval;
1280 ULONG updateCoreIMG(CONST_STRPTR grubpath, /* path of grub dir */
1281 struct Volume *volume, /* volume core.img is on */
1282 ULONG *buffer /* a buffer of at least 512 bytes */)
1284 ULONG block = 0;
1285 struct FileInfoBlock fib;
1286 BPTR fh;
1287 TEXT coreimgpath[256];
1289 D(bug("[install] updateCoreIMG(%x)\n", volume));
1291 AddPart(coreimgpath, grubpath, 256);
1292 AddPart(coreimgpath, CORE_IMG_FILE_NAME, 256);
1293 fh = Open(coreimgpath, MODE_OLDFILE);
1294 if (fh)
1296 if (ExamineFH(fh, &fib))
1298 if (Read(fh, buffer, 512) == 512)
1301 Get and store all blocks of core.img in first block of core.img.
1302 First block of core.img will be returned.
1303 List of BlockNode starts at 512 - sizeof(BlockNode). List grows downwards.
1304 buffer is ULONG, buffer[128] is one pointer after first element(upwards).
1305 collectBlockList assumes it receives one pointer after first element(upwards).
1308 if (volume->dos_id == ID_SFS_BE_DISK)
1310 D(bug("[install] core.img on SFS file system\n"));
1311 block = collectBlockListSFS
1312 (volume, fib.fib_DiskKey, (struct BlockNode *)&buffer[128]);
1314 else
1315 if ((volume->dos_id == ID_FFS_DISK) || (volume->dos_id == ID_INTER_DOS_DISK) ||
1316 (volume->dos_id == ID_INTER_FFS_DISK) || (volume->dos_id == ID_FASTDIR_DOS_DISK) ||
1317 (volume->dos_id == ID_FASTDIR_FFS_DISK))
1319 D(bug("[install] core.img on FFS file system\n"));
1320 block = collectBlockListFFS
1321 (volume, fib.fib_DiskKey, (struct BlockNode *)&buffer[128]);
1323 else
1325 block = 0;
1326 D(bug("[install] core.img on unsupported file system\n"));
1327 Printf("Unsupported file system\n");
1330 D(bug("[install] core.img first block: %ld\n", block));
1332 if (block)
1334 if (!writeCoreIMG(fh, (UBYTE *)buffer, volume))
1335 block = 0;
1338 else
1339 Printf("%s: Read Error\n", coreimgpath);
1341 else
1342 PrintFault(IoErr(), coreimgpath);
1344 Close(fh);
1347 else
1348 PrintFault(IoErr(), coreimgpath);
1349 return block;
1352 /* Installs boot.img to MBR and updates core.img */
1353 BOOL installGrubFiles(struct Volume *coreimgvol, /* core.img volume */
1354 CONST_STRPTR grubpath, /* path to grub files */
1355 ULONG unit, /* unit core.img is on */
1356 struct Volume *bootimgvol) /* boot device for boot.img */
1358 BOOL retval = FALSE;
1359 TEXT bootimgpath[256];
1360 ULONG block;
1362 D(bug("[install] installStageFiles(%x)\n", bootimgvol));
1364 /* Flush GRUB volume's cache */
1365 flushFS(grubpath);
1367 block = updateCoreIMG(grubpath, coreimgvol, bootimgvol->blockbuffer);
1369 if (block)
1371 AddPart(bootimgpath, grubpath, 256);
1372 AddPart(bootimgpath, (CONST_STRPTR) "i386-pc/boot.img", 256);
1373 if (writeBootIMG(bootimgpath, bootimgvol, coreimgvol, block, unit))
1374 retval = TRUE;
1376 else
1377 bug("failed %d\n", IoErr());
1379 return retval;
1382 int main(int argc, char **argv)
1384 struct RDArgs *rdargs;
1385 struct Volume *grubvol;
1386 struct Volume *bbvol;
1387 struct FileSysStartupMsg *fssm;
1388 int ret = RETURN_OK;
1390 D(bug("[install] main()\n"));
1392 rdargs = ReadArgs(template, myargs, NULL);
1393 if (rdargs)
1395 CONST_STRPTR bootDevice = (CONST_STRPTR) myargs[0];
1396 LONG unit = *(LONG *) myargs[1];
1397 LONG *partnum = (LONG *) myargs[2];
1398 CONST_STRPTR grubpath = (CONST_STRPTR) myargs[3];
1400 D(bug("[install] FORCELBA = %d\n", myargs[4]));
1401 if (myargs[4])
1402 Printf("FORCELBA ignored\n");
1404 if (partnum)
1406 Printf("PARTITIONNUMBER not supported yet\n");
1407 FreeArgs(rdargs);
1408 return RETURN_ERROR;
1411 fssm = getDiskFSSM(grubpath);
1412 if (fssm != NULL)
1414 CONST_STRPTR grubDevice = AROS_BSTR_ADDR(fssm->fssm_Device);
1416 if (!strcmp((const char *) grubDevice, (const char *) bootDevice))
1418 struct DosEnvec *dosEnvec;
1419 dosEnvec = (struct DosEnvec *) BADDR(fssm->fssm_Environ);
1421 grubvol = getGrubStageVolume(grubDevice, fssm->fssm_Unit,
1422 fssm->fssm_Flags, dosEnvec);
1423 if (grubvol)
1426 bbvol = getBBVolume(bootDevice, unit, partnum);
1427 if (bbvol)
1429 if (!installGrubFiles(grubvol, grubpath,
1430 fssm->fssm_Unit, bbvol))
1431 ret = RETURN_ERROR;
1433 uninitVolume(bbvol);
1435 else
1437 D(bug("getBBVolume failed miserably\n"));
1438 ret = RETURN_ERROR;
1441 uninitVolume(grubvol);
1444 else
1446 Printf("%s is not on device %s unit %ld\n",
1447 grubpath, bootDevice, (long)unit);
1448 ret = RETURN_ERROR;
1451 else if (fssm)
1453 Printf("kernel path must begin with a device name\n");
1454 FreeArgs(rdargs);
1455 ret = RETURN_ERROR;
1458 FreeArgs(rdargs);
1460 else
1461 PrintFault(IoErr(), (STRPTR) argv[0]);
1463 return ret;