summaryrefslogtreecommitdiffstats
path: root/stand/powerpc/kboot/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'stand/powerpc/kboot/main.c')
-rw-r--r--stand/powerpc/kboot/main.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/stand/powerpc/kboot/main.c b/stand/powerpc/kboot/main.c
new file mode 100644
index 0000000..7a24c16
--- /dev/null
+++ b/stand/powerpc/kboot/main.c
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (C) 2010-2014 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <fdt_platform.h>
+
+#define _KERNEL
+#include <machine/cpufunc.h>
+#include "bootstrap.h"
+#include "host_syscall.h"
+
+struct arch_switch archsw;
+extern void *_end;
+
+extern char bootprog_info[];
+
+int kboot_getdev(void **vdev, const char *devspec, const char **path);
+ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len);
+ssize_t kboot_readin(const int fd, vm_offset_t dest, const size_t len);
+int kboot_autoload(void);
+uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr);
+int kboot_setcurrdev(struct env_var *ev, int flags, const void *value);
+
+extern int command_fdt_internal(int argc, char *argv[]);
+
+int
+kboot_getdev(void **vdev, const char *devspec, const char **path)
+{
+ int i;
+ const char *devpath, *filepath;
+ struct devsw *dv;
+ struct devdesc *desc;
+
+ if (strchr(devspec, ':') != NULL) {
+ devpath = devspec;
+ filepath = strchr(devspec, ':') + 1;
+ } else {
+ devpath = getenv("currdev");
+ filepath = devspec;
+ }
+
+ for (i = 0; (dv = devsw[i]) != NULL; i++) {
+ if (strncmp(dv->dv_name, devpath, strlen(dv->dv_name)) == 0)
+ goto found;
+ }
+ return (ENOENT);
+
+found:
+ if (path != NULL && filepath != NULL)
+ *path = filepath;
+ else if (path != NULL)
+ *path = strchr(devspec, ':') + 1;
+
+ if (vdev != NULL) {
+ desc = malloc(sizeof(*desc));
+ desc->d_dev = dv;
+ desc->d_unit = 0;
+ desc->d_opendata = strdup(devpath);
+ *vdev = desc;
+ }
+
+ return (0);
+}
+
+int
+main(int argc, const char **argv)
+{
+ void *heapbase;
+ const size_t heapsize = 15*1024*1024;
+ const char *bootdev = argv[1];
+
+ /*
+ * Set the heap to one page after the end of the loader.
+ */
+ heapbase = host_getmem(heapsize);
+ setheap(heapbase, heapbase + heapsize);
+
+ /*
+ * Set up console.
+ */
+ cons_probe();
+
+ printf("Boot device: %s\n", bootdev);
+
+ archsw.arch_getdev = kboot_getdev;
+ archsw.arch_copyin = kboot_copyin;
+ archsw.arch_copyout = kboot_copyout;
+ archsw.arch_readin = kboot_readin;
+ archsw.arch_autoload = kboot_autoload;
+ archsw.arch_loadaddr = kboot_loadaddr;
+
+ printf("\n%s", bootprog_info);
+
+ setenv("currdev", bootdev, 1);
+ setenv("loaddev", bootdev, 1);
+ setenv("LINES", "24", 1);
+
+ interact(NULL); /* doesn't return */
+
+ return (0);
+}
+
+void
+exit(int code)
+{
+ /* XXX: host_exit */
+}
+
+void
+delay(int usecs)
+{
+ struct host_timeval tvi, tv;
+ uint64_t ti, t;
+ host_gettimeofday(&tvi, NULL);
+ ti = tvi.tv_sec*1000000 + tvi.tv_usec;
+ do {
+ host_gettimeofday(&tv, NULL);
+ t = tv.tv_sec*1000000 + tv.tv_usec;
+ } while (t < ti + usecs);
+}
+
+time_t
+getsecs(void)
+{
+ struct host_timeval tv;
+ host_gettimeofday(&tv, NULL);
+ return (tv.tv_sec);
+}
+
+time_t
+time(time_t *tloc)
+{
+ time_t rv;
+
+ rv = getsecs();
+ if (tloc != NULL)
+ *tloc = rv;
+
+ return (rv);
+}
+
+struct kexec_segment {
+ void *buf;
+ int bufsz;
+ void *mem;
+ int memsz;
+};
+
+struct kexec_segment loaded_segments[128];
+int nkexec_segments = 0;
+
+static ssize_t
+get_phys_buffer(vm_offset_t dest, const size_t len, void **buf)
+{
+ int i = 0;
+ const size_t segsize = 2*1024*1024;
+
+ for (i = 0; i < nkexec_segments; i++) {
+ if (dest >= (vm_offset_t)loaded_segments[i].mem &&
+ dest < (vm_offset_t)loaded_segments[i].mem +
+ loaded_segments[i].memsz)
+ goto out;
+ }
+
+ loaded_segments[nkexec_segments].buf = host_getmem(segsize);
+ loaded_segments[nkexec_segments].bufsz = segsize;
+ loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize);
+ loaded_segments[nkexec_segments].memsz = segsize;
+ i = nkexec_segments;
+ nkexec_segments++;
+
+out:
+ *buf = loaded_segments[i].buf + (dest -
+ (vm_offset_t)loaded_segments[i].mem);
+ return (min(len,loaded_segments[i].bufsz - (dest -
+ (vm_offset_t)loaded_segments[i].mem)));
+}
+
+ssize_t
+kboot_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ ssize_t segsize, remainder;
+ void *destbuf;
+
+ remainder = len;
+ do {
+ segsize = get_phys_buffer(dest, remainder, &destbuf);
+ bcopy(src, destbuf, segsize);
+ remainder -= segsize;
+ src += segsize;
+ dest += segsize;
+ } while (remainder > 0);
+
+ return (len);
+}
+
+ssize_t
+kboot_copyout(vm_offset_t src, void *dest, const size_t len)
+{
+ ssize_t segsize, remainder;
+ void *srcbuf;
+
+ remainder = len;
+ do {
+ segsize = get_phys_buffer(src, remainder, &srcbuf);
+ bcopy(srcbuf, dest, segsize);
+ remainder -= segsize;
+ src += segsize;
+ dest += segsize;
+ } while (remainder > 0);
+
+ return (len);
+}
+
+ssize_t
+kboot_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ void *buf;
+ size_t resid, chunk, get;
+ ssize_t got;
+ vm_offset_t p;
+
+ p = dest;
+
+ chunk = min(PAGE_SIZE, len);
+ buf = malloc(chunk);
+ if (buf == NULL) {
+ printf("kboot_readin: buf malloc failed\n");
+ return (0);
+ }
+
+ for (resid = len; resid > 0; resid -= got, p += got) {
+ get = min(chunk, resid);
+ got = read(fd, buf, get);
+ if (got <= 0) {
+ if (got < 0)
+ printf("kboot_readin: read failed\n");
+ break;
+ }
+
+ kboot_copyin(buf, p, got);
+ }
+
+ free (buf);
+ return (len - resid);
+}
+
+int
+kboot_autoload(void)
+{
+
+ return (0);
+}
+
+uint64_t
+kboot_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ /*
+ * Need to stay out of the way of Linux. /chosen/linux,kernel-end does
+ * a better job here, but use a fixed offset for now.
+ */
+
+ if (type == LOAD_ELF)
+ addr = roundup(addr, PAGE_SIZE);
+ else
+ addr += 64*1024*1024; /* Stay out of the way of Linux */
+
+ return (addr);
+}
+
+void
+_start(int argc, const char **argv, char **env)
+{
+ register volatile void **sp asm("r1");
+ main((int)sp[0], (const char **)&sp[1]);
+}
+
+/*
+ * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
+ * and declaring it as extern is in contradiction with COMMAND_SET() macro
+ * (which uses static pointer), we're defining wrapper function, which
+ * calls the proper fdt handling routine.
+ */
+static int
+command_fdt(int argc, char *argv[])
+{
+
+ return (command_fdt_internal(argc, argv));
+}
+
+COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
+
OpenPOWER on IntegriCloud