parisc: Convert PDC console to an early console

Rewrite the PDC console to become an early console.
Beside the fact that now boot information is visible until another
(text- or graphics) console takes over, this benefits as well machines
with a yet-unsupported STI console and kgdb.

Signed-off-by: Helge Deller <deller@gmx.de>
This commit is contained in:
Helge Deller 2022-10-01 00:32:07 +02:00
parent b148766e2b
commit 027c3d345e
6 changed files with 32 additions and 249 deletions

View File

@ -19,9 +19,6 @@ extern unsigned long parisc_pat_pdc_cap; /* PDC capabilities (PAT) */
#define PDC_TYPE_SYSTEM_MAP 1 /* 32-bit, but supports PDC_SYSTEM_MAP */ #define PDC_TYPE_SYSTEM_MAP 1 /* 32-bit, but supports PDC_SYSTEM_MAP */
#define PDC_TYPE_SNAKE 2 /* Doesn't support SYSTEM_MAP */ #define PDC_TYPE_SNAKE 2 /* Doesn't support SYSTEM_MAP */
void pdc_console_init(void); /* in pdc_console.c */
void pdc_console_restart(void);
void setup_pdc(void); /* in inventory.c */ void setup_pdc(void); /* in inventory.c */
/* wrapper-functions from pdc.c */ /* wrapper-functions from pdc.c */

View File

@ -1,46 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* PDC Console support - ie use firmware to dump text via boot console * PDC early console support - use PDC firmware to dump text via boot console
* *
* Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org> * Copyright (C) 2001-2022 Helge Deller <deller@gmx.de>
* Copyright (C) 2000 Martin K Petersen <mkp at mkp.net>
* Copyright (C) 2000 John Marvin <jsm at parisc-linux.org>
* Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org>
* Copyright (C) 2000 Philipp Rumpf <prumpf with tux.org>
* Copyright (C) 2000 Michael Ang <mang with subcarrier.org>
* Copyright (C) 2000 Grant Grundler <grundler with parisc-linux.org>
* Copyright (C) 2001-2002 Ryan Bradetich <rbrad at parisc-linux.org>
* Copyright (C) 2001 Helge Deller <deller at parisc-linux.org>
* Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org>
* Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org>
* Copyright (C) 2010 Guy Martin <gmsoft at tuxicoman.be>
*/ */
/*
* The PDC console is a simple console, which can be used for debugging
* boot related problems on HP PA-RISC machines. It is also useful when no
* other console works.
*
* This code uses the ROM (=PDC) based functions to read and write characters
* from and to PDC's boot path.
*/
/* Define EARLY_BOOTUP_DEBUG to debug kernel related boot problems.
* On production kernels EARLY_BOOTUP_DEBUG should be undefined. */
#define EARLY_BOOTUP_DEBUG
#include <linux/kernel.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/string.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/major.h> #include <linux/serial_core.h>
#include <linux/tty.h> #include <linux/kgdb.h>
#include <asm/page.h> /* for PAGE0 */ #include <asm/page.h> /* for PAGE0 */
#include <asm/pdc.h> /* for iodc_call() proto and friends */ #include <asm/pdc.h> /* for iodc_call() proto and friends */
static DEFINE_SPINLOCK(pdc_console_lock); static DEFINE_SPINLOCK(pdc_console_lock);
static struct console pdc_cons;
static void pdc_console_write(struct console *co, const char *s, unsigned count) static void pdc_console_write(struct console *co, const char *s, unsigned count)
{ {
@ -54,7 +26,8 @@ static void pdc_console_write(struct console *co, const char *s, unsigned count)
spin_unlock_irqrestore(&pdc_console_lock, flags); spin_unlock_irqrestore(&pdc_console_lock, flags);
} }
int pdc_console_poll_key(struct console *co) #ifdef CONFIG_KGDB
static int kgdb_pdc_read_char(void)
{ {
int c; int c;
unsigned long flags; unsigned long flags;
@ -63,201 +36,40 @@ int pdc_console_poll_key(struct console *co)
c = pdc_iodc_getc(); c = pdc_iodc_getc();
spin_unlock_irqrestore(&pdc_console_lock, flags); spin_unlock_irqrestore(&pdc_console_lock, flags);
return c; return (c <= 0) ? NO_POLL_CHAR : c;
} }
static int pdc_console_setup(struct console *co, char *options) static void kgdb_pdc_write_char(u8 chr)
{ {
return 0; if (PAGE0->mem_cons.cl_class != CL_DUPLEX)
pdc_console_write(NULL, &chr, 1);
} }
#if defined(CONFIG_PDC_CONSOLE) static struct kgdb_io kgdb_pdc_io_ops = {
#include <linux/vt_kern.h> .name = "kgdb_pdc",
#include <linux/tty_flip.h> .read_char = kgdb_pdc_read_char,
.write_char = kgdb_pdc_write_char,
#define PDC_CONS_POLL_DELAY (30 * HZ / 1000)
static void pdc_console_poll(struct timer_list *unused);
static DEFINE_TIMER(pdc_console_timer, pdc_console_poll);
static struct tty_port tty_port;
static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
{
tty_port_tty_set(&tty_port, tty);
mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
return 0;
}
static void pdc_console_tty_close(struct tty_struct *tty, struct file *filp)
{
if (tty->count == 1) {
del_timer_sync(&pdc_console_timer);
tty_port_tty_set(&tty_port, NULL);
}
}
static int pdc_console_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
pdc_console_write(NULL, buf, count);
return count;
}
static unsigned int pdc_console_tty_write_room(struct tty_struct *tty)
{
return 32768; /* no limit, no buffer used */
}
static const struct tty_operations pdc_console_tty_ops = {
.open = pdc_console_tty_open,
.close = pdc_console_tty_close,
.write = pdc_console_tty_write,
.write_room = pdc_console_tty_write_room,
}; };
static void pdc_console_poll(struct timer_list *unused)
{
int data, count = 0;
while (1) {
data = pdc_console_poll_key(NULL);
if (data == -1)
break;
tty_insert_flip_char(&tty_port, data & 0xFF, TTY_NORMAL);
count ++;
}
if (count)
tty_flip_buffer_push(&tty_port);
if (pdc_cons.flags & CON_ENABLED)
mod_timer(&pdc_console_timer, jiffies + PDC_CONS_POLL_DELAY);
}
static struct tty_driver *pdc_console_tty_driver;
static int __init pdc_console_tty_driver_init(void)
{
struct tty_driver *driver;
int err;
/* Check if the console driver is still registered.
* It is unregistered if the pdc console was not selected as the
* primary console. */
struct console *tmp;
console_lock();
for_each_console(tmp)
if (tmp == &pdc_cons)
break;
console_unlock();
if (!tmp) {
printk(KERN_INFO "PDC console driver not registered anymore, not creating %s\n", pdc_cons.name);
return -ENODEV;
}
printk(KERN_INFO "The PDC console driver is still registered, removing CON_BOOT flag\n");
pdc_cons.flags &= ~CON_BOOT;
driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
TTY_DRIVER_RESET_TERMIOS);
if (IS_ERR(driver))
return PTR_ERR(driver);
tty_port_init(&tty_port);
driver->driver_name = "pdc_cons";
driver->name = "ttyB";
driver->major = MUX_MAJOR;
driver->minor_start = 0;
driver->type = TTY_DRIVER_TYPE_SYSTEM;
driver->init_termios = tty_std_termios;
tty_set_operations(driver, &pdc_console_tty_ops);
tty_port_link_device(&tty_port, driver, 0);
err = tty_register_driver(driver);
if (err) {
printk(KERN_ERR "Unable to register the PDC console TTY driver\n");
tty_port_destroy(&tty_port);
tty_driver_kref_put(driver);
return err;
}
pdc_console_tty_driver = driver;
return 0;
}
device_initcall(pdc_console_tty_driver_init);
static struct tty_driver * pdc_console_device (struct console *c, int *index)
{
*index = c->index;
return pdc_console_tty_driver;
}
#else
#define pdc_console_device NULL
#endif #endif
static struct console pdc_cons = { static int __init pdc_earlycon_setup(struct earlycon_device *device,
.name = "ttyB", const char *opt)
.write = pdc_console_write,
.device = pdc_console_device,
.setup = pdc_console_setup,
.flags = CON_BOOT | CON_PRINTBUFFER,
.index = -1,
};
static int pdc_console_initialized;
static void pdc_console_init_force(void)
{ {
if (pdc_console_initialized) struct console *earlycon_console;
return;
++pdc_console_initialized;
/* If the console is duplex then copy the COUT parameters to CIN. */ /* If the console is duplex then copy the COUT parameters to CIN. */
if (PAGE0->mem_cons.cl_class == CL_DUPLEX) if (PAGE0->mem_cons.cl_class == CL_DUPLEX)
memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons)); memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons));
/* register the pdc console */ earlycon_console = device->con;
register_console(&pdc_cons); earlycon_console->write = pdc_console_write;
} device->port.iotype = UPIO_MEM32BE;
void __init pdc_console_init(void) #ifdef CONFIG_KGDB
{ kgdb_register_io_module(&kgdb_pdc_io_ops);
#if defined(EARLY_BOOTUP_DEBUG) || defined(CONFIG_PDC_CONSOLE)
pdc_console_init_force();
#endif
#ifdef EARLY_BOOTUP_DEBUG
printk(KERN_INFO "Initialized PDC Console for debugging.\n");
#endif #endif
return 0;
} }
EARLYCON_DECLARE(pdc, pdc_earlycon_setup);
/*
* Used for emergencies. Currently only used if an HPMC occurs. If an
* HPMC occurs, it is possible that the current console may not be
* properly initialised after the PDC IO reset. This routine unregisters
* all of the current consoles, reinitializes the pdc console and
* registers it.
*/
void pdc_console_restart(void)
{
struct console *console;
if (pdc_console_initialized)
return;
/* If we've already seen the output, don't bother to print it again */
if (console_drivers != NULL)
pdc_cons.flags &= ~CON_PRINTBUFFER;
while ((console = console_drivers) != NULL)
unregister_console(console_drivers);
/* force registering the pdc console */
pdc_console_init_force();
}

View File

@ -70,6 +70,10 @@ void __init setup_cmdline(char **cmdline_p)
strlcat(p, "tty0", COMMAND_LINE_SIZE); strlcat(p, "tty0", COMMAND_LINE_SIZE);
} }
/* default to use early console */
if (!strstr(p, "earlycon"))
strlcat(p, " earlycon=pdc", COMMAND_LINE_SIZE);
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
if (boot_args[2] != 0) /* did palo pass us a ramdisk? */ if (boot_args[2] != 0) /* did palo pass us a ramdisk? */
{ {
@ -139,8 +143,6 @@ void __init setup_arch(char **cmdline_p)
if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE) if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE)
panic("KERNEL_INITIAL_ORDER too small!"); panic("KERNEL_INITIAL_ORDER too small!");
pdc_console_init();
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
if(parisc_narrow_firmware) { if(parisc_narrow_firmware) {
printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n"); printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n");

View File

@ -239,13 +239,6 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
/* unlock the pdc lock if necessary */ /* unlock the pdc lock if necessary */
pdc_emergency_unlock(); pdc_emergency_unlock();
/* maybe the kernel hasn't booted very far yet and hasn't been able
* to initialize the serial or STI console. In that case we should
* re-enable the pdc console, so that the user will be able to
* identify the problem. */
if (!console_drivers)
pdc_console_restart();
if (err) if (err)
printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n", printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n",
current->comm, task_pid_nr(current), str, err); current->comm, task_pid_nr(current), str, err);
@ -429,10 +422,6 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
/* unlock the pdc lock if necessary */ /* unlock the pdc lock if necessary */
pdc_emergency_unlock(); pdc_emergency_unlock();
/* restart pdc console if necessary */
if (!console_drivers)
pdc_console_restart();
/* Not all paths will gutter the processor... */ /* Not all paths will gutter the processor... */
switch(code){ switch(code){
@ -482,9 +471,7 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
unsigned long fault_space = 0; unsigned long fault_space = 0;
int si_code; int si_code;
if (code == 1) if (!irqs_disabled_flags(regs->gr[0]))
pdc_console_restart(); /* switch back to pdc if HPMC */
else if (!irqs_disabled_flags(regs->gr[0]))
local_irq_enable(); local_irq_enable();
/* Security check: /* Security check:

View File

@ -602,21 +602,6 @@ config SERIAL_MUX_CONSOLE
select SERIAL_CORE_CONSOLE select SERIAL_CORE_CONSOLE
default y default y
config PDC_CONSOLE
bool "PDC software console support"
depends on PARISC && !SERIAL_MUX && VT
help
Saying Y here will enable the software based PDC console to be
used as the system console. This is useful for machines in
which the hardware based console has not been written yet. The
following steps must be completed to use the PDC console:
1. create the device entry (mknod /dev/ttyB0 c 11 0)
2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0
3. Add device ttyB0 to /etc/securetty (if you want to log on as
root on this console.)
4. Change the kernel command console parameter to: console=ttyB0
config SERIAL_SUNSAB config SERIAL_SUNSAB
tristate "Sun Siemens SAB82532 serial support" tristate "Sun Siemens SAB82532 serial support"
depends on SPARC && PCI depends on SPARC && PCI

View File

@ -121,7 +121,7 @@ config KDB_DEFAULT_ENABLE
config KDB_KEYBOARD config KDB_KEYBOARD
bool "KGDB_KDB: keyboard as input device" bool "KGDB_KDB: keyboard as input device"
depends on VT && KGDB_KDB depends on VT && KGDB_KDB && !PARISC
default n default n
help help
KDB can use a PS/2 type keyboard for an input device KDB can use a PS/2 type keyboard for an input device