blob: 1fb9527aefe8d8c03630d33d0ef27c63086e0651 [file] [log] [blame]
/* Miscelaneous functions
Copyright (C) 1996 David Miller
1996 Pete A. Zaitcev
1996,1997 Jakub Jelinek
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
#include <silo.h>
#include <asm/idprom.h>
#include <asm/machines.h>
#include <stringops.h>
void silo_fatal(const char *msg)
{
printf("\nFatal error: %s\n", msg);
}
char *silo_v0_device(char *imagename)
{
if (((imagename[0] == 's' && strchr ("dt", imagename[1])) ||
(imagename[0] == 'x' && strchr ("dy", imagename[1])) ||
(imagename[0] == 'f' && imagename[1] == 'd') ||
(imagename[0] == 'l' && imagename[1] == 'e') ||
(imagename[0] == 'i' && imagename[1] == 'e')) &&
imagename[2] == '(') {
return strchr (imagename, ')');
} else
return 0;
}
char *seed_part_into_device (char *device, int part)
{
static char buffer[256];
strcpy (buffer, device);
if (prom_vers != PROM_V0) {
char *p = strchr (buffer, ':');
if (!p) {
p = strchr (buffer, 0);
*p++ = ':';
} else
p++;
*p++ = 'a' + part - 1;
*p = 0;
} else {
int i = strlen (device);
char *p;
if (i >= 4 && buffer[2] == '(' && buffer[i - 1] == ')') {
if (i == 4) {
strcpy (buffer + 3, "0,0,");
buffer [7] = '0' + part - 1;
strcpy (buffer + 8, ")");
} else {
p = strchr (buffer + 3, ',');
if (!p) {
strcpy (buffer + i - 1, ",0,");
buffer [i + 2] = '0' + part - 1;
strcpy (buffer + i + 3, ")");
} else {
p = strchr (p + 1, ',');
if (!p) {
buffer [i - 1] = ',';
buffer [i] = '0' + part - 1;
strcpy (buffer + i + 1, ")");
} else {
*p = '0' - part - 1;
strcpy (p + 1, ")");
}
}
}
}
}
return buffer;
}
static char barg_buf[1024];
char barg_out[1024];
void silo_set_bootargs(char *params, char *device)
{
char *q, *r;
char **p;
int iter, i;
/*
* We expect a kernel to extract a command line
* from our dead body promptly, if the arguments are longer than 90 or so.
*/
if (params) {
q = params;
r = barg_out;
/* Remove unnecessary spaces */
do {
while (*q == ' ') q++;
if (!*q) break;
if (r != barg_out) *r++ = ' ';
while (*q && *q != ' ') *r++ = *q++;
} while (*q);
*r = 0;
} else
*barg_out = 0;
switch (prom_vers) {
case PROM_V0:
if (strlen (barg_out) > 100 - 12) {
p = (*(romvec->pv_v0bootargs))->argv;
p [0] = "silo()";
q = barg_out;
for (iter = 1; iter < 7; iter++) {
while (*q == ' ') q++;
if (!*q) {
p [iter] = 0;
continue;
}
r = q;
while (*r && *r != ' ') r++;
if (!p[iter] || strlen (p[iter]) != r - q || strncmp (q, p[iter], r - q)) {
if (*r) *r++ = 0;
p [iter] = q;
}
q = r;
}
while (*q == ' ') q++;
if (*q)
p [7] = q;
else
p [7] = 0;
return;
} else {
q = (*(romvec->pv_v0bootargs))->args;
p = (*(romvec->pv_v0bootargs))->argv;
p[0] = q;
if (!device) {
strcpy (q, "silo()");
q += 7;
strcpy (q, barg_out);
} else {
strcpy (q, device);
q = strchr (q, 0);
strcpy (q, barg_out);
r = strchr (q, ' ');
if (!r)
r = strchr (q, 0);
else {
*r = 0;
r++;
}
(*(romvec->pv_v0bootargs))->kernel_file_name = q;
(*(romvec->pv_v0bootargs))->boot_dev[0] = device[0];
(*(romvec->pv_v0bootargs))->boot_dev[1] = device[1];
q = device + 3;
i = 0;
while (*q != ',' && *q != ')') {
i = i * 10 + *q - '0';
q++;
}
if (*q == ',') q++;
(*(romvec->pv_v0bootargs))->boot_dev_ctrl = i;
i = 0;
while (*q != ',' && *q != ')') {
i = i * 10 + *q - '0';
q++;
}
if (*q == ',') q++;
(*(romvec->pv_v0bootargs))->boot_dev_unit = i;
i = 0;
while (*q != ',' && *q != ')') {
i = i * 10 + *q - '0';
q++;
}
(*(romvec->pv_v0bootargs))->dev_partition = i;
q = r;
}
for (i = 1; i < 7; i++) {
if (*q) {
r = strchr (q, ' ');
if (r) {
*r = 0;
r++;
} else
r = strchr (q, 0);
p[i] = q;
q = r;
} else
p[i] = 0;
}
if (*q)
p[7] = q;
else
p[7] = 0;
return;
}
case PROM_V2:
case PROM_V3:
if (strlen (barg_out) < 128)
strcpy (*romvec->pv_v2bootargs.bootargs, barg_out);
else
*romvec->pv_v2bootargs.bootargs = barg_out;
if (device)
strcpy (*romvec->pv_v2bootargs.bootpath, device);
break;
case PROM_P1275:
break;
}
}
char *silo_get_bootargs(int full)
{
int iter;
char *cp, *q;
char **p;
switch (prom_vers) {
case PROM_V0:
p = (*(romvec->pv_v0bootargs))->argv;
cp = barg_buf;
*cp = 0;
if (p [0]) {
for (iter = 0; iter < 8; iter++) {
q = p [iter];
if (q) {
if (!iter && !full) {
q = silo_v0_device(q);
if (q && !q[1])
continue;
else if (q)
q++;
else
q = p [iter];
}
strcpy (cp, q);
cp += strlen (cp);
*cp++ = ' ';
} else
break;
}
if (cp != barg_buf)
cp[-1] = 0;
}
break;
case PROM_V2:
case PROM_V3:
if (!full)
q = barg_buf;
else {
strcpy (barg_buf, *romvec->pv_v2bootargs.bootpath);
q = strchr (barg_buf, 0);
}
if (*romvec->pv_v2bootargs.bootargs) {
if (full)
*q++ = ' ';
strcpy (q, *romvec->pv_v2bootargs.bootargs);
} else if (!full)
*q = 0;
break;
case PROM_P1275:
if (!full)
q = barg_buf;
else {
iter = prom_getproperty (prom_chosen, "bootpath", barg_buf, 510);
if (iter != -1)
if (iter && !barg_buf [iter - 1])
q = barg_buf + iter - 1;
else
q = barg_buf + iter;
else
q = barg_buf;
}
iter = prom_getproperty (prom_chosen, "bootargs", full ? q + 1 : q, 512);
if (iter == -1 || !iter)
*q = 0;
else {
if (full)
*q = ' ';
if (q [iter + 1])
q [iter + 1] = 0;
}
break;
}
return barg_buf;
}
void silo_show_bootargs(void)
{
printf("Kernel args: %s\n", silo_get_bootargs(0));
}
unsigned char *silo_find_linux_HdrS(char *base, int len)
{
/* Ugly magic to find HdrS, we dig into first jmp gokernel */
char *p = base + ((*(unsigned short *) (base+2)) << 2) - 512;
char *q;
q = base+8;
if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S')
return q;
if (p >= base + len || p <= base)
return 0;
for (q = p + 512; p < q; p += 4) {
if (*p == 'H' && p[1] == 'd' && p[2] == 'r' && p[3] == 'S')
return p;
}
return 0;
}
static struct idp_struct idprm;
/* Here is the master table of Sun machines which use some implementation
* of the Sparc CPU and have a meaningful IDPROM machtype value that we
* know about. See asm/machines.h for empirical constants.
*/
struct SMM {
char *name;
char *package;
enum arch architecture;
unsigned char id_machtype;
} Machines[NUM_SUN_MACHINES] = {
/* First, Sun4's */
{ "Sun 4/100 Series", "sun4", sun4, (SM_SUN4 | SM_4_110) },
{ "Sun 4/200 Series", "sun4", sun4, (SM_SUN4 | SM_4_260) },
{ "Sun 4/300 Series", "sun4", sun4, (SM_SUN4 | SM_4_330) },
{ "Sun 4/400 Series", "sun4", sun4, (SM_SUN4 | SM_4_470) },
/* Now, Sun4c's */
{ "SparcStation 1", "SUNW,Sun_4_60", sun4c, (SM_SUN4C | SM_4C_SS1) },
{ "SparcStation IPC", "SUNW,Sun_4_40", sun4c, (SM_SUN4C | SM_4C_IPC) },
{ "SparcStation 1+", "SUNW,Sun_4_65", sun4c, (SM_SUN4C | SM_4C_SS1PLUS) },
{ "SparcStation SLC", "SUNW,Sun_4_20", sun4c, (SM_SUN4C | SM_4C_SLC) },
{ "SparcStation 2", "SUNW,Sun_4_75", sun4c, (SM_SUN4C | SM_4C_SS2) },
{ "SparcStation ELC", "SUNW,Sun_4_25", sun4c, (SM_SUN4C | SM_4C_ELC) },
{ "SparcStation IPX", "SUNW,Sun_4_50", sun4c, (SM_SUN4C | SM_4C_IPX) },
/* Finally, early Sun4m's */
{ "SparcSystem 600", "SUNW,Sun_4_600", sun4m, (SM_SUN4M | SM_4M_SS60) },
{ "SparcStation 10/20", "sun4m", sun4m, (SM_SUN4M | SM_4M_SS50) },
{ "SparcStation 4/5", "sun4m", sun4m, (SM_SUN4M | SM_4M_SS40) },
/* One entry for the OBP arch's which are sun4d, sun4e, sun4u and newer sun4m's */
{ "OBP based system", "sun4m", sun4m, (SM_SUN4M_OBP | 0x0) } };
char *get_systype(void)
{
static char system_name[128];
int i;
for(i = 0; i < NUM_SUN_MACHINES; i++)
if(Machines[i].id_machtype == idprm.id_machtype) {
if(idprm.id_machtype!=(SM_SUN4M_OBP | 0x0))
return Machines[i].name;
else {
prom_getproperty(prom_root_node, "banner-name",
system_name, sizeof(system_name));
return system_name;
}
}
return "Unknown Sparc";
}
char *get_syspackage(void)
{
static char system_name[128];
int i;
*system_name = 0;
prom_getproperty(prom_root_node, "name",
system_name, sizeof(system_name));
if (*system_name)
return system_name;
for(i = 0; i < NUM_SUN_MACHINES; i++)
if(Machines[i].id_machtype == idprm.id_machtype) {
return Machines[i].package;
}
return "sun4c";
}
void
get_idprom(void)
{
prom_getproperty (prom_root_node, "idprom", (char *)&idprm, sizeof (idprm));
}
void print_message (char *msg)
{
char *p = msg, *q = 0;
int curly;
int i;
while (*p) {
while (*p && *p != '$') p++;
if (!*p)
break;
*p = 0;
printf ("%s", msg);
msg = p;
*p++ = '$';
if ((curly = (*p == '{')))
p++;
if (!strncmp (p, "ARCH", 4)) {
switch (silo_get_architecture()) {
case sun4: q = "SUN4"; break;
case sun4c: q = "SUN4C"; break;
case sun4d: q = "SUN4D"; break;
case sun4m: q = "SUN4M"; break;
case sun4e: q = "SUN4E"; break;
case sun4p: q = "SUN4P"; break;
case sun4u: q = "SUN4U"; break;
default: break;
}
printf ("%s", q);
p += 4;
} else if (!strncmp (p, "PROM", 4)) {
switch (prom_vers) {
case PROM_V0: printf ("0"); break;
case PROM_V2: printf ("2"); break;
case PROM_V3: printf ("3"); break;
case PROM_P1275: printf ("IEEE"); break;
}
p += 4;
} else if (!strncmp (p, "ETHER", 5)) {
for (i = 0; i < 6; i++)
printf ("%x%x%s", ((unsigned char)idprm.id_eaddr[i]) >> 4, idprm.id_eaddr[i] & 0xf, i == 5 ? "" : ":");
p += 5;
} else if (!strncmp (p, "SYSTYPE", 7)) {
printf ("%s", get_systype ());
p += 7;
} else
continue;
if (curly && *p == '}')
p++;
msg = p;
}
if (*msg)
printf ("%s", msg);
}
void silo_set_prollargs(char *params, unsigned int kbase, int ksize)
{
struct silo_to_proll {
char magic[4]; /* SiPR */
int kern_base; /* Always 256K so far. But who knows... */
int kern_size;
int xxx;
char kern_args[256];
} *p;
int *v;
/*
* Old, bad way:
* p = ((struct silo_to_proll *) 0x40000) - sizeof (struct silo_to_proll);
*
* New way allows to warn if PROLL is not cooperative.
*/
v = (int *) 0x4000;
if (v[2] != 0x5377) {
printf ("PROLL does not accept arguments\n");
return;
}
/* PROLL's base address is floating often. Mask the linking address off. */
p = (struct silo_to_proll *)(v[3] & (0x40000-1));
if (p->magic[0] != 'L' || p->magic[1] != 'R') {
printf ("Bad magic in PROLL parameter");
return;
}
p->magic[0] = 'S';
p->magic[1] = 'i';
p->magic[2] = 'L';
p->magic[3] = 'o';
p->kern_base = kbase; /* Hopefuly >= 0x40000 */
p->kern_size = ksize;
p->xxx = 0;
strcpy(p->kern_args, params);
}
enum arch silo_get_architecture(void)
{
char *buffer = "sun4c ";
int i;
if (prom_vers == PROM_P1275) {
i = prom_getchild(prom_root_node);
if ((i = prom_searchsiblings(i, "MicroSPARC-IIep")) != 0) {
return sun4p;
}
return sun4u;
}
i = prom_getproperty (prom_root_node, "compatability", buffer, 8);
if (!i || i == -1)
i = prom_getproperty (prom_root_node, "compatible", buffer, 8);
switch (buffer[4]) {
case 'c':
return sun4c;
case 'm':
return sun4m;
case 'd':
return sun4d;
case 'e':
return sun4e;
case 'u':
return sun4u;
default:
for(i = 0; i < NUM_SUN_MACHINES; i++)
if(Machines[i].id_machtype == idprm.id_machtype)
return Machines[i].architecture;
return sununknown;
}
}