#include <sys/param.h>
#include <sys/disklabel.h>
#ifdef NETBOOT
#include <netinet/in.h>
#endif
#include <lib/libkern/funcs.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/ufs.h>
#include <lib/libsa/ufs2.h>
#include <lib/libsa/cd9660.h>
#ifdef NETBOOT
#include <lib/libsa/nfs.h>
#endif
#ifdef SOFTRAID
#include <sys/queue.h>
#include <dev/softraidvar.h>
#include "softraid_sparc64.h"
#include "disk.h"
#endif
#include <dev/sun/disklabel.h>
#include "openfirm.h"
#include "ofdev.h"
int ffs(int);
extern char bootdev[];
static char *
filename(char *str, char *ppart)
{
char *cp, *lp;
char savec;
int dhandle;
char devtype[16];
lp = str;
devtype[0] = 0;
*ppart = 0;
for (cp = str; *cp; lp = cp) {
while (*++cp && *cp != '/');
savec = *cp;
*cp = 0;
dhandle = OF_finddevice(str);
DNPRINTF(BOOT_D_OFDEV, "filename: OF_finddevice(%s) says %x\n",
str, dhandle);
*cp = savec;
if (dhandle == -1) {
if (!strcmp(devtype, "block")) {
DNPRINTF(BOOT_D_OFDEV, "filename: hunting for "
"arguments in %s\n", str);
for (cp = lp;
--cp >= str && *cp != '/' && *cp != '-';);
if (cp >= str && *cp == '-') {
*cp = 0;
for (cp = lp; *--cp && *cp != ',';)
;
if (DL_PARTNAME2NUM(*++cp) != -1)
*ppart = *cp;
}
}
DNPRINTF(BOOT_D_OFDEV, "filename: found %s\n", lp);
return lp;
} else if (OF_getprop(dhandle, "device_type", devtype, sizeof devtype) < 0)
devtype[0] = 0;
}
DNPRINTF(BOOT_D_OFDEV, "filename: not found\n", lp);
return 0;
}
int
strategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
size_t *rsize)
{
struct of_dev *dev = devdata;
u_quad_t pos;
int n;
#ifdef SOFTRAID
if (dev->type == OFDEV_SOFTRAID)
return sr_strategy(bootdev_dip->sr_vol, bootdev_dip->sr_handle,
rw, blk, size, buf, rsize);
#endif
if (dev->type != OFDEV_DISK)
panic("strategy");
DNPRINTF(BOOT_D_OFDEV, "strategy: block %lx, partition offset %lx, "
"blksz %lx\n", (long)blk, (long)dev->partoff, (long)dev->bsize);
DNPRINTF(BOOT_D_OFDEV, "strategy: seek position should be: %lx\n",
(long)((blk + dev->partoff) * dev->bsize));
pos = (u_quad_t)(blk + dev->partoff) * dev->bsize;
for (;;) {
DNPRINTF(BOOT_D_OFDEV, "strategy: seeking to %lx\n", (long)pos);
if (OF_seek(dev->handle, pos) < 0)
break;
DNPRINTF(BOOT_D_OFDEV, "strategy: reading %lx at %p\n",
(long)size, buf);
if (rw == F_READ)
n = OF_read(dev->handle, buf, size);
else
n = OF_write(dev->handle, buf, size);
if (n == -2)
continue;
if (n < 0)
break;
if (rsize)
*rsize = n;
return 0;
}
return EIO;
}
static int
devclose(struct open_file *of)
{
struct of_dev *op = of->f_devdata;
#ifdef NETBOOT
if (op->type == OFDEV_NET)
net_close(op);
#endif
#ifdef SOFTRAID
if (op->type == OFDEV_SOFTRAID) {
op->handle = -1;
return 0;
}
#endif
OF_close(op->handle);
op->handle = -1;
return 0;
}
struct devsw devsw[1] = {
{
"OpenFirmware",
strategy,
(int (*)(struct open_file *, ...))nodev,
devclose,
noioctl
}
};
int ndevs = sizeof devsw / sizeof devsw[0];
#ifdef SPARC_BOOT_UFS
static struct fs_ops file_system_ufs = {
ufs_open, ufs_close, ufs_read, ufs_write, ufs_seek,
ufs_stat, ufs_readdir, ufs_fchmod
};
static struct fs_ops file_system_ufs2 = {
ufs2_open, ufs2_close, ufs2_read, ufs2_write, ufs2_seek,
ufs2_stat, ufs2_readdir, ufs2_fchmod
};
#endif
#ifdef SPARC_BOOT_HSFS
static struct fs_ops file_system_cd9660 = {
cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
cd9660_stat, cd9660_readdir
};
#endif
#ifdef NETBOOT
static struct fs_ops file_system_nfs = {
nfs_open, nfs_close, nfs_read, nfs_write, nfs_seek,
nfs_stat, nfs_readdir
};
#endif
struct fs_ops file_system[4];
int nfsys;
static struct of_dev ofdev = {
-1,
};
char opened_name[256];
static u_char
sun_fstypes[8] = {
FS_BSDFFS,
FS_SWAP,
FS_OTHER,
FS_BSDFFS,
FS_BSDFFS,
FS_BSDFFS,
FS_BSDFFS,
FS_BSDFFS,
};
static __inline u_int
sun_extended_sum(struct sun_disklabel *sl, void *end)
{
u_int sum, *xp, *ep;
xp = (u_int *)&sl->sl_xpmag;
ep = (u_int *)end;
sum = 0;
for (; xp < ep; xp++)
sum += *xp;
return (sum);
}
static int
disklabel_sun_to_bsd(struct sun_disklabel *sl, struct disklabel *lp)
{
struct sun_preamble *preamble = (struct sun_preamble *)sl;
struct sun_partinfo *ppp;
struct sun_dkpart *spp;
struct partition *npp;
u_short cksum = 0, *sp1, *sp2;
int i, secpercyl;
sp1 = (u_short *)sl;
sp2 = (u_short *)(sl + 1);
while (sp1 < sp2)
cksum ^= *sp1++;
if (cksum != 0)
return (EINVAL);
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_flags = D_VENDOR;
memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
lp->d_secsize = DEV_BSIZE;
lp->d_nsectors = sl->sl_nsectors;
lp->d_ntracks = sl->sl_ntracks;
lp->d_ncylinders = sl->sl_ncylinders;
secpercyl = sl->sl_nsectors * sl->sl_ntracks;
lp->d_secpercyl = secpercyl;
if (DL_GETDSIZE(lp) == 0)
DL_SETDSIZE(lp, (u_int64_t)secpercyl * sl->sl_ncylinders);
lp->d_version = 1;
memcpy(&lp->d_uid, &sl->sl_uid, sizeof(lp->d_uid));
lp->d_acylinders = sl->sl_acylinders;
lp->d_npartitions = MAXPARTITIONS16;
for (i = 0; i < 8; i++) {
spp = &sl->sl_part[i];
npp = &lp->d_partitions[i];
DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
DL_SETPSIZE(npp, spp->sdkp_nsectors);
if (DL_GETPSIZE(npp) == 0) {
npp->p_fstype = FS_UNUSED;
} else {
npp->p_fstype = sun_fstypes[i];
if (npp->p_fstype == FS_BSDFFS) {
npp->p_fragblock =
DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
npp->p_cpg = 16;
}
}
}
for (i = 0; i < SUNXPART; i++) {
npp = &lp->d_partitions[i+8];
DL_SETPOFFSET(npp, 0);
DL_SETPSIZE(npp, 0);
npp->p_fstype = FS_UNUSED;
}
if ((sl->sl_xpmag == SL_XPMAG &&
sun_extended_sum(sl, &sl->sl_types) == sl->sl_xpsum) ||
(sl->sl_xpmag == SL_XPMAGTYP &&
sun_extended_sum(sl, &sl->sl_uid) == sl->sl_xpsum) ||
(sl->sl_xpmag == SL_XPMAGTYP &&
sun_extended_sum(sl, &sl->sl_xxx1) == sl->sl_xpsum)) {
for (i = 0; i < SUNXPART; i++) {
spp = &sl->sl_xpart[i];
npp = &lp->d_partitions[i+8];
DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
DL_SETPSIZE(npp, spp->sdkp_nsectors);
if (DL_GETPSIZE(npp) == 0) {
npp->p_fstype = FS_UNUSED;
continue;
}
npp->p_fstype = FS_BSDFFS;
npp->p_fragblock =
DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
npp->p_cpg = 16;
}
if (sl->sl_xpmag == SL_XPMAGTYP) {
for (i = 0; i < MAXPARTITIONS16; i++) {
npp = &lp->d_partitions[i];
npp->p_fstype = sl->sl_types[i];
npp->p_fragblock = sl->sl_fragblock[i];
npp->p_cpg = sl->sl_cpg[i];
}
}
} else if (preamble->sl_nparts <= 8) {
i = preamble->sl_nparts;
if (i == 0)
i = 8;
npp = &lp->d_partitions[i-1];
ppp = &preamble->sl_part[i-1];
for (; i > 0; i--, npp--, ppp--) {
if (npp->p_size == 0)
continue;
if ((ppp->spi_tag == 0) && (ppp->spi_flag == 0))
continue;
switch (ppp->spi_tag) {
case SPTAG_SUNOS_ROOT:
case SPTAG_SUNOS_USR:
case SPTAG_SUNOS_VAR:
case SPTAG_SUNOS_HOME:
npp->p_fstype = FS_BSDFFS;
npp->p_fragblock =
DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
npp->p_cpg = 16;
break;
case SPTAG_LINUX_EXT2:
npp->p_fstype = FS_EXT2FS;
break;
default:
npp->p_fstype = FS_UNUSED;
break;
}
}
}
lp->d_checksum = 0;
lp->d_checksum = dkcksum(lp);
DNPRINTF(BOOT_D_OFDEV, "disklabel_sun_to_bsd: success!\n");
return (0);
}
static char *
search_label(struct of_dev *devp, u_long off, char *buf, struct disklabel *lp,
u_long off0)
{
struct disklabel *dlp;
struct sun_disklabel *slp;
size_t read;
if (DL_GETDSIZE(lp) == 0)
DL_SETDSIZE(lp, 0x1fffffff);
lp->d_npartitions = MAXPARTITIONS16;
if (DL_GETPSIZE(&lp->d_partitions[0]) == 0)
DL_SETPSIZE(&lp->d_partitions[0], 0x1fffffff);
DL_SETPOFFSET(&lp->d_partitions[0], 0);
if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read)
|| read != DEV_BSIZE)
return ("Cannot read label");
dlp = (struct disklabel *) (buf + LABELOFFSET);
if (dlp->d_magic == DISKMAGIC) {
if (dkcksum(dlp))
return ("corrupt disk label");
*lp = *dlp;
DNPRINTF(BOOT_D_OFDEV, "search_label: found disk label\n");
return (NULL);
}
slp = (struct sun_disklabel *)buf;
if (slp->sl_magic == SUN_DKMAGIC) {
if (disklabel_sun_to_bsd(slp, lp) != 0)
return ("corrupt disk label");
DNPRINTF(BOOT_D_OFDEV, "search_label: found disk label\n");
return (NULL);
}
return ("no disk label");
}
int
load_disklabel(struct of_dev *ofdev, struct disklabel *label)
{
char buf[DEV_BSIZE];
size_t read;
int error = 0;
char *errmsg = NULL;
DNPRINTF(BOOT_D_OFDEV, "load_disklabel: trying to read disklabel\n");
if (strategy(ofdev, F_READ,
LABELSECTOR, DEV_BSIZE, buf, &read) != 0
|| read != DEV_BSIZE
|| (errmsg = getdisklabel(buf, label))) {
#ifdef BOOT_DEBUG
if (errmsg)
DNPRINTF(BOOT_D_OFDEV,
"load_disklabel: getdisklabel says %s\n", errmsg);
#endif
errmsg = search_label(ofdev, LABELSECTOR, buf, label, 0);
if (errmsg) {
printf("load_disklabel: search_label says %s\n",
errmsg);
error = ERDLAB;
}
}
return (error);
}
int
devopen(struct open_file *of, const char *name, char **file)
{
char *cp;
char partition;
char fname[256];
char buf[DEV_BSIZE];
struct disklabel label;
int dhandle, ihandle, part, parent;
int error = 0;
#ifdef SOFTRAID
char volno;
#endif
nfsys = 0;
if (ofdev.handle != -1)
panic("devopen");
DNPRINTF(BOOT_D_OFDEV, "devopen: you want %s\n", name);
if (strlcpy(fname, name, sizeof fname) >= sizeof fname)
return ENAMETOOLONG;
#ifdef SOFTRAID
if (bootdev_dip) {
if (fname[0] == 's' && fname[1] == 'r' &&
'0' <= fname[2] && fname[2] <= '9') {
of->f_flags |= F_NOWRITE;
volno = fname[2];
if (DL_PARTNUM2NAME(fname[3]) != -1) {
partition = fname[3];
if (fname[4] == ':')
cp = &fname[5];
else
cp = &fname[4];
} else {
partition = 'a';
cp = &fname[3];
}
} else {
volno = '0';
partition = 'a';
cp = &fname[0];
}
snprintf(buf, sizeof buf, "sr%c%c:", volno, partition);
if (strlcpy(opened_name, buf, sizeof opened_name)
>= sizeof opened_name)
return ENAMETOOLONG;
*file = opened_name + strlen(opened_name);
if (!*cp) {
if (strlcpy(buf, DEFAULT_KERNEL, sizeof buf)
>= sizeof buf)
return ENAMETOOLONG;
} else {
if (snprintf(buf, sizeof buf, "%s%s",
*cp == '/' ? "" : "/", cp) >= sizeof buf)
return ENAMETOOLONG;
}
if (strlcat(opened_name, buf, sizeof opened_name) >=
sizeof opened_name)
return ENAMETOOLONG;
} else {
#endif
cp = filename(fname, &partition);
if (cp) {
if (strlcpy(buf, cp, sizeof buf) >= sizeof buf)
return ENAMETOOLONG;
*cp = 0;
}
if (!cp || !*buf) {
if (strlcpy(buf, DEFAULT_KERNEL, sizeof buf)
>= sizeof buf)
return ENAMETOOLONG;
}
if (!*fname) {
if (strlcpy(fname, bootdev, sizeof fname)
>= sizeof fname)
return ENAMETOOLONG;
}
if (strlcpy(opened_name, fname,
partition ? (sizeof opened_name) - 2 : sizeof opened_name)
>= sizeof opened_name)
return ENAMETOOLONG;
if (partition) {
cp = opened_name + strlen(opened_name);
*cp++ = ':';
*cp++ = partition;
*cp = 0;
}
if (*buf != '/') {
if (strlcat(opened_name, "/", sizeof opened_name) >=
sizeof opened_name)
return ENAMETOOLONG;
}
if (strlcat(opened_name, buf, sizeof opened_name) >=
sizeof opened_name)
return ENAMETOOLONG;
*file = opened_name + strlen(fname) + 1;
#ifdef SOFTRAID
}
#endif
DNPRINTF(BOOT_D_OFDEV, "devopen: trying %s\n", fname);
#ifdef SOFTRAID
if (bootdev_dip) {
struct partition *pp;
bzero(&ofdev, sizeof ofdev);
ofdev.type = OFDEV_SOFTRAID;
if (partition) {
part = DL_PARTNAME2NUM(partition);
if (part == -1) {
printf("invalid partition '%c'\n", partition);
return EINVAL;
}
pp = &bootdev_dip->disklabel.d_partitions[part];
if (pp->p_fstype == FS_UNUSED || pp->p_size == 0) {
printf("invalid partition '%c'\n", partition);
return EINVAL;
}
bootdev_dip->sr_vol->sbv_part = partition;
} else
bootdev_dip->sr_vol->sbv_part = 'a';
of->f_dev = devsw;
of->f_devdata = &ofdev;
#ifdef SPARC_BOOT_UFS
bcopy(&file_system_ufs, &file_system[nfsys++], sizeof file_system[0]);
bcopy(&file_system_ufs2, &file_system[nfsys++], sizeof file_system[0]);
#else
#error "-DSOFTRAID requires -DSPARC_BOOT_UFS"
#endif
return 0;
}
#endif
if ((dhandle = OF_finddevice(fname)) == -1)
return ENOENT;
DNPRINTF(BOOT_D_OFDEV, "devopen: found %s\n", fname);
if (OF_getprop(dhandle, "name", buf, sizeof buf) < 0)
return ENXIO;
DNPRINTF(BOOT_D_OFDEV, "devopen: %s is called %s\n", fname, buf);
if (OF_getprop(dhandle, "device_type", buf, sizeof buf) < 0)
return ENXIO;
DNPRINTF(BOOT_D_OFDEV, "devopen: %s is a %s device\n", fname, buf);
DNPRINTF(BOOT_D_OFDEV, "devopen: opening %s\n", fname);
if ((ihandle = OF_open(fname)) == -1) {
DNPRINTF(BOOT_D_OFDEV, "devopen: open of %s failed\n", fname);
return ENXIO;
}
DNPRINTF(BOOT_D_OFDEV, "devopen: %s is now open\n", fname);
bzero(&ofdev, sizeof ofdev);
ofdev.handle = ihandle;
ofdev.type = OFDEV_DISK;
ofdev.bsize = DEV_BSIZE;
if (!strcmp(buf, "block")) {
error = load_disklabel(&ofdev, &label);
if (error && error != ERDLAB)
goto bad;
else if (error == ERDLAB) {
if (partition)
goto bad;
ofdev.partoff = 0;
} else {
part = partition ? DL_PARTNAME2NUM(partition) : 0;
ofdev.partoff = label.d_partitions[part].p_offset;
DNPRINTF(BOOT_D_OFDEV, "devopen: setting partition %d "
"offset %x\n", part, ofdev.partoff);
}
of->f_dev = devsw;
of->f_devdata = &ofdev;
parent = OF_parent(dhandle);
if (parent && OF_getprop(parent, "device_type", buf,
sizeof(buf)) > 0 && strcmp(buf, "ide") == 0) {
DNPRINTF(BOOT_D_OFDEV,
"devopen: Disable writing for IDE block device\n");
of->f_flags |= F_NOWRITE;
}
#ifdef SPARC_BOOT_UFS
bcopy(&file_system_ufs, &file_system[nfsys++], sizeof file_system[0]);
bcopy(&file_system_ufs2, &file_system[nfsys++], sizeof file_system[0]);
#endif
#ifdef SPARC_BOOT_HSFS
bcopy(&file_system_cd9660, &file_system[nfsys++],
sizeof file_system[0]);
#endif
DNPRINTF(BOOT_D_OFDEV, "devopen: return 0\n");
return 0;
}
#ifdef NETBOOT
if (!strcmp(buf, "network")) {
ofdev.type = OFDEV_NET;
of->f_dev = devsw;
of->f_devdata = &ofdev;
bcopy(&file_system_nfs, file_system, sizeof file_system[0]);
nfsys = 1;
if ((error = net_open(&ofdev)))
goto bad;
return 0;
}
#endif
error = EFTYPE;
bad:
DNPRINTF(BOOT_D_OFDEV, "devopen: error %d, cannot open device\n",
error);
OF_close(ihandle);
ofdev.handle = -1;
return error;
}