root/sys/arch/ews4800mips/stand/common/bootxx.c
/*      $NetBSD: bootxx.c,v 1.6 2020/06/07 03:00:53 tsutsui Exp $       */

/*-
 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 THE FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#ifdef _STANDALONE
#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>
#endif

#include <sys/types.h>
#include <sys/param.h>
#include "bootxx.h"

#include <sys/exec_ecoff.h>
#include <sys/exec_elf.h>

#include <machine/sector.h>
#include <machine/sbd.h>

#include "common.h"

#define IS_TEXT(p)      (p.p_flags & PF_X)
#define IS_DATA(p)      ((p.p_flags & PF_X) == 0)
#define IS_BSS(p)       (p.p_filesz < p.p_memsz)

#define FILHSZ  (sizeof(struct ecoff_filehdr))
#define SCNHSZ  (sizeof(struct ecoff_scnhdr))
#define N_TXTOFF(f, a)                                                  \
        ((a)->vstamp < 23 ?                                             \
        ((FILHSZ +(f)->f_opthdr +(f)->f_nscns * SCNHSZ + 7) & ~7) :     \
        ((FILHSZ +(f)->f_opthdr +(f)->f_nscns * SCNHSZ + 15) & ~15))

int main(void);
int loader(const char *, uint32_t *);
int load_elf(uint8_t *, uint32_t *);
int load_coff(uint8_t *, uint32_t *);
int dk_read(int, int, void *);

const char *errmsg[] = {
        [BERR_PDINFO]           = "No PDINFO",
        [BERR_RDVTOC]           = "VTOC read error",
        [BERR_VTOC]             = "VTOC bad magic",
        [BERR_NOBFS]            = "No BFS partition",
        [BERR_RDINODE]          = "I-node read error",
        [BERR_NOROOT]           = "No root",
        [BERR_RDDIRENT]         = "Dirent read error",
        [BERR_NOFILE]           = "No such a file",
        [BERR_RDFILE]           = "File read error",
        [BERR_FILEMAGIC]        = "Bad file magic",
        [BERR_AOUTHDR]          = "Not a OMAGIC",
        [BERR_ELFHDR]           = "Not a ELF",
        [BERR_TARHDR]           = "Read tar file",
        [BERR_TARMAGIC]         = "Bad tar magic",
};

const char *boottab[] = {
        "boot",         /* NetBSD (bfs,ustarfs) */
        "iopboot",      /* EWS-UX (bfs) */
        0               /* terminate */
};

int __dk_unit, __dk_type, __spb;
bool (*fd_position)(uint32_t, uint32_t *, int *);

int
main(void)
{
        char msg[] = "[+++] NetBSD/ews4800mips >> ";
        const char *p;
        const char **boot;
        uint32_t entry;
        int err;

#ifdef _STANDALONE
        int i, fd_format;

        boot_device(&__dk_type, &__dk_unit, &fd_format);
        msg[1] = __dk_type + '0';
        msg[2] = __dk_unit + '0';
        msg[3] = FS_SIGNATURE;
        for (i = 0; msg[i] != 0; i++)
                ROM_PUTC(32 + i * 12, 536, msg[i]);

        if (__dk_type == NVSRAM_BOOTDEV_FLOPPYDISK) {
                fd_position = blk_to_2d_position;
                if (fd_format == FD_FORMAT_2D) {
                        fd_position = blk_to_2d_position;
                        __spb = 2;              /* 256bytes/sector */
                } else {
                        fd_position = blk_to_2hd_position;
                        __dk_unit |= 0x1000000;         /* | 2HD flag */
                        __spb = 1;
                }
        }
#else
        printf("%s\n", msg);
        sector_init("/dev/rsd1p", DEV_BSIZE);
        sector_read(SDBOOT_PDINFOADDR, PDINFO_SECTOR);
#endif

        for (boot = boottab; *boot; boot++) {
                if ((err = loader(*boot, &entry)) == 0) {
#ifdef _STANDALONE
                        ROM_PUTC(0, 0, '\n');
                        ROM_PUTC(0, 0, '\r');
                        return entry;
#else
                        printf("entry=%p\n", (void *)entry);
                        sector_fini();
                        return 0;
#endif
                }
                p = errmsg[err];
#ifdef _STANDALONE
                for (i = 0; p[i] != 0; i++)
                        ROM_PUTC(600 + i * 12, 536, p[i]);
#else
                DPRINTF("***ERROR *** %s\n", p);
#endif
        }

#ifdef _STANDALONE
        while (/*CONSTCOND*/1)
                ;
        /* NOTREACHED */
#else
        sector_fini();
#endif
        return 0;
}

int
loader(const char *fname, uint32_t *entry)
{
        int err;
        uint16_t mag;
        size_t sz;
#ifdef _STANDALONE
        int i;

        for (i = 0; fname[i] != 0; i++)
                ROM_PUTC(380 + i * 12, 536, fname[i]);
#else
        printf("%s\n", fname);
#endif

        if ((err = fileread(fname, &sz)) != 0)
                return err;

        DPRINTF("%s found. %d bytes.\n", fname, sz);

        mag = *(uint16_t *)SDBOOT_SCRATCHADDR;
        if (mag == 0x7f45)
                return load_elf((uint8_t *)SDBOOT_SCRATCHADDR, entry);
        else if (mag == ECOFF_MAGIC_MIPSEB)
                return load_coff((uint8_t *)SDBOOT_SCRATCHADDR, entry);

        return BERR_FILEMAGIC;
}

int
load_elf(uint8_t *buf, uint32_t *entry)
{
        Elf32_Ehdr *e = (void *)buf;
        Elf32_Phdr *p;
        int i;

        if (e->e_ident[EI_MAG2] != 'L' || e->e_ident[EI_MAG3] != 'F' ||
            e->e_ident[EI_CLASS] != ELFCLASS32 ||
            e->e_ident[EI_DATA] != ELFDATA2MSB ||
            e->e_type != ET_EXEC ||
            e->e_machine != EM_MIPS)
                return BERR_ELFHDR;

        BASSERT(e->e_phentsize == sizeof(Elf32_Phdr));
        p = (void *)(buf + e->e_phoff);
#ifdef _STANDALONE
        for (i = 0; i < e->e_phnum; i++) {
                if (p[i].p_type != PT_LOAD ||
                    (p[i].p_flags & (PF_W|PF_R|PF_X)) == 0)
                        continue;
                if (IS_TEXT(p[i]) || IS_DATA(p[i])) {
                        memcpy((void *)p[i].p_vaddr,
                            buf + p[i].p_offset, p[i].p_filesz);
                }
                if (IS_BSS(p[i])) {
                        memset((void *)(p[i].p_vaddr + p[i].p_filesz), 0,
                            p[i].p_memsz - p[i].p_filesz);
                }
        }
#else
        DPRINTF("ELF entry point 0x%08x\n", e->e_entry);
        for (i = 0; i < e->e_phnum; i++) {
                if (p[i].p_type != PT_LOAD ||
                    (p[i].p_flags & (PF_W|PF_R|PF_X)) == 0)
                        continue;
                if (IS_TEXT(p[i]) || IS_DATA(p[i])) {
                        DPRINTF("[text/data] 0x%08x 0x%x %dbyte.\n",
                            p[i].p_vaddr, p[i].p_offset, p[i].p_filesz);
                }
                if (IS_BSS(p[i])) {
                        DPRINTF("[bss] 0x%08x %dbyte.\n",
                            p[i].p_vaddr + p[i].p_filesz,
                            p[i].p_memsz - p[i].p_filesz);
                }
        }
#endif
        *entry = e->e_entry;

        return 0;
}

int
load_coff(uint8_t *p, uint32_t *entry)
{
        struct ecoff_exechdr *eh = (void *)p;
        struct ecoff_aouthdr *a = &eh->a;
        struct ecoff_filehdr *f = &eh->f;

        if (a->magic != ECOFF_OMAGIC)
                return BERR_AOUTHDR;

        p += N_TXTOFF(f, a);
        DPRINTF("file offset = 0x%x\n", N_TXTOFF(f, a));
#ifdef _STANDALONE
        memcpy((void *)a->text_start, p, a->tsize);
        memcpy((void *)a->data_start, p + a->tsize, a->dsize);
#else
        DPRINTF("COFF entry point 0x%08lx\n", a->entry);
        DPRINTF("[text] 0x%08lx %ld byte.\n", a->text_start, a->tsize);
        DPRINTF("[data] 0x%08lx %ld byte.\n", a->data_start, a->dsize);
#endif

        *entry = a->entry;

        return 0;
}

int
dk_read(int sector, int count, void *buf)
{
#ifdef _STANDALONE
        int cnt;
        uint32_t pos;
        uint8_t *p = buf;

        if (__dk_type == NVSRAM_BOOTDEV_HARDDISK) {     /* DISK */
                if ((ROM_DK_READ(__dk_unit, sector, count, p) & 0x7f) != 0)
                        return 1;
        } else {        /* FD */
                while (count > 0) {
                        fd_position(sector, &pos, &cnt);
                        if (cnt > count)
                                cnt = count;
                        if ((ROM_FD_READ(__dk_unit, pos, cnt * __spb, p)
                            & 0x7f) != 0)
                                return 1;
                        count -= cnt;
                        sector += cnt;
                        p += 512 * cnt;
                }
        }
#else
        sector_read_n(buf, sector, count);
#endif
        return 0;
}