#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/obpdefs.h>
#include <sys/reboot.h>
#include <sys/promif.h>
#include <sys/stat.h>
#include <sys/bootvfs.h>
#include <sys/platnames.h>
#include <sys/salib.h>
#include <sys/elf.h>
#include <sys/link.h>
#include <sys/auxv.h>
#include <sys/boot_policy.h>
#include <sys/boot_redirect.h>
#include <sys/bootconf.h>
#include <sys/boot.h>
#include "boot_plat.h"
#define SUCCESS 0
#define FAILURE -1
#define ISSPACE(c) (c == ' ' || c == '\t')
#define SKIP_WHITESPC(cp) while (*cp && ISSPACE(*cp)) cp++;
#ifdef DEBUG
int debug = 0;
#else
static const int debug = 0;
#endif
#define dprintf if (debug) printf
#ifdef DEBUG_LISTS
void print_memlist(struct memlist *av);
#endif
extern int (*readfile(int fd, int print))();
extern void kmem_init(void);
extern void *kmem_alloc(size_t, int);
extern void kmem_free(void *, size_t);
extern void get_boot_args(char *buf);
extern void setup_bootops(void);
extern struct bootops bootops;
extern void exitto(int (*entrypoint)());
extern void exitto64(int (*entrypoint)(), void *bootvec);
int openfile(char *filename);
char *default_name;
char *default_path;
int vac;
int is_sun4v;
int client_isLP64 = 1;
extern bootplat_defaults_t sun4u_plat_defaults;
extern bootplat_defaults_t sun4v_plat_defaults;
char filename[MAXPATHLEN];
char * const defname = "kernel/sparcv9/unix";
char *mfg_name;
int cache_state = 1;
char filename2[MAXPATHLEN];
int boothowto = 0;
int verbosemode = 0;
void
set_client_bootargs(const char *filename, const char *bargs)
{
int i = 0;
const char *s;
s = filename;
while (*s != '\0' && i < V2ARGS_BUF_SZ - 1)
v2args_buf[i++] = *s++;
if (i >= V2ARGS_BUF_SZ - 2) {
v2args_buf[i] = '\0';
return;
}
v2args_buf[i++] = ' ';
s = bargs;
while (*s != '\0' && i < V2ARGS_BUF_SZ - 1)
v2args_buf[i++] = *s++;
v2args_buf[i] = '\0';
}
static int
read_redirect(char *redirect)
{
int fd;
char slicec;
size_t nread = 0;
if ((fd = open(BOOT_REDIRECT, O_RDONLY)) != -1) {
nread = read(fd, &slicec, 1);
(void) close(fd);
if (nread == 1)
*redirect++ = slicec;
}
*redirect = '\0';
return (nread == 1);
}
void
post_mountroot(char *bootfile, char *redirect)
{
int (*go2)();
int fd;
(void) strcpy(filename2, bootfile);
for (;;) {
if (boothowto & RB_ASKNAME) {
char tmpname[MAXPATHLEN];
printf("Enter filename [%s]: ", bootfile);
(void) cons_gets(tmpname, sizeof (tmpname));
if (tmpname[0] != '\0')
(void) strcpy(bootfile, tmpname);
}
if (boothowto & RB_HALT) {
printf("Boot halted.\n");
prom_enter_mon();
}
if ((fd = openfile(bootfile)) == FAILURE) {
if (redirect != NULL &&
read_redirect(redirect)) {
(void) strcpy(bootfile, filename2);
return;
}
printf("%s: cannot open %s\n", my_own_name, bootfile);
boothowto |= RB_ASKNAME;
(void) strcpy(bootfile, filename2);
continue;
}
if ((go2 = readfile(fd, boothowto & RB_VERBOSE)) !=
(int(*)()) -1) {
(void) close(fd);
} else {
printf("boot failed\n");
boothowto |= RB_ASKNAME;
continue;
}
if (boothowto & RB_HALT) {
printf("Boot halted before exit to 0x%p.\n",
(void *)go2);
prom_enter_mon();
}
my_own_name = bootfile;
dprintf("Calling exitto64(%p, %p)\n", (void *)go2,
(void *)elfbootvecELF64);
exitto64(go2, (void *)elfbootvecELF64);
}
}
static int
boot_open(char *pathname, void *arg)
{
dprintf("trying '%s'\n", pathname);
return (open(pathname, O_RDONLY));
}
int
openfile(char *filename)
{
static char *fullpath;
static int once;
int fd;
if (once == 0) {
++once;
if (mfg_name == NULL)
mfg_name = get_mfg_name();
fullpath = (char *)kmem_alloc(MAXPATHLEN, 0);
}
if (*filename == '/') {
(void) strcpy(fullpath, filename);
fd = boot_open(fullpath, NULL);
return (fd);
}
fd = open_platform_file(filename, boot_open, NULL, fullpath);
if (fd == -1)
return (-1);
(void) strcpy(filename, fullpath);
return (fd);
}
static void
init_bootargs(char *fname_buf, int fname_buf_sz, char *bargs_buf,
int bargs_buf_sz)
{
const char *tp = prom_bootargs();
if (!tp || *tp == '\0') {
*bargs_buf = '\0';
return;
}
SKIP_WHITESPC(tp);
if (*tp && *tp != '-') {
int i;
for (i = 0; i < fname_buf_sz && *tp && !ISSPACE(*tp); ++i)
*fname_buf++ = *tp++;
if (i >= fname_buf_sz) {
printf("boot: boot filename too long!\n");
printf("boot halted.\n");
prom_enter_mon();
} else {
*fname_buf = '\0';
}
SKIP_WHITESPC(tp);
}
while (bargs_buf_sz > 1 && *tp) {
*bargs_buf++ = *tp++;
--bargs_buf_sz;
}
*bargs_buf = '\0';
if (bargs_buf_sz == 1) {
printf("boot: boot arguments too long!\n");
printf("boot halted.\n");
prom_enter_mon();
}
}
boolean_t
is_netdev(char *devpath)
{
pnode_t node = prom_finddevice(devpath);
char *options;
if ((node == OBP_NONODE) || (node == OBP_BADNODE))
return (B_FALSE);
if (prom_devicetype(node, "network") != 0)
return (B_TRUE);
if (prom_devicetype(node, "ib") != 0) {
options = prom_path_options(devpath);
if (options != NULL) {
#define SEARCHSTRING ",protocol=ip"
#define SEARCHSTRLEN strlen(SEARCHSTRING)
if (strstr(options, ",protocol=ip,") != NULL)
return (B_TRUE);
while ((options = strstr(options, SEARCHSTRING)) !=
NULL) {
char nextc;
nextc = options[SEARCHSTRLEN];
if ((nextc == ',') || (nextc == 0))
return (B_TRUE);
options += SEARCHSTRLEN;
}
}
}
return (B_FALSE);
}
void
mangle_os_bootpath(char *bpath)
{
pnode_t node;
char *stripped_pathname;
node = prom_finddevice(bpath);
if (prom_devicetype(node, "network") == 0)
return;
stripped_pathname = kmem_alloc(OBP_MAXPATHLEN, 0);
prom_strip_options(bpath, stripped_pathname);
v2path = stripped_pathname;
}
void
redirect_boot_path(char *bpath, char *redirect)
{
char slicec = *redirect;
char *p = bpath + strlen(bpath);
if (slicec < '0' || slicec > '7') {
printf("boot: bad redirection slice '%c'\n", slicec);
return;
}
while (--p >= bpath && *p != '@' && *p != '/')
if (*p == ':')
break;
if (*p++ == ':') {
*p++ = 'a' + slicec - '0';
*p = '\0';
v2path = bpath;
return;
}
prom_panic("redirect_boot_path: mangled boot path!");
}
void
system_check(void)
{
pnode_t n;
char arch[128];
size_t len;
bootplat_defaults_t *plat_defaults;
is_sun4v = 0;
n = prom_rootnode();
len = prom_getproplen(n, "device_type");
if (len > 0 && len < sizeof (arch)) {
(void) prom_getprop(n, "device_type", arch);
arch[len] = '\0';
dprintf("device_type=%s\n", arch);
if (strcmp(arch, "sun4v") == 0) {
is_sun4v = 1;
}
} else {
dprintf("device_type: no such property, len=%d\n", (int)len);
}
plat_defaults = (is_sun4v) ?
&sun4v_plat_defaults : &sun4u_plat_defaults;
default_name = plat_defaults->plat_defaults_name;
default_path = plat_defaults->plat_defaults_path;
vac = plat_defaults->plat_defaults_vac;
dprintf("default_name: %s\n", default_name);
dprintf("default_path: %s\n", default_path);
dprintf("vac: %d\n", vac);
}
int
main(void *cookie, char **argv, int argc)
{
static char bpath[OBP_MAXPATHLEN], bargs[OBP_MAXPATHLEN];
boolean_t user_specified_filename;
prom_init("boot", cookie);
fiximp();
system_check();
dprintf("\nboot: V%d /boot interface.\n", BO_VERSION);
#ifdef HALTBOOT
prom_enter_mon();
#endif
init_memlists();
#ifdef DEBUG_LISTS
dprintf("Physmem avail:\n");
if (debug) print_memlist(pfreelistp);
dprintf("Virtmem avail:\n");
if (debug) print_memlist(vfreelistp);
dprintf("Phys installed:\n");
if (debug) print_memlist(pinstalledp);
prom_enter_mon();
#endif
set_default_filename(defname);
filename[0] = '\0';
init_bootargs(filename, sizeof (filename), bargs, sizeof (bargs));
if (strcmp(filename, "kmdb") == 0 || strcmp(filename, "kadb") == 0) {
boothowto |= RB_KMDB;
*filename = '\0';
}
bootflags(bargs, sizeof (bargs));
user_specified_filename = (filename[0] != '\0');
(void) strncpy(bpath, prom_bootpath(), sizeof (bpath) - 1);
bpath[sizeof (bpath) - 1] = '\0';
dprintf("arch: %s\n", is_sun4v ? "sun4v" : "sun4u");
dprintf("bootpath: 0x%p %s\n", (void *)bpath, bpath);
dprintf("bootargs: 0x%p %s\n", (void *)bargs, bargs);
dprintf("filename: 0x%p %s\n", (void *)filename, filename);
dprintf("kernname: 0x%p %s\n", (void *)kernname, kernname);
v2path = bpath;
setup_bootops();
mangle_os_bootpath(bpath);
if (!is_sun4v) {
retain_nvram_page();
}
if (bootprog(bpath, bargs, user_specified_filename) == 0) {
post_mountroot(filename, NULL);
}
return (0);
}