Files
linux-stable-mirror/arch/mips/include/asm/dec/prom.h
T
Maciej W. RozyckiandThomas Bogendoerfer 5ff79e8bdc MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf()
In 64-bit configurations calling any firmware entry points from a kernel
thread other than the initial one will result in a situation where the
stack has been placed in the XKPHYS 64-bit memory segment.

Consequently the stack pointer is no longer a 32-bit value and when the
32-bit firmware code called uses 32-bit ALU operations to manipulate the
stack pointer, the calculated result is incorrect (in fact in the 64-bit
MIPS ISA almost all 32-bit ALU operations will produce an unpredictable
result when executed on 64-bit data) and control goes astray.

This may happen when no final console driver has been enabled in the
configuration and consequently the initial console continues being used
late into bootstrap, or with an upcoming change that will switch the zs
driver to use a platform device, which in turn will make the console
handover happen only after other kernel threads have already been
started, and the kernel will hang at:

  pid_max: default: 32768 minimum: 301

or somewhat later, but always before:

  cblist_init_generic: Setting adjustable number of callback queues.

has been printed.

It seems that only the prom_printf() entry point is affected.  Of all
the other entry points wired only rex_slot_address() and rex_gettcinfo()
are called from a kernel thread other than the initial one, specifically
kernel_init(), and they are leaf functions that do no business with the
stack, having worked with no issue ever since 64-bit support was added
for the platform back in 2002.

To address this issue then, arrange for the stack to be switched in the
o32 wrapper as required for prom_printf() only, by supplying call_o32()
with a pointer to a chunk of initdata space, which is placed in the
CKSEG0 32-bit compatibility segment, observing that prom_printf() is
only called from console output handler and therefore with the console
lock held, implying no need for this code to be reentrant.

Other firmware entry points may be called with interrupts enabled and no
lock held, and may therefore require that call_o32() be reentrant.  They
trigger no issue at this point and "if it ain't broke, don't fix it," so
just leave them alone.

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # v2.6.12+
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
2026-05-26 16:35:36 +02:00

