Logo Search packages:      
Sourcecode: palo version File versions

pdc_misc.c

/* 
 * 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 DEFAULT_W 0x2
#define DEFAULT_E 0x1
#define PDC_PSW 21
#define PDC_RETURN_MASK 0
#define PDC_RETURN_DEFAULTS 1
#define PDC_SET_DEFAULTS 2
#define PDC_STABLE 10
#define HPHW_A_DIRECT 5
#define MUX_SVERSION 0x0d

void die(const char *s)
{
    puts(s);
    puts("\n");
}

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_RETURN_MASK, pdc_result);
    convert_from_wide(pdc_result);
    switch(r)
    {
    case 0:       /* PDC call worked */
      mask = pdc_result[0];
      if (wide && ((mask & DEFAULT_W) == 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_RETURN_DEFAULTS, pdc_result);
      convert_from_wide(pdc_result);
      if (wide)
      {
          if ((mask & DEFAULT_W) == 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] |= DEFAULT_W;
      }
      else
      {
          pdc_result[0] &= ~DEFAULT_W;
      }
      pdc_result[0] &= ~DEFAULT_E;
      /* Ask firmware to set the W bit appropriately */
      r = firmware_call(mem_pdc, PDC_PSW, PDC_SET_DEFAULTS, pdc_result[0]);
      if (r < 0)
      {
          printf("PDC_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_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,
            0, 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;

    if (s[0] == 0)
      /* this test is usually the one to catch overwriting palo with kernel */
      asm("\npdc_iodc_cout_test1fail: b,n .");

    if (size >= sizeof iodc_string)
      asm("\npdc_iodc_cout_test2fail: b,n .");

    memcpy(iodc_string, s, size);

    if (s[0] != iodc_string[0] || s[0] == 0)
      asm("\npdc_iodc_cout_test3fail: b,n .");

    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, size, 0);

    if (r != 0)
      asm("\npdc_iodc_cout_test4fail: b,n .");
}

int
pdc_iodc_bootin(unsigned devaddr, char *memaddr, unsigned size)
{
    int r;

    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, 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 */
}

Generated by  Doxygen 1.6.0   Back to index