blob: 12c46137d8c92e3cd30128c4ddefcb2a194279a8 [file] [log] [blame]
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com
*/
#include <stdarg.h>
#include <stddef.h>
#include <asm/pdc.h>
#include "bootloader.h"
#undef PAGE0
#define PAGE0 ((struct zeropage *)0x00000000)
#define HPHW_A_DIRECT 5
#define MUX_SVERSION 0x0d
#define F_EXTEND(x) ((unsigned long)((x) | (0xffffffff00000000ULL)))
#define COMMAND_GLOBAL F_EXTEND(0xfffe0030)
#define CMD_RESET 5 /* reset any module */
void die(const char *s)
{
puts(s);
}
static int firmware_is_wide;
static long long mem_pdc;
void
firmware_init(int started_wide)
{
mem_pdc = PAGE0->mem_pdc;
mem_pdc |= (unsigned long long) PAGE0->mem_pdc_hi << 32;
firmware_is_wide = started_wide;
}
/* pdc_result[] is big enough for either narrow or wide calls */
static unsigned pdc_result[64] __attribute__ ((aligned (8)));
static char iodc_string[512] __attribute__ ((aligned (64)));
struct wide_stack {
unsigned long long arg0;
unsigned long long arg1;
unsigned long long arg2;
unsigned long long arg3;
unsigned long long arg4;
unsigned long long arg5;
unsigned long long arg6;
unsigned long long arg7;
unsigned long long arg8;
unsigned long long arg9;
unsigned long long arg10;
unsigned long long arg11;
unsigned long long arg12;
unsigned long long arg13;
unsigned long long frame_marker[2]; /* rp, previous sp */
unsigned long long sp;
/* in reality, there's nearly 8k of stack after this */
};
static int
firmware_call(unsigned long long fn, ...)
{
va_list args;
int r;
if (firmware_is_wide)
{
extern struct wide_stack real_stack;
extern unsigned int real64_call_asm(unsigned long long *,
unsigned long long *,
unsigned long long);
va_start(args, fn);
real_stack.arg0 = va_arg(args, unsigned long);
real_stack.arg1 = va_arg(args, unsigned long);
real_stack.arg2 = va_arg(args, unsigned long);
real_stack.arg3 = va_arg(args, unsigned long);
real_stack.arg4 = va_arg(args, unsigned long);
real_stack.arg5 = va_arg(args, unsigned long);
real_stack.arg6 = va_arg(args, unsigned long);
real_stack.arg7 = va_arg(args, unsigned long);
real_stack.arg8 = va_arg(args, unsigned long);
real_stack.arg9 = va_arg(args, unsigned long);
real_stack.arg10 = va_arg(args, unsigned long);
real_stack.arg11 = va_arg(args, unsigned long);
real_stack.arg12 = va_arg(args, unsigned long);
real_stack.arg13 = va_arg(args, unsigned long);
va_end(args);
r = real64_call_asm(&real_stack.sp, &real_stack.arg0, fn);
}
else
{
typedef int (*firmware_entry)();
unsigned long arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
arg9, arg10, arg11, arg12, arg13;
va_start(args, fn);
arg0 = va_arg(args, unsigned long);
arg1 = va_arg(args, unsigned long);
arg2 = va_arg(args, unsigned long);
arg3 = va_arg(args, unsigned long);
arg4 = va_arg(args, unsigned long);
arg5 = va_arg(args, unsigned long);
arg6 = va_arg(args, unsigned long);
arg7 = va_arg(args, unsigned long);
arg8 = va_arg(args, unsigned long);
arg9 = va_arg(args, unsigned long);
arg10 = va_arg(args, unsigned long);
arg11 = va_arg(args, unsigned long);
arg12 = va_arg(args, unsigned long);
arg13 = va_arg(args, unsigned long);
va_end(args);
r = (*(firmware_entry) (unsigned int) fn) (arg0, arg1, arg2, arg3, arg4,
arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
}
return r;
}
void
convert_from_wide(unsigned *retbuf)
{
if (firmware_is_wide)
{
*retbuf = *(unsigned long long *)retbuf;
}
}
/* flag=true means enable default wide mode.
*/
int
pdc_default_width(int wide)
{
int r;
int mask;
/* Ask firmware which default PSW bits we're allowed to set */
r = firmware_call(mem_pdc, PDC_PSW, PDC_PSW_MASK, pdc_result);
convert_from_wide(pdc_result);
switch(r)
{
case 0: /* PDC call worked */
mask = pdc_result[0];
if (wide && ((mask & PDC_PSW_WIDE_BIT) == 0))
{
die("Firmware does not allow selection of default wide mode.\n"
"Are you trying to boot a 64-bit kernel on a 32-bit box?");
return 1;
}
/* get the current default bit settings */
firmware_call(mem_pdc, PDC_PSW, PDC_PSW_GET_DEFAULTS, pdc_result);
convert_from_wide(pdc_result);
if (wide)
{
if ((mask & PDC_PSW_WIDE_BIT) == 0) {
die("Firmware does not allow selection of default wide mode.\n"
"Are you trying to boot a 64-bit kernel on a 32-bit box?");
return 1;
}
pdc_result[0] |= PDC_PSW_WIDE_BIT;
}
else
{
pdc_result[0] &= ~PDC_PSW_WIDE_BIT;
}
pdc_result[0] &= ~PDC_PSW_ENDIAN_BIT;
/* Ask firmware to set the W bit appropriately */
r = firmware_call(mem_pdc, PDC_PSW, PDC_PSW_SET_DEFAULTS, pdc_result[0]);
if (r < 0)
{
printf("PDC_PSW_SET_DEFAULTS returns error %d\n", r);
die("Requested default wide/narrow mode not set");
return 1;
}
if (0) printf("Set default PSW W bit to %d\n", wide);
break;
case -2: /* unsupported PDC call */
default:
/* assume that when this fails, it's an older machine which */
/* doesn't support wide mode */
if (wide)
{
die("Can't select default wide mode, PDC_PSW call does not work");
return 1;
}
else /* narrow */
{
if (0) printf("Warning: narrow mode requested, PDC_PSW call fails\n");
}
break;
}
return 0;
}
int
pdc_os_bits()
{
int r;
int osbits = 0x2; /* default to 32-bit OS */
r = firmware_call(mem_pdc, PDC_MODEL, PDC_MODEL_CAPABILITIES, pdc_result);
convert_from_wide(pdc_result);
if (r < 0)
{
if (0) printf("Annoyance: Firmware does not support PDC_MODEL_CAPABILITIES call\n");
}
else
{
osbits = pdc_result[0];
/* printf("Firmware OS bits = %d\n", osbits); */
}
return osbits;
}
int
pdc_coproc_cfg(void)
{
int r;
r = firmware_call(mem_pdc, PDC_COPROC, PDC_COPROC_CFG, pdc_result);
convert_from_wide(pdc_result);
if (r == PDC_OK) {
return pdc_result[0]; /* ccr_functional */
} else {
return 0;
}
}
int
pdc_model_sysmodel(char *name)
{
int r;
r = firmware_call(mem_pdc, PDC_MODEL, PDC_MODEL_SYSMODEL, pdc_result,
OS_ID_HPUX, name);
convert_from_wide(pdc_result);
if (r == PDC_OK) {
name[pdc_result[0]] = '\0'; /* add trailing '\0' */
} else {
name[0] = 0;
}
return r;
}
/*
See https://parisc.wiki.kernel.org/images-parisc/9/9c/Pdc11-v0.96-Ch3-IODC.pdf The
IODC_FEATURES (byte 10) byte specifies which optional IODC feature are
supported by this module. The rightmost bit (block field) specifies whether
ENTRY_IO support block input (ARG1=16) and Boot block output (ARG1=17) are
supported.
*/
int
pdc_bootdisk_2GB_limit(void)
{
int r;
struct pdc_iodc iodc __attribute__ ((aligned (8)));
iodc.features = 0;
r = firmware_call(mem_pdc, PDC_IODC, PDC_IODC_READ,
pdc_result, PAGE0->mem_boot.hpa,
PDC_IODC_INDEX_DATA, &iodc, sizeof(iodc));
/* check boot block feature */
if (r >= 0)
return (iodc.features & 1) == 0;
return 1;
}
int
pdc_cons_duplex()
{
return (PAGE0->mem_cons.cl_class == CL_DUPLEX);
}
int
pdc_cons_mux(int *is_mux)
{
int r;
unsigned char hw_type; /* 5 bits used */
unsigned int sversion; /* 20 bits used */
unsigned long pdc_result2[32] __attribute__ ((aligned (8)));
*is_mux = 0;
r = firmware_call(mem_pdc, PDC_IODC, PDC_IODC_READ,
pdc_result, PAGE0->mem_cons.hpa,
PDC_IODC_INDEX_DATA, pdc_result2, 32);
if (r >= 0)
{
unsigned char iodc_data[8];
memcpy(&iodc_data, pdc_result2, 8);
hw_type = iodc_data[3] & 0x1f;
sversion = ((iodc_data[4] & 0x0f) << 16) | (iodc_data[5] << 8) | iodc_data[6];
if(hw_type == HPHW_A_DIRECT && sversion == MUX_SVERSION)
*is_mux = 1;
return PDC_OK;
}
return r; /* r < 0; error */
}
int
pdc_iodc_cin(char *buf, int size)
{
int r;
struct pz_device *in = pdc_cons_duplex() ?
&PAGE0->mem_cons : &PAGE0->mem_kbd;
if (size >= sizeof iodc_string)
asm("\npdc_iodc_cin_test1fail: b,n .");
r = firmware_call(in->iodc_io,
in->hpa, ENTRY_IO_CIN,
in->spa, in->dp.layers,
pdc_result, 0, iodc_string, size, 0);
if (r >= 0)
{
convert_from_wide(pdc_result);
memcpy(buf, iodc_string, pdc_result[0]);
return pdc_result[0]; /* count */
}
return r; /* r < 0; error */
}
void
pdc_iodc_cout(const char *s, int size)
{
int r, len;
if (s[0] == 0)
/* this test is usually the one to catch overwriting palo with kernel */
while (1) { /* endless loop */ };
while (size) {
if (size >= sizeof iodc_string)
len = sizeof iodc_string;
else
len = size;
memcpy(iodc_string, s, len);
size -= len;
s += len;
r = firmware_call(PAGE0->mem_cons.iodc_io,
PAGE0->mem_cons.hpa, ENTRY_IO_COUT,
PAGE0->mem_cons.spa, PAGE0->mem_cons.dp.layers,
pdc_result, 0, iodc_string, len, 0);
if (r != 0)
while (1) { /* endless loop */ };
}
}
int
pdc_iodc_bootin(__u64 devaddr, char *memaddr, unsigned size)
{
int r = -1;
if (!disk_2gb_limit) {
unsigned long a = devaddr / FW_BLOCKSIZE;
unsigned long s = (size + FW_BLOCKSIZE - 1) / FW_BLOCKSIZE;
r = firmware_call(PAGE0->mem_boot.iodc_io,
PAGE0->mem_boot.hpa, ENTRY_IO_BBLOCK_IN,
PAGE0->mem_boot.spa, PAGE0->mem_boot.dp.layers,
pdc_result, a, memaddr, s, s);
if (r >= 0)
{
convert_from_wide(pdc_result);
/* ENTRY_IO_BBLOCK_IN returns blocks, not bytes */
return pdc_result[0] * FW_BLOCKSIZE; /* return count in bytes */
}
}
if (r < 0) {
/* check for overflow - ENTRY_IO_BOOTIN allows up to 2GB only */
if (devaddr >> 31)
r = PDC_INVALID_ARG;
else
r = firmware_call(PAGE0->mem_boot.iodc_io,
PAGE0->mem_boot.hpa, ENTRY_IO_BOOTIN,
PAGE0->mem_boot.spa, PAGE0->mem_boot.dp.layers,
pdc_result, (unsigned long)devaddr, memaddr, size, size);
}
if (r == 3) /* EOF */
{
return 0; /* count = 0 at EOF */
}
else if (r >= 0)
{
convert_from_wide(pdc_result);
return pdc_result[0]; /* count */
}
return r; /* r < 0; error */
}
int
pdc_read_conspath(unsigned char *memaddr)
{
int r;
r = firmware_call(mem_pdc, PDC_STABLE, 0, 96, memaddr, 8);
if (r >= 0)
{
convert_from_wide(pdc_result);
return pdc_result[0]; /* count */
}
return r; /* r < 0; error */
}
static inline void gsc_writel(unsigned int val, unsigned long addr)
{
__asm__ __volatile__(
" stwas %0,0(%1)\n"
: : "r" (val), "r" (addr) );
}
int
pdc_do_reset(void)
{
printf("Resetting machine.\n");
/* reset the machine - this call will most likely not return. */
firmware_call(mem_pdc, PDC_BROADCAST_RESET, PDC_DO_RESET);
/* Nope...box should reset with just CMD_RESET now */
gsc_writel(CMD_RESET, COMMAND_GLOBAL);
/* Wait for RESET to lay us to rest. */
while (1) /* wait */;
return -1;
}