File: pdc_misc.c

package info (click to toggle)
palo 2.27
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 512 kB
  • sloc: ansic: 6,304; asm: 218; makefile: 196; sh: 61
file content (456 lines) | stat: -rw-r--r-- 11,310 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
/* 
 * 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 Pdc11-v0.96-Ch3-IODC.pdf at https://parisc.docs.kernel.org/en/latest/technical_documentation.html
  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;

    /* limit transfers per PDC call to this size. PDC on C8000 had issues. */
    if (size > MAX_IO_LIMIT)
	size = MAX_IO_LIMIT;

    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;
}