176 lines
5.4 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* include/asm-mips/dec/prom.h
*
* DECstation PROM interface.
*
* Copyright (C) 2002, 2026 Maciej W. Rozycki
*
* Based on arch/mips/dec/prom/prom.h by the Anonymous.
*/
#ifndef _ASM_DEC_PROM_H
#define _ASM_DEC_PROM_H
#include <linux/types.h>
#include <asm/addrspace.h>
/*
* PMAX/3MAX PROM entry points for DS2100/3100's and DS5000/2xx's.
* Many of these will work for MIPSen as well!
*/
#define VEC_RESET (u64 *)CKSEG1ADDR(0x1fc00000)
/* Prom base address */
#define PMAX_PROM_ENTRY(x) (VEC_RESET + (x)) /* Prom jump table */
#define PMAX_PROM_HALT PMAX_PROM_ENTRY(2) /* valid on MIPSen */
#define PMAX_PROM_AUTOBOOT PMAX_PROM_ENTRY(5) /* valid on MIPSen */
#define PMAX_PROM_OPEN PMAX_PROM_ENTRY(6)
#define PMAX_PROM_READ PMAX_PROM_ENTRY(7)
#define PMAX_PROM_CLOSE PMAX_PROM_ENTRY(10)
#define PMAX_PROM_LSEEK PMAX_PROM_ENTRY(11)
#define PMAX_PROM_GETCHAR PMAX_PROM_ENTRY(12)
#define PMAX_PROM_PUTCHAR PMAX_PROM_ENTRY(13) /* 12 on MIPSen */
#define PMAX_PROM_GETS PMAX_PROM_ENTRY(15)
#define PMAX_PROM_PRINTF PMAX_PROM_ENTRY(17)
#define PMAX_PROM_GETENV PMAX_PROM_ENTRY(33) /* valid on MIPSen */
/*
* Magic number indicating REX PROM available on DECstation. Found in
* register a2 on transfer of control to program from PROM.
*/
#define REX_PROM_MAGIC 0x30464354
/* KN04 and KN05 are REX PROMs, so only do the check for R3k systems. */
static inline bool prom_is_rex(u32 magic)
{
return !IS_ENABLED(CONFIG_CPU_R3000) || magic == REX_PROM_MAGIC;
}
/*
* 3MIN/MAXINE PROM entry points for DS5000/1xx's, DS5000/xx's and
* DS5000/2x0.
*/
#define REX_PROM_GETBITMAP 0x84/4 /* get mem bitmap */
#define REX_PROM_GETCHAR 0x24/4 /* getch() */
#define REX_PROM_GETENV 0x64/4 /* get env. variable */
#define REX_PROM_GETSYSID 0x80/4 /* get system id */
#define REX_PROM_GETTCINFO 0xa4/4
#define REX_PROM_PRINTF 0x30/4 /* printf() */
#define REX_PROM_SLOTADDR 0x6c/4 /* slotaddr */
#define REX_PROM_BOOTINIT 0x54/4 /* open() */
#define REX_PROM_BOOTREAD 0x58/4 /* read() */
#define REX_PROM_CLEARCACHE 0x7c/4
/*
* Used by rex_getbitmap().
*/
typedef struct {
int pagesize;
unsigned char bitmap[];
} memmap;
/*
* Function pointers as read from a PROM's callback vector.
*/
extern int (*__rex_bootinit)(void);
extern int (*__rex_bootread)(void);
extern int (*__rex_getbitmap)(memmap *);
extern unsigned long *(*__rex_slot_address)(int);
extern void *(*__rex_gettcinfo)(void);
extern int (*__rex_getsysid)(void);
extern void (*__rex_clear_cache)(void);
extern int (*__prom_getchar)(void);
extern char *(*__prom_getenv)(char *);
extern int (*__prom_printf)(char *, ...);
extern int (*__pmax_open)(char*, int);
extern int (*__pmax_lseek)(int, long, int);
extern int (*__pmax_read)(int, void *, int);
extern int (*__pmax_close)(int);
#ifdef CONFIG_64BIT
#define O32_STK_SIZE 512
extern unsigned long o32_stk[];
/* Switch the stack if outside the 32-bit address space. */
static inline unsigned long *o32_get_stk(void)
{
long fp = (long)__builtin_frame_address(0);
return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL;
}
/*
* On MIPS64 we have to call PROM functions via a helper
* dispatcher to accommodate ABI incompatibilities.
*/
#define __DEC_PROM_O32(fun, arg) fun arg __asm__(#fun); \
__asm__(#fun " = call_o32")
int __DEC_PROM_O32(_rex_bootinit, (int (*)(void), void *));
int __DEC_PROM_O32(_rex_bootread, (int (*)(void), void *));
int __DEC_PROM_O32(_rex_getbitmap, (int (*)(memmap *), void *, memmap *));
unsigned long *__DEC_PROM_O32(_rex_slot_address,
(unsigned long *(*)(int), void *, int));
void *__DEC_PROM_O32(_rex_gettcinfo, (void *(*)(void), void *));
int __DEC_PROM_O32(_rex_getsysid, (int (*)(void), void *));
void __DEC_PROM_O32(_rex_clear_cache, (void (*)(void), void *));
int __DEC_PROM_O32(_prom_getchar, (int (*)(void), void *));
char *__DEC_PROM_O32(_prom_getenv, (char *(*)(char *), void *, char *));
int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...));
#define rex_bootinit() _rex_bootinit(__rex_bootinit, NULL)
#define rex_bootread() _rex_bootread(__rex_bootread, NULL)
#define rex_getbitmap(x) _rex_getbitmap(__rex_getbitmap, NULL, x)
#define rex_slot_address(x) _rex_slot_address(__rex_slot_address, NULL, x)
#define rex_gettcinfo() _rex_gettcinfo(__rex_gettcinfo, NULL)
#define rex_getsysid() _rex_getsysid(__rex_getsysid, NULL)
#define rex_clear_cache() _rex_clear_cache(__rex_clear_cache, NULL)
#define prom_getchar() _prom_getchar(__prom_getchar, NULL)
#define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x)
#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x)
#else /* !CONFIG_64BIT */
/*
* On plain MIPS we just call PROM functions directly.
*/
#define rex_bootinit __rex_bootinit
#define rex_bootread __rex_bootread
#define rex_getbitmap __rex_getbitmap
#define rex_slot_address __rex_slot_address
#define rex_gettcinfo __rex_gettcinfo
#define rex_getsysid __rex_getsysid
#define rex_clear_cache __rex_clear_cache
#define prom_getchar __prom_getchar
#define prom_getenv __prom_getenv
#define prom_printf __prom_printf
#define pmax_open __pmax_open
#define pmax_lseek __pmax_lseek
#define pmax_read __pmax_read
#define pmax_close __pmax_close
#endif /* !CONFIG_64BIT */
extern void prom_meminit(u32);
extern void prom_identify_arch(u32);
extern void prom_init_cmdline(s32, s32 *, u32);
extern void register_prom_console(void);
#endif /* _ASM_DEC_PROM_H */