/* $NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $ */
/*
* Copyright (c) 2003 Naoto Shimazaki.
* 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 NAOTO SHIMAZAKI 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 NAOTO 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.
*/
/*
* Boot loader for L-Card+
*
* ROM Map
* -------
* ROM1
* BFFF FFFF ------------------------------
*
* reserved
*
* BF80 0000 ------------------------------
*
* ROM0
* BFFF FFFF ------------------------------
*
* user storage (max 2Mbytes)
*
* BFE0 0000 ------------------------------
*
* reserved
*
* BFD4 0000 ------------------------------
*
* boot params
*
* BFD2 0000 ------------------------------
*
* second boot loader (mirror image)
* or Linux Kernel
*
* BFD0 0000 ------------------------------
*
* first boot loader (L-Card+ original loader)
*
* reset vector
* BFC0 0000 ------------------------------
*
* gziped kernel image (max 4Mbytes)
*
* BF80 0000 ------------------------------
*
*
*
* RAM Map
* -------
*
* 80FF FFFF ------------------------------
* ROM ICE work
* 80FF FE00 ------------------------------
* ROM ICE stack
* 80FF FDA8 ------------------------------
*
*
*
* kernel
* 8004 0000 ------------------------------
* kernel stack (growing to lower)
*
*
* boot loader heap (growing to upper)
* boot loader text & data (at exec time)
* 8000 1000 ------------------------------
* vector table
* 8000 0000 ------------------------------
*
* virtual memory space
*
* 0000 0000 ------------------------------
*
*
*
* ROMCS0 <-> ROMCS3 mapping
*
* ROMCS0 ROMCS3
* BE7F FFFF <-> BFFF FFFF
* BE40 0000 <-> BFC0 0000 reset vector
* BE00 0000 <-> BF80 0000
*
*
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $");
#include <lib/libsa/stand.h>
#include <lib/libsa/loadfile.h>
#include <lib/libkern/libkern.h>
#include <hpcmips/vr/vripreg.h>
#include <hpcmips/vr/cmureg.h>
#include <hpcmips/vr/vr4181giureg.h>
#include "extern.h"
#include "i28f128reg.h"
/* XXX */
#define ISABRGCTL 0x00
#define ISABRGSTS 0x02
#define XISACTL 0x04
#define BOOTTIMEOUT 9 /* must less than 10 */
#define LINEBUFLEN 80
extern const char bootprog_rev[];
extern const char bootprog_name[];
static void command_help(char *opt);
static void command_dump(char *opt);
static void command_boot(char *opt);
static void command_load(char *opt);
static void command_fill(char *opt);
static void command_write(char *opt);
static void command_option(char *subcmd);
static void opt_subcmd_print(char *opt);
static void opt_subcmd_read(char *opt);
static void opt_subcmd_write(char *opt);
static void opt_subcmd_path(char *opt);
static void opt_subcmd_bootp(char *opt);
static void opt_subcmd_ip(char *opt);
struct boot_option bootopts;
static struct bootmenu_command commands[] = {
{ "?", command_help },
{ "h", command_help },
{ "d", command_dump },
{ "b", command_boot },
{ "l", command_load },
{ "f", command_fill },
{ "w", command_write },
{ "o", command_option },
{ NULL, NULL },
};
static struct bootmenu_command opt_subcommands[] = {
{ "p", opt_subcmd_print },
{ "r", opt_subcmd_read },
{ "w", opt_subcmd_write },
{ "path", opt_subcmd_path },
{ "bootp", opt_subcmd_bootp },
{ "ip", opt_subcmd_ip },
{ NULL, NULL },
};
static void
print_banner(void)
{
printf("\n");
printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
#if 0
printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
#endif
}
static void
init_devices(void)
{
/* Init RTC */
REGWRITE_2(VRETIMEH, 0, 0);
REGWRITE_2(VRETIMEM, 0, 0);
REGWRITE_2(VRETIMEL, 0, 0);
/*
* CLKSPEEDREG 0x6012
* DIV DIV2 mode
* CLKSP 18 (0x12)
* PClock (CPU clock) 65.536MHz
* PClock = (18.432MHz / CLKSP) x 64
* = (18.432MHz / 18) x 64
* = 65.536MHz
* TClock (peripheral clock) 32.768MHz
* TClock = PClock / DIV
* = 65.536MHz / 2
* = 32.768MHz
*/
/*
* setup ISA BUS clock freqency
*
* set PCLK (internal peripheral clock) to 32.768MHz (TClock / 1)
* set External ISA bus clock to 10.922MHz (TClock / 3)
*/
REGWRITE_2(VR4181_ISABRG_ADDR, ISABRGCTL, 0x0003);
REGWRITE_2(VR4181_ISABRG_ADDR, XISACTL, 0x0401);
/*
* setup peripheral's clock supply
*
* CSU: disable
* AIU: enable (AIU, ADU, ADU18M)
* PIU: disable
* SIU: enable (SIU18M)
*/
REGWRITE_2(VR4181_CMU_ADDR, 0, CMUMASK_SIU | CMUMASK_AIU);
/*
* setup GPIO
*/
#if 0
/* L-Card+ generic setup */
/*
* pin mode comment
* GP0 : GPI not used
* GP1 : GPI not used
* GP2 : GPO LED6 (0: on 1: off)
* GP3 : PCS0 chip select for CS8900A Lan controller
* GP4 : GPI IRQ input from CS8900A
* GP5 : GPI not used
* GP6 : GPI not used
* GP7 : GPI reserved by TANBAC TB0193
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
GP3_PCS0 | GP2_GPO);
/*
* pin mode comment
* GP8 : GPO LED5 (0: on 1: off)
* GP9 : GPI CD2
* GP10: GPI CD1
* GP11: GPI not used
* GP12: GPI not used
* GP13: GPI not used
* GP14: GPI not used
* GP15: GPI not used
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W, GP8_GPO);
/*
* pin mode comment
* GP16: IORD ISA bus
* GP17: IOWR ISA bus
* GP18: IORDY ISA bus
* GP19: GPI not used
* GP20: GPI not used
* GP21: RESET resets CS8900A
* GP22: ROMCS0 ROM chip select
* GP23: ROMCS1 ROM chip select
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
| GP18_IORDY | GP17_IOWR | GP16_IORD);
/*
* GP24: ROMCS2 ROM chip select
* GP25: RxD1 SIU1
* GP26: TxD1 SIU1
* GP27: RTS1 SIU1
* GP28: CTS1 SIU1
* GP29: GPI LED3
* GP30: GPI reserved by TANBAC TB0193
* GP31: GPI LED4
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
GP30_GPI
| GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
| GP24_ROMCS2);
#else
/* e-care node specific setup */
/*
* pin mode comment
* GP0 : GPO ECNRTC_RST
* GP1 : GPO ECNRTC_CLK
* GP2 : GPO LED6 (0: on 1: off)
* GP3 : PCS0 chip select for CS8900A Lan controller
* GP4 : GPI IRQ input from CS8900A
* GP5 : GPO ECNRTC_DIR
* GP6 : GPO ECNRTC_OUT
* GP7 : GPI reserved by TANBAC TB0193
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
GP6_GPO | GP5_GPO | GP3_PCS0
| GP2_GPO | GP1_GPO | GP0_GPO);
/*
* pin mode comment
* GP8 : GPO LED5 (0: on 1: off)
* GP9 : GPI CD2
* GP10: GPI CD1
* GP11: GPI not used
* GP12: GPI ECNRTC_IN
* GP13: GPI not used
* GP14: GPI not used
* GP15: GPI not used
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W,
GP12_GPI | GP8_GPO);
/*
* pin mode comment
* GP16: IORD ISA bus
* GP17: IOWR ISA bus
* GP18: IORDY ISA bus
* GP19: GPI not used
* GP20: GPI not used
* GP21: RESET resets CS8900A
* GP22: ROMCS0 ROM chip select
* GP23: ROMCS1 ROM chip select
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
| GP18_IORDY | GP17_IOWR | GP16_IORD);
/*
* GP24: ROMCS2 ROM chip select
* GP25: RxD1 SIU1
* GP26: TxD1 SIU1
* GP27: RTS1 SIU1
* GP28: CTS1 SIU1
* GP29: GPI LED3
* GP30: GPI reserved by TANBAC TB0193
* GP31: GPI LED4
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
GP30_GPI
| GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
| GP24_ROMCS2);
#endif
#if 0
/*
* setup interrupt
*
* I4TYP: falling edge trigger
* GIMSK4: unmask
* GIEN4: enable
* other: unused, mask, disable
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTTYP_L_REG_W,
I4TYP_HIGH_LEVEL);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTMASK_REG_W,
0xffffU & ~GIMSK4);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTEN_REG_W, GIEN4);
#endif
/*
* programmable chip select
*
* PCS0 is used to select CS8900A Ethernet controller
* on TB0193
*
* PCS0:
* 0x14010000 - 0x14010fff
* I/O access, 16bit cycle, both of read/write
* PCS1: unused
*/
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STRA_REG_W, 0x0000);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STPA_REG_W, 0x0fff);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0HIA_REG_W, 0x1401);
REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCSMODE_REG_W,
PCS0MIOB_IO | PCS0DSIZE_16BIT | PCS0MD_READWRITE);
}
/*
* chops the head from the arguments and returns the arguments if any,
* or possibly an empty string.
*/
static char *
get_next_arg(char *arg)
{
char *opt;
if ((opt = strchr(arg, ' ')) == NULL) {
opt = "";
} else {
*opt++ = '\0';
}
/* trim leading blanks */
while (*opt == ' ')
opt++;
return opt;
}
static void
command_help(char *opt)
{
printf("commands are:\n"
"boot:\tb\n"
"dump:\td addr [addr]\n"
"fill:\tf addr addr char\n"
"load:\tl [offset] (with following S-Record)\n"
"write:\tw dst src len\n"
"option:\to subcommand [params]\n"
"help:\th|?\n"
"\n"
"option subcommands are:\n"
"print:\to p\n"
"read:\to r\n"
"write:\to w\n"
"path:\to path pathname\n"
"bootp:\to bootp yes|no\n"
"ip:\to ip remote local netmask gateway\n"
);
}
static void
bad_param(void)
{
printf("bad param\n");
command_help(NULL);
}
static const u_int8_t print_cnv[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
static void
printhexul(u_int32_t n)
{
int i;
for (i = 28; i >= 0; i -= 4)
putchar(print_cnv[(n >> i) & 0x0f]);
}
static void
printhexuc(u_int8_t n)
{
int i;
for (i = 4; i >= 0; i -= 4)
putchar(print_cnv[(n >> i) & 0x0f]);
}
static void
command_dump(char *opt)
{
char *endptr;
const char *p;
const char *line_fence;
const char *limit;
p = (const char *) strtoul(opt, &endptr, 16);
if (opt == endptr) {
bad_param();
return;
}
opt = get_next_arg(opt);
limit = (const char *) strtoul(opt, &endptr, 16);
if (opt == endptr) {
limit = p + 256;
}
for (;;) {
printhexul((u_int32_t) p);
putchar(' ');
line_fence = p + 16;
while (p < line_fence) {
printhexuc(*p++);
putchar(' ');
if (p >= limit) {
putchar('\n');
return;
}
}
putchar('\n');
if (ISKEY) {
if (getchar() == '\x03')
break;
}
}
}
static void
command_boot(char *opt)
{
u_long marks[MARK_MAX];
marks[MARK_START] = 0;
if (loadfile(bootopts.b_pathname, marks, LOAD_KERNEL)) {
printf("loadfile failed\n");
return;
}
start_netbsd();
/* no return */
}
/*
* loading S-Record
*/
static int
load_srec(char *offset)
{
char s2lbuf[9];
char c;
char rectype;
u_int32_t reclen;
u_int32_t reclen_bk;
u_int32_t recaddr;
char *endptr;
char *p;
u_int32_t sum;
int err = 0;
for (;;) {
/*
* the first step is to read a S-Record.
*/
if ((c = getchar()) != 'S')
goto out;
rectype = getchar();
s2lbuf[0] = getchar();
s2lbuf[1] = getchar();
s2lbuf[2] = '\0';
reclen_bk = reclen = strtoul(s2lbuf, &endptr, 16);
if (endptr != &s2lbuf[2])
goto out;
sum = reclen;
p = s2lbuf;
switch (rectype) {
case '0':
/* just ignore */
do {
c = getchar();
} while (c != '\r' && c != '\n');
continue;
case '3':
*p++ = getchar();
*p++ = getchar();
reclen--;
/* FALLTHRU */
case '2':
*p++ = getchar();
*p++ = getchar();
reclen--;
/* FALLTHRU */
case '1':
*p++ = getchar();
*p++ = getchar();
*p++ = getchar();
*p++ = getchar();
*p = '\0';
reclen -= 2;
recaddr = strtoul(s2lbuf, &endptr, 16);
if (endptr != p)
goto out;
sum += (recaddr >> 24) & 0xff;
sum += (recaddr >> 16) & 0xff;
sum += (recaddr >> 8) & 0xff;
sum += recaddr & 0xff;
p = offset + recaddr;
/*
* XXX
* address range is must be chaked here!
*/
reclen--;
s2lbuf[2] = '\0';
while (reclen > 0) {
s2lbuf[0] = getchar();
s2lbuf[1] = getchar();
*p = (u_int8_t) strtoul(s2lbuf, &endptr, 16);
if (endptr != &s2lbuf[2])
goto out;
sum += *p++;
reclen--;
}
break;
case '7':
case '8':
case '9':
goto out2;
default:
goto out;
}
s2lbuf[0] = getchar();
s2lbuf[1] = getchar();
s2lbuf[2] = '\0';
sum += (strtoul(s2lbuf, &endptr, 16) & 0xff);
sum &= 0xff;
if (sum != 0xff) {
printf("checksum error\n");
err = 1;
goto out2;
}
c = getchar();
if (c != '\r' && c != '\n')
goto out;
}
/* never reach */
return 1;
out:
printf("invalid S-Record\n");
err = 1;
out2:
do {
c = getchar();
} while (c != '\r' && c != '\n');
return err;
}
static void
command_load(char *opt)
{
char *endptr;
char *offset;
offset = (char *) strtoul(opt, &endptr, 16);
if (opt == endptr)
offset = 0;
load_srec(offset);
}
static void
command_fill(char *opt)
{
char *endptr;
char *p;
char *limit;
int c;
p = (char *) strtoul(opt, &endptr, 16);
if (opt == endptr) {
bad_param();
return;
}
opt = get_next_arg(opt);
limit = (char *) strtoul(opt, &endptr, 16);
if (opt == endptr) {
bad_param();
return;
}
opt = get_next_arg(opt);
c = strtoul(opt, &endptr, 16);
if (opt == endptr)
c = '\0';
memset(p, c, limit - p);
}
static void
check_write_verify_flash(u_int32_t src, u_int32_t dst, size_t len)
{
int status;
if ((dst & I28F128_BLOCK_MASK) != 0) {
printf("dst addr must be aligned to block boundary (0x%x)\n",
I28F128_BLOCK_SIZE);
return;
}
if (i28f128_probe((void *) dst)) {
printf("dst addr is not a intel 28F128\n");
} else {
printf("intel 28F128 detected\n");
}
if ((status = i28f128_region_write((void *) dst, (void *) src, len))
!= 0) {
printf("write mem to flash failed status = %x\n", status);
return;
}
printf("verifying...");
if (memcmp((void *) dst, (void *) src, len)) {
printf("verify error\n");
return;
}
printf("ok\n");
printf("writing memory to flash succeeded\n");
}
static void
command_write(char *opt)
{
char *endptr;
u_int32_t src;
u_int32_t dst;
size_t len;
dst = strtoul(opt, &endptr, 16);
if (opt == endptr)
goto out;
opt = get_next_arg(opt);
src = strtoul(opt, &endptr, 16);
if (opt == endptr)
goto out;
opt = get_next_arg(opt);
len = strtoul(opt, &endptr, 16);
if (opt == endptr)
goto out;
check_write_verify_flash(src, dst, len);
return;
out:
bad_param();
return;
}
static void
command_option(char *subcmd)
{
char *opt;
int i;
opt = get_next_arg(subcmd);
/* dispatch subcommand */
for (i = 0; opt_subcommands[i].c_name != NULL; i++) {
if (strcmp(subcmd, opt_subcommands[i].c_name) == 0) {
opt_subcommands[i].c_fn(opt);
break;
}
}
if (opt_subcommands[i].c_name == NULL) {
printf("unknown option subcommand\n");
command_help(NULL);
}
}
static void
opt_subcmd_print(char *opt)
{
printf("boot options:\n"
"magic:\t\t%s\n"
"pathname:\t`%s'\n"
"bootp:\t\t%s\n",
bootopts.b_magic == BOOTOPT_MAGIC ? "ok" : "bad",
bootopts.b_pathname,
bootopts.b_flags & B_F_USE_BOOTP ? "yes" : "no");
printf("remote IP:\t%s\n", inet_ntoa(bootopts.b_remote_ip));
printf("local IP:\t%s\n", inet_ntoa(bootopts.b_local_ip));
printf("netmask:\t%s\n", intoa(bootopts.b_netmask));
printf("gateway IP:\t%s\n", inet_ntoa(bootopts.b_gate_ip));
}
static void
opt_subcmd_read(char *opt)
{
bootopts = *((struct boot_option *) BOOTOPTS_BASE);
if (bootopts.b_magic != BOOTOPT_MAGIC)
bootopts.b_pathname[0] = '\0';
}
static void
opt_subcmd_write(char *opt)
{
bootopts.b_magic = BOOTOPT_MAGIC;
check_write_verify_flash((u_int32_t) &bootopts, BOOTOPTS_BASE,
sizeof bootopts);
}
static void
opt_subcmd_path(char *opt)
{
strlcpy(bootopts.b_pathname, opt, sizeof bootopts.b_pathname);
}
static void
opt_subcmd_bootp(char *opt)
{
if (strcmp(opt, "yes") == 0) {
bootopts.b_flags |= B_F_USE_BOOTP;
} else if (strcmp(opt, "no") == 0) {
bootopts.b_flags &= ~B_F_USE_BOOTP;
} else {
bad_param();
}
}
static void
opt_subcmd_ip(char *opt)
{
bootopts.b_remote_ip.s_addr = inet_addr(opt);
opt = get_next_arg(opt);
bootopts.b_local_ip.s_addr = inet_addr(opt);
opt = get_next_arg(opt);
bootopts.b_netmask = inet_addr(opt);
opt = get_next_arg(opt);
bootopts.b_gate_ip.s_addr = inet_addr(opt);
}
static void
bootmenu(void)
{
char input[LINEBUFLEN];
char *cmd;
char *opt;
int i;
for (;;) {
/* input a line */
input[0] = '\0';
printf("> ");
kgets(input, sizeof(input));
cmd = input;
/* skip leading whitespace. */
while(*cmd == ' ')
cmd++;
if(*cmd) {
/* here, some command entered */
opt = get_next_arg(cmd);
/* dispatch command */
for (i = 0; commands[i].c_name != NULL; i++) {
if (strcmp(cmd, commands[i].c_name) == 0) {
commands[i].c_fn(opt);
break;
}
}
if (commands[i].c_name == NULL) {
printf("unknown command\n");
command_help(NULL);
}
}
}
}
static char
awaitkey(void)
{
int i;
int j;
char c = 0;
while (ISKEY)
getchar();
for (i = BOOTTIMEOUT; i > 0; i--) {
printf("%d\b", i);
for (j = 0; j < 1000000; j++) {
if (ISKEY) {
while (ISKEY)
c = getchar();
goto out;
}
}
}
out:
printf("0\n");
return(c);
}
__dead void
_rtt(void)
{
for (;;)
;
}
int
main(void)
{
char c;
init_devices();
comcninit();
opt_subcmd_read(NULL);
print_banner();
c = awaitkey();
if (c != '\r' && c != '\n' && c != '\0') {
printf("type \"?\" or \"h\" for help.\n");
bootmenu(); /* does not return */
}
command_boot(NULL);
/*
* command_boot() returns only if it failed to boot.
* we enter to boot menu in this case.
*/
bootmenu();
return 0;
}