a4091-software/mounter.c
2022-10-02 12:59:57 -07:00

970 lines
26 KiB
C

// Generic autoboot/automount RDB parser and mounter.
// - KS 1.3 support, including autoboot mode.
// - 68000 compatible.
// - Boot ROM and executable modes.
// - Autoboot capable (Boot ROM mode only).
// - Full automount support
// - Full RDB filesystem support.
//
// Copyright 2021-2022 Toni Wilen
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
#ifdef DEBUG_MOUNTER
#define USE_SERIAL_OUTPUT
#endif
#include "port.h"
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/alerts.h>
#include <exec/ports.h>
#include <exec/execbase.h>
#include <exec/io.h>
#include <devices/trackdisk.h>
#include <devices/hardblocks.h>
#include <resources/filesysres.h>
#include <libraries/expansion.h>
#include <libraries/expansionbase.h>
#include <libraries/configvars.h>
#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/doshunks.h>
#include <string.h>
#include <stdio.h>
#include <proto/exec.h>
#include <proto/expansion.h>
#include <proto/dos.h>
#include "ndkcompat.h"
#include "mounter.h"
#define TRACE 1
#define Trace printf
#if TRACE
#define dbg Trace
#else
#define dbg
#endif
#define MAX_BLOCKSIZE 2048
#define LSEG_DATASIZE (512 / 4 - 5)
#if NO_CONFIGDEV
extern UBYTE entrypoint, entrypoint_end;
extern UBYTE bootblock, bootblock_end;
#endif
struct MountData
{
struct ExecBase *SysBase;
struct ExpansionBase *ExpansionBase;
struct DosLibrary *DOSBase;
struct IOExtTD *request;
struct ConfigDev *configDev;
const UBYTE *creator;
const UBYTE *devicename;
ULONG lsegblock;
ULONG lseglongs;
ULONG lsegoffset;
struct LoadSegBlock *lsegbuf;
UWORD lsegwordbuf;
UWORD lseghasword;
ULONG unitnum;
LONG ret;
UBYTE buf[MAX_BLOCKSIZE * 3];
UBYTE zero[2];
BOOL wasLastDev;
int blocksize;
};
// KS 1.3 compatibility functions
APTR W_CreateIORequest(struct MsgPort *ioReplyPort, ULONG size, struct ExecBase *SysBase)
{
struct IORequest *ret = NULL;
if(ioReplyPort == NULL)
return NULL;
ret = (struct IORequest*)AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR);
if(ret != NULL)
{
ret->io_Message.mn_ReplyPort = ioReplyPort;
ret->io_Message.mn_Length = size;
}
return ret;
}
void W_DeleteIORequest(APTR iorequest, struct ExecBase *SysBase)
{
if(iorequest != NULL) {
FreeMem(iorequest, ((struct Message*)iorequest)->mn_Length);
}
}
struct MsgPort *W_CreateMsgPort(struct ExecBase *SysBase)
{
struct MsgPort *ret;
ret = (struct MsgPort*)AllocMem(sizeof(struct MsgPort), MEMF_PUBLIC | MEMF_CLEAR);
if(ret != NULL)
{
BYTE sb = AllocSignal(-1);
if (sb != -1)
{
ret->mp_Flags = PA_SIGNAL;
ret->mp_Node.ln_Type = NT_MSGPORT;
NewList(&ret->mp_MsgList);
ret->mp_SigBit = sb;
ret->mp_SigTask = FindTask(NULL);
return ret;
}
FreeMem(ret, sizeof(struct MsgPort));
}
return NULL;
}
void W_DeleteMsgPort(struct MsgPort *port, struct ExecBase *SysBase)
{
if(port != NULL)
{
FreeSignal(port->mp_SigBit);
FreeMem(port, sizeof(struct MsgPort));
}
}
// Flush cache (Filesystem relocation)
static void cacheclear(struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
if (SysBase->LibNode.lib_Version >= 37) {
CacheClearU();
}
}
// Simply memory copy.
// Only used for few short copies, it does not need to be optimal.
// Required because compiler built-in memcpy() can have
// extra dependencies which will make boot rom build
// impossible.
static void copymem(void *dstp, void *srcp, UWORD size)
{
UBYTE *dst = (UBYTE*)dstp;
UBYTE *src = (UBYTE*)srcp;
while (size != 0) {
*dst++ = *src++;
size--;
}
}
// Check block checksum
static UWORD checksum(UBYTE *buf, struct MountData *md)
{
ULONG chk = 0;
for (UWORD i = 0; i < md->blocksize; i += 4) {
ULONG v = (buf[i + 0] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | (buf[i + 3 ] << 0);
chk += v;
}
if (chk) {
dbg("Checksum error %08"PRIx32"\n", chk);
return FALSE;
}
return TRUE;
}
#define MAX_RETRIES 3
// Read single block with retries
static BOOL readblock(UBYTE *buf, ULONG block, ULONG id, struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
struct IOExtTD *request = md->request;
UWORD i;
request->iotd_Req.io_Command = CMD_READ;
request->iotd_Req.io_Offset = block << 9;
request->iotd_Req.io_Data = buf;
request->iotd_Req.io_Length = md->blocksize;
for (i = 0; i < MAX_RETRIES; i++) {
LONG err = DoIO((struct IORequest*)request);
if (!err) {
break;
}
dbg("Read block %"PRIu32" error %"PRId32"\n", block, err);
}
if (i == MAX_RETRIES) {
return FALSE;
}
ULONG v = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
dbg("Read block %"PRIu32" %08"PRIx32"\n", block, v);
if (id != 0xffffffff) {
if (v != id) {
return FALSE;
}
}
if (!checksum(buf, md)) {
return FALSE;
}
return TRUE;
}
// Read multiple longs from LSEG blocks
static BOOL lseg_read_longs(struct MountData *md, ULONG longs, ULONG *data)
{
dbg("lseg_read_longs, longs %"PRId32" ptr %p, remaining %"PRId32"\n", longs, data, md->lseglongs);
ULONG cnt = 0;
md->lseghasword = FALSE;
while (longs > cnt) {
if (md->lseglongs > 0) {
data[cnt] = md->lsegbuf->lsb_LoadData[md->lsegoffset];
md->lsegoffset++;
md->lseglongs--;
cnt++;
if (longs == cnt) {
return TRUE;
}
}
if (!md->lseglongs) {
if (md->lsegblock == 0xffffffff) {
dbg("lseg_read_long premature end!\n");
return FALSE;
}
if (!readblock((UBYTE*)md->lsegbuf, md->lsegblock, IDNAME_LOADSEG, md)) {
return FALSE;
}
md->lseglongs = LSEG_DATASIZE;
md->lsegoffset = 0;
dbg("lseg_read_long lseg block %"PRId32" loaded, next %"PRId32"\n", md->lsegblock, md->lsegbuf->lsb_Next);
md->lsegblock = md->lsegbuf->lsb_Next;
}
}
return TRUE;
}
// Read single long from LSEG blocks
static BOOL lseg_read_long(struct MountData *md, ULONG *data)
{
BOOL v;
if (md->lseghasword) {
ULONG temp;
v = lseg_read_longs(md, 1, &temp);
*data = (md->lsegwordbuf << 16) | (temp >> 16);
md->lsegwordbuf = (UWORD)temp;
md->lseghasword = TRUE;
} else {
v = lseg_read_longs(md, 1, data);
}
dbg("lseg_read_long %08"PRIx32"\n", *data);
return v;
}
// Read single word from LSEG blocks
// Internally reads long and buffers second word.
static BOOL lseg_read_word(struct MountData *md, ULONG *data)
{
if (md->lseghasword) {
*data = md->lsegwordbuf;
md->lseghasword = FALSE;
dbg("lseg_read_word 2/2 %08"PRIx32"\n", *data);
return TRUE;
}
ULONG temp;
BOOL v = lseg_read_longs(md, 1, &temp);
if (v) {
md->lseghasword = TRUE;
md->lsegwordbuf = (UWORD)temp;
*data = temp >> 16;
}
dbg("lseg_read_word 1/2 %08"PRIx32"\n", *data);
return v;
}
struct RelocHunk
{
ULONG hunkSize;
ULONG *hunkData;
};
// Filesystem relocator
static APTR fsrelocate(struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
ULONG data;
struct RelocHunk *relocHunks;
LONG firstHunk, lastHunk;
LONG totalHunks;
WORD hunkCnt;
WORD ret = 0;
APTR firstProcessedHunk = NULL;
if (!lseg_read_long(md, &data)) {
return NULL;
}
if (data != HUNK_HEADER) {
return NULL;
}
// skip first two longs
lseg_read_long(md, &firstHunk);
lseg_read_long(md, &firstHunk);
firstHunk = lastHunk = -1;
// first hunk
lseg_read_long(md, &firstHunk);
// last hunk
lseg_read_long(md, &lastHunk);
if (firstHunk < 0 || lastHunk < 0 || firstHunk > lastHunk) {
return NULL;
}
totalHunks = lastHunk - firstHunk + 1;
dbg("first hunk %"PRId32", last hunk %"PRId32"\n", firstHunk, lastHunk);
relocHunks = AllocMem(totalHunks * sizeof(struct RelocHunk), MEMF_CLEAR);
if (!relocHunks) {
return NULL;
}
// Pre-allocate hunks
ULONG *prevChunk = NULL;
hunkCnt = 0;
while (hunkCnt < totalHunks) {
struct RelocHunk *rh = &relocHunks[hunkCnt];
ULONG hunkHeadSize;
ULONG memoryFlags = MEMF_PUBLIC;
if (!lseg_read_long(md, &hunkHeadSize)) {
goto end;
}
if ((hunkHeadSize & (HUNKF_CHIP | HUNKF_FAST)) == (HUNKF_CHIP | HUNKF_FAST)) {
if (!lseg_read_long(md, &memoryFlags)) {
goto end;
}
} else if (hunkHeadSize & HUNKF_CHIP) {
memoryFlags |= MEMF_CHIP;
}
hunkHeadSize &= ~(HUNKF_CHIP | HUNKF_FAST);
rh->hunkSize = hunkHeadSize;
rh->hunkData = AllocMem((hunkHeadSize + 2) * sizeof(ULONG), memoryFlags | MEMF_CLEAR);
if (!rh->hunkData) {
goto end;
}
dbg("hunk %"PRId32": ptr %p, size %"PRId32", memory flags %08"PRIx32"\n", hunkCnt + firstHunk, rh->hunkData, hunkHeadSize, memoryFlags);
rh->hunkData[0] = rh->hunkSize + 2;
rh->hunkData[1] = MKBADDR(prevChunk);
prevChunk = &rh->hunkData[1];
rh->hunkData += 2;
if (!firstProcessedHunk) {
firstProcessedHunk = (APTR)(rh->hunkData - 1);
}
hunkCnt++;
}
dbg("hunks allocated\n");
// Load hunks/relocate
hunkCnt = 0;
struct RelocHunk *rh = NULL;
while (hunkCnt < totalHunks) {
ULONG hunkType;
if (!lseg_read_long(md, &hunkType)) {
goto end;
}
dbg("HUNK %08"PRIx32"\n", hunkType);
switch(hunkType)
{
case HUNK_CODE:
case HUNK_DATA:
case HUNK_BSS:
{
ULONG hunkSize;
rh = &relocHunks[hunkCnt++];
if (!lseg_read_long(md, &hunkSize)) {
goto end;
}
if (hunkSize > rh->hunkSize) {
goto end;
}
if (hunkType != HUNK_BSS) {
if (!lseg_read_longs(md, hunkSize, rh->hunkData)) {
goto end;
}
}
}
break;
case HUNK_RELOC32:
case HUNK_RELOC32SHORT:
{
ULONG relocCnt, relocHunk;
if (rh == NULL) {
goto end;
}
for (;;) {
if (!lseg_read_long(md, &relocCnt)) {
goto end;
}
if (!relocCnt) {
break;
}
if (!lseg_read_long(md, &relocHunk)) {
goto end;
}
relocHunk -= firstHunk;
if (relocHunk >= totalHunks) {
goto end;
}
dbg("HUNK_RELOC32: relocs %"PRId32" hunk %"PRId32"\n", relocCnt, relocHunk + firstHunk);
struct RelocHunk *rhr = &relocHunks[relocHunk];
while (relocCnt != 0) {
ULONG relocOffset;
if (hunkType == HUNK_RELOC32SHORT) {
if (!lseg_read_word(md, &relocOffset)) {
goto end;
}
} else {
if (!lseg_read_long(md, &relocOffset)) {
goto end;
}
}
if (relocOffset > (rh->hunkSize - 1) * sizeof(ULONG)) {
goto end;
}
UBYTE *hData = (UBYTE*)rh->hunkData + relocOffset;
if (relocOffset & 1) {
// Odd address, 68000/010 support.
ULONG v = (hData[0] << 24) | (hData[1] << 16) | (hData[2] << 8) | (hData[3] << 0);
v += (ULONG)rhr->hunkData;
hData[0] = v >> 24;
hData[1] = v >> 16;
hData[2] = v >> 8;
hData[3] = v >> 0;
} else {
*((ULONG*)hData) += (ULONG)rhr->hunkData;
}
relocCnt--;
}
}
}
break;
case HUNK_END:
// do nothing
break;
default:
dbg("Unexpected HUNK!\n");
goto end;
}
}
ret = 1;
end:
if (!ret) {
dbg("reloc failed\n");
hunkCnt = 0;
while (hunkCnt < totalHunks) {
struct RelocHunk *rh = &relocHunks[hunkCnt];
if (rh->hunkData) {
FreeMem(rh->hunkData - 2, (rh->hunkSize + 2) * sizeof(ULONG));
}
hunkCnt++;
}
firstProcessedHunk = NULL;
} else {
cacheclear(md);
dbg("reloc ok, first hunk %p\n", firstProcessedHunk);
}
FreeMem(relocHunks, totalHunks * sizeof(struct RelocHunk));
return firstProcessedHunk;
}
// Scan FileSystem.resource, create new if it is not found or existing entry has older version number.
static struct FileSysEntry *FSHDProcess(struct FileSysHeaderBlock *fshb, ULONG dostype, ULONG version, BOOL newOnly, struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
struct FileSysEntry *fse = NULL;
const UBYTE *creator = md->creator ? md->creator : md->zero;
Forbid();
struct FileSysResource *fsr = OpenResource(FSRNAME);
if (!fsr) {
// FileSystem.resource didn't exist (KS 1.3), create it.
struct FileSysResource *fsr = AllocMem(sizeof(struct FileSysResource) + strlen(creator) + 1, MEMF_PUBLIC | MEMF_CLEAR);
if (fsr) {
NewList(&fsr->fsr_FileSysEntries);
strcpy((UBYTE*)(fsr + 1), creator);
fsr->fsr_Creator = (UBYTE*)(fsr + 1);
AddTail(&SysBase->ResourceList, &fsr->fsr_Node);
}
dbg("FileSystem.resource created %p\n", fsr);
}
if (fsr) {
fse = (struct FileSysEntry*)fsr->fsr_FileSysEntries.lh_Head;
while (fse->fse_Node.ln_Succ) {
if (fse->fse_DosType == dostype) {
if (fse->fse_Version >= version) {
// FileSystem.resource filesystem is same or newer, don't update
if (newOnly) {
dbg("FileSystem.resource scan: %p dostype %08"PRIx32" found, FSRES version %08"PRIx32" >= FSHD version %08"PRIx32"\n", fse, dostype, fse->fse_Version, version);
fse = NULL;
}
goto end;
}
}
fse = (struct FileSysEntry*)fse->fse_Node.ln_Succ;
}
if (fshb && newOnly) {
fse = AllocMem(sizeof(struct FileSysEntry) + strlen(creator) + 1, MEMF_PUBLIC | MEMF_CLEAR);
if (fse) {
// Process patchflags
ULONG *dstPatch = &fse->fse_Type;
ULONG *srcPatch = &fshb->fhb_Type;
ULONG patchFlags = fshb->fhb_PatchFlags;
while (patchFlags) {
*dstPatch++ = *srcPatch++;
patchFlags >>= 1;
}
fse->fse_DosType = fshb->fhb_DosType;
fse->fse_Version = fshb->fhb_Version;
fse->fse_PatchFlags = fshb->fhb_PatchFlags;
strcpy((UBYTE*)(fse + 1), creator);
fse->fse_Node.ln_Name = (UBYTE*)(fse + 1);
}
dbg("FileSystem.resource scan: dostype %08"PRIx32" not found or old version: created new\n", dostype);
}
}
end:
Permit();
return fse;
}
// Add new FileSysEntry to FileSystem.resource or free it if filesystem load failed.
static void FSHDAdd(struct FileSysEntry *fse, struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
if (fse->fse_SegList) {
Forbid();
struct FileSysResource *fsr = OpenResource(FSRNAME);
if (fsr) {
AddHead(&fsr->fsr_FileSysEntries, &fse->fse_Node);
dbg("FileSysEntry %p added to FileSystem.resource, dostype %08"PRIx32"\n", fse, fse->fse_DosType);
fse = NULL;
}
Permit();
}
if (fse) {
dbg("FileSysEntry %p freed, dostype %08"PRIx32"\n", fse, fse->fse_DosType);
FreeMem(fse, sizeof(struct FileSysEntry));
}
}
// Parse FileSystem Header Blocks, load and relocate filesystem if needed.
static struct FileSysEntry *ParseFSHD(UBYTE *buf, ULONG block, ULONG dostype, struct MountData *md)
{
struct FileSysHeaderBlock *fshb = (struct FileSysHeaderBlock*)buf;
struct FileSysEntry *fse = NULL;
for (;;) {
if (block == 0xffffffff) {
break;
}
if (!readblock(buf, block, IDNAME_FILESYSHEADER, md)) {
break;
}
dbg("FSHD found, block %"PRIu32", dostype %08"PRIx32", looking for dostype %08"PRIx32"\n", block, fshb->fhb_DosType, dostype);
if (fshb->fhb_DosType == dostype) {
dbg("FSHD dostype match found\n");
fse = FSHDProcess(fshb, dostype, fshb->fhb_Version, TRUE, md);
if (fse) {
md->lsegblock = fshb->fhb_SegListBlocks;
md->lsegbuf = (struct LoadSegBlock*)(buf + md->blocksize);
md->lseglongs = 0;
APTR seg = fsrelocate(md);
fse->fse_SegList = MKBADDR(seg);
// Add to FileSystem.resource if succeeded, delete entry if failure.
FSHDAdd(fse, md);
}
break;
}
block = fshb->fhb_Next;
}
if (!fse) {
fse = FSHDProcess(NULL, dostype, 0, FALSE, md);
}
return fse;
}
#if NO_CONFIGDEV
// Create fake ConfigDev and DiagArea to support autoboot without requiring real autoconfig device.
static void CreateFakeConfigDev(struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
struct ExpansionBase *ExpansionBase = md->ExpansionBase;
struct ConfigDev *configDev;
configDev = AllocConfigDev();
if (configDev) {
configDev->cd_BoardAddr = (void*)&entrypoint;
configDev->cd_BoardSize = (UBYTE*)&entrypoint_end - (UBYTE*)&entrypoint;
configDev->cd_Rom.er_Type = ERTF_DIAGVALID;
ULONG bbSize = &bootblock_end - &bootblock;
ULONG daSize = sizeof(struct DiagArea) + bbSize;
struct DiagArea *diagArea = AllocMem(daSize, MEMF_CLEAR | MEMF_PUBLIC);
if (diagArea) {
diagArea->da_Config = DAC_CONFIGTIME;
diagArea->da_BootPoint = sizeof(struct DiagArea);
diagArea->da_Size = (UWORD)daSize;
copymem(diagArea + 1, &bootblock, bbSize);
*((ULONG*)&configDev->cd_Rom.er_Reserved0c) = (ULONG)diagArea;
cacheclear(md);
}
md->configDev = configDev;
}
}
#endif
struct ParameterPacket
{
const UBYTE *dosname;
const UBYTE *execname;
ULONG unitnum;
ULONG flags;
struct DosEnvec de;
};
static UBYTE ToUpper(UBYTE c)
{
if (c >= 'a' || c <= 'z') {
c |= 0x20;
}
return c;
}
// Case-insensitive BSTR string comparison
static BOOL CompareBSTRNoCase(const UBYTE *src1, const UBYTE *src2)
{
UBYTE len1 = *src1++;
UBYTE len2 = *src2++;
if (len1 != len2) {
return FALSE;
}
for (UWORD i = 0; i < len1; i++) {
UBYTE c1 = *src1++;
UBYTE c2 = *src2++;
c1 = ToUpper(c1);
c2 = ToUpper(c2);
if (c1 != c2) {
return FALSE;
}
}
return TRUE;
}
// Check for duplicate device names
static void CheckAndFixDevName(struct MountData *md, UBYTE *bname)
{
struct ExecBase *SysBase = md->SysBase;
Forbid();
struct BootNode *bn = (struct BootNode*)md->ExpansionBase->MountList.lh_Head;
while (bn->bn_Node.ln_Succ) {
struct DeviceNode *dn = bn->bn_DeviceNode;
const UBYTE *bname2 = BADDR(dn->dn_Name);
if (CompareBSTRNoCase(bname, bname2)) {
UBYTE len = bname[0];
UBYTE *name = bname + 1;
dbg("Duplicate device name '%s'\n", name);
if (len > 2 && name[len - 2] == '.' && name[len - 1] >= '0' && name[len - 1] < '9') {
// if already ends to .<digit>: increase digit by one
name[len - 1]++;
} else {
// else append .1
name[len++] = '.';
name[len++] = '1';
name[len] = 0;
bname[0] += 2;
}
dbg("-> new device name '%s'\n", name);
// retry
bn = (struct BootNode*)md->ExpansionBase->MountList.lh_Head;
continue;
}
bn = (struct BootNode*)bn->bn_Node.ln_Succ;
}
Permit();
}
// Add DeviceNode to Expansion MountList.
static void AddNode(struct PartitionBlock *part, struct ParameterPacket *pp, struct DeviceNode *dn, UBYTE *name, struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
struct ExpansionBase *ExpansionBase = md->ExpansionBase;
struct DosLibrary *DOSBase = md->DOSBase;
LONG bootPri = (part->pb_Flags & PBFF_BOOTABLE) ? pp->de.de_BootPri : -128;
if (ExpansionBase->LibNode.lib_Version >= 37) {
// KS 2.0+
if (!md->DOSBase && bootPri > -128) {
dbg("KS20+ Mounting as bootable: pri %08"PRIx32"\n", bootPri);
AddBootNode(bootPri, ADNF_STARTPROC, dn, md->configDev);
} else {
dbg("KS20+: Mounting as non-bootable\n");
AddDosNode(bootPri, ADNF_STARTPROC, dn);
}
} else {
// KS 1.3
if (!md->DOSBase && bootPri > -128) {
dbg("KS13 Mounting as bootable: pri %08"PRIx32"\n", bootPri);
// Create and insert bootnode manually.
struct BootNode *bn = AllocMem(sizeof(struct BootNode), MEMF_CLEAR | MEMF_PUBLIC);
if (bn) {
bn->bn_Node.ln_Type = NT_BOOTNODE;
bn->bn_Node.ln_Pri = (BYTE)bootPri;
bn->bn_Node.ln_Name = (UBYTE*)md->configDev;
bn->bn_DeviceNode = dn;
Forbid();
Enqueue(&md->ExpansionBase->MountList, &bn->bn_Node);
Permit();
}
} else {
dbg("KS13: Mounting as non-bootable\n");
AddDosNode(bootPri, 0, dn);
if (md->DOSBase) {
// KS 1.3 ADNF_STARTPROC is not supported
// need to use DeviceProc() to start the filesystem process.
UWORD len = strlen(name);
name[len++] = ':';
name[len] = 0;
void * __attribute__((unused)) mp = DeviceProc(name);
dbg("DeviceProc() returned %p\n", mp);
}
}
}
}
// Parse PART block, mount drive.
static ULONG ParsePART(UBYTE *buf, ULONG block, ULONG filesysblock, struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
struct ExpansionBase *ExpansionBase = md->ExpansionBase;
struct PartitionBlock *part = (struct PartitionBlock*)buf;
ULONG nextpartblock = 0xffffffff;
if (!readblock(buf, block, IDNAME_PARTITION, md)) {
return nextpartblock;
}
dbg("PART found, block %"PRIu32"\n", block);
nextpartblock = part->pb_Next;
if (!(part->pb_Flags & PBFF_NOMOUNT)) {
struct ParameterPacket *pp = AllocMem(sizeof(struct ParameterPacket), MEMF_PUBLIC | MEMF_CLEAR);
if (pp) {
copymem(&pp->de, &part->pb_Environment, 17 * sizeof(ULONG));
struct FileSysEntry *fse = ParseFSHD(buf + md->blocksize, filesysblock, pp->de.de_DosType, md);
pp->execname = md->devicename;
pp->unitnum = md->unitnum;
pp->dosname = part->pb_DriveName + 1;
part->pb_DriveName[(*part->pb_DriveName) + 1] = 0;
dbg("PART '%s'\n", pp->dosname);
CheckAndFixDevName(md, part->pb_DriveName);
struct DeviceNode *dn = MakeDosNode(pp);
if (dn) {
if (fse) {
// Process PatchFlags
ULONG *dstPatch = &dn->dn_Type;
ULONG *srcPatch = &fse->fse_Type;
ULONG patchFlags = fse->fse_PatchFlags;
while (patchFlags) {
if (patchFlags & 1) {
*dstPatch = *srcPatch;
}
patchFlags >>= 1;
srcPatch++;
dstPatch++;
}
}
dbg("Mounting partition\n");
#if NO_CONFIGDEV
if (!md->configDev && !md->DOSBase) {
CreateFakeConfigDev(md);
}
#endif
AddNode(part, pp, dn, part->pb_DriveName + 1, md);
md->ret++;
} else {
dbg("Device node creation failed\n");
}
FreeMem(pp, sizeof(struct ParameterPacket));
}
}
return nextpartblock;
}
// Scan PART blocks
static LONG ParseRDSK(UBYTE *buf, struct MountData *md)
{
struct RigidDiskBlock *rdb = (struct RigidDiskBlock*)buf;
ULONG partblock = rdb->rdb_PartitionList;
ULONG filesysblock = rdb->rdb_FileSysHeaderList;
ULONG flags = rdb->rdb_Flags;
for (;;) {
if (partblock == 0xffffffff) {
break;
}
partblock = ParsePART(buf, partblock, filesysblock, md);
}
md->wasLastDev = (flags & RDBFF_LAST) != 0;
return md->ret;
}
// Search for RDB
static LONG ScanRDSK(struct MountData *md)
{
struct ExecBase *SysBase = md->SysBase;
LONG ret = -1;
for (UWORD i = 0; i < RDB_LOCATION_LIMIT; i++) {
if (readblock(md->buf, i, 0xffffffff, md)) {
struct RigidDiskBlock *rdb = (struct RigidDiskBlock*)md->buf;
if (rdb->rdb_ID == IDNAME_RIGIDDISK) {
dbg("RDB found, block %"PRIu32"\n", i);
ret = ParseRDSK(md->buf, md);
break;
}
}
}
md->request->iotd_Req.io_Command = TD_MOTOR;
md->request->iotd_Req.io_Length = 0;
DoIO((struct IORequest*)md->request);
return ret;
}
struct MountStruct
{
// Device name. ("myhddriver.device")
// Offset 0.
const UBYTE *deviceName;
// Unit number pointer or single integer value.
// if >= 0x100 (256), pointer to array of ULONGs, first ULONG is number of unit numbers followed (for example { 2, 0, 1 }. 2 units, unit numbers 0 and 1).
// if < 0x100 (256): used as a single unit number value.
// Offset 4.
ULONG *unitNum;
// Name string used to set Creator field in FileSystem.resource (if KS 1.3) and in FileSystem.resource entries.
// If NULL: use device name.
// Offset 8.
const UBYTE *creatorName;
// ConfigDev: set if autoconfig board autoboot support is wanted.
// If NULL and bootable partition found: fake ConfigDev is automatically created.
// Offset 12.
struct ConfigDev *configDev;
// SysBase.
// Offset 16.
struct ExecBase *SysBase;
};
// Return values:
// If single unit number:
// -1 = No RDB found, device failed to open, disk error or RDB block checksum error.
// 0 = RDB found but no partitions found, disk error or mount failure.
// >0: Number of partitions mounted.
// If unit number array:
// Unit number is replaced with error code:
// Error codes are same as above except:
// -2 = Skipped, previous unit had RDBFF_LAST set.
LONG MountDrive(struct MountStruct *ms)
{
LONG ret = -1;
struct MsgPort *port = NULL;
struct IOExtTD *request = NULL;
struct ExpansionBase *ExpansionBase;
struct ExecBase *SysBase = ms->SysBase;
dbg("Starting..\n");
ExpansionBase = (struct ExpansionBase*)OpenLibrary("expansion.library", 34);
if (ExpansionBase) {
struct MountData *md = AllocMem(sizeof(struct MountData), MEMF_CLEAR | MEMF_PUBLIC);
if (md) {
md->DOSBase = (struct DosLibrary*)OpenLibrary("dos.library", 34);
md->SysBase = SysBase;
md->ExpansionBase = ExpansionBase;
dbg("SysBase=%p ExpansionBase=%p DosBase=%p\n", md->SysBase, md->ExpansionBase, md->DOSBase);
md->configDev = ms->configDev;
md->creator = ms->creatorName;
port = W_CreateMsgPort(SysBase);
if(port) {
request = (struct IOExtTD*)W_CreateIORequest(port, sizeof(struct IOExtTD), SysBase);
if(request) {
ULONG *unitNumP = ms->unitNum;
ULONG unitNumVal[2];
unitNumVal[0] = 1;
unitNumVal[1] = (ULONG)unitNumP;
if (unitNumVal[1] < 0x100) {
unitNumP = &unitNumVal[0];
}
ULONG unitNumCnt = *unitNumP++;
while (unitNumCnt-- > 0) {
ULONG unitNum = *unitNumP;
dbg("OpenDevice('%s', %"PRId32", %p, 0)\n", ms->deviceName, unitNum, request);
UBYTE err = OpenDevice(ms->deviceName, unitNum, (struct IORequest*)request, 0);
if (err == 0) {
md->request = request;
md->devicename = ms->deviceName;
md->unitnum = unitNum;
md->blocksize=512;
ret = ScanRDSK(md);
CloseDevice((struct IORequest*)request);
*unitNumP++ = ret;
if (md->wasLastDev) {
while (unitNumCnt-- > 0) {
*unitNumP++ = -2;
}
dbg("RDBFF_LAST exit\n");
break;
}
} else {
dbg("OpenDevice(%s,%"PRId32") failed: %"PRId32"\n", ms->deviceName, unitNum, (BYTE)err);
*unitNumP++ = -1;
}
}
W_DeleteIORequest(request, SysBase);
}
W_DeleteMsgPort(port, SysBase);
}
FreeMem(md, sizeof(struct MountData));
if (md->DOSBase) {
CloseLibrary(&md->DOSBase->dl_lib);
}
}
CloseLibrary(&ExpansionBase->LibNode);
}
dbg("Exit code %"PRId32"\n", ret);
return ret;
}
int mount_drives(struct ConfigDev *cd, struct Library *dev)
{
extern char real_device_name[];
static BOOL mounted = FALSE;
struct MountStruct ms;
int i, ret = 0;
ULONG unitNum[] = { 7, 0, 1, 2, 3, 4, 5, 6 };
printf("Mounter:\n");
if (!mounted) {
ms.deviceName = real_device_name;
ms.unitNum = unitNum;
ms.creatorName = NULL;
ms.configDev = cd;
ms.SysBase = *(struct ExecBase **)4UL;
mounted = TRUE;
ret = MountDrive(&ms);
}
printf("ret = %x\nunitNum = { ", ret);
for (i=0; i<8; i++)
printf("%x%s", unitNum[i], i<7?", ":" }\n");
return ret;
}