Write to HVC terminal from purgatory code Current x86/x86-64 kexec-tools print the message "I'm in purgatory" to serial console/VGA while executing the purgatory code. Implement this feature for POWERPC pseries platform by using the H_PUT_TERM_CHAR hypervisor call by printng to hvc console. Signed-off-by: M. Mohan Kumar <mohan at in.ibm.com> --- kexec/arch/ppc64/fs2dt.c | 47 +++++++++++++++++++++++++++++++- kexec/arch/ppc64/kexec-elf-ppc64.c | 7 +++++ kexec/arch/ppc64/kexec-ppc64.h | 1 + purgatory/arch/ppc64/Makefile | 1 + purgatory/arch/ppc64/console-ppc64.c | 14 +++++++++ purgatory/arch/ppc64/hvCall.S | 28 +++++++++++++++++++ purgatory/arch/ppc64/hvCall.h | 8 +++++ purgatory/arch/ppc64/purgatory-ppc64.c | 1 + 8 files changed, 106 insertions(+), 1 deletions(-) create mode 100644 purgatory/arch/ppc64/hvCall.S create mode 100644 purgatory/arch/ppc64/hvCall.h diff --git a/kexec/arch/ppc64/fs2dt.c b/kexec/arch/ppc64/fs2dt.c index b01ff86..bd9d36c 100644 --- a/kexec/arch/ppc64/fs2dt.c +++ b/kexec/arch/ppc64/fs2dt.c @@ -434,6 +434,9 @@ static void putnode(void) if (!strcmp(basename,"/chosen/")) { size_t cmd_len = 0; char *param = NULL; + char filename[MAXPATH]; + char buff[64]; + int fd; cmd_len = strlen(local_cmdline); if (cmd_len != 0) { @@ -446,7 +449,6 @@ static void putnode(void) /* ... if not, grab root= from the old command line */ if (!param) { - char filename[MAXPATH]; FILE *fp; char *last_cmdline = NULL; char *old_param; @@ -483,8 +485,51 @@ static void putnode(void) dt += (cmd_len + 3)/4; fprintf(stderr, "Modified cmdline:%s\n", local_cmdline); + + /* + * Determine the platform type/stdout type, so that purgatory + * code can print 'I'm in purgatory' message. Currently only + * pseries/hvcterminal is supported. + */ + strcpy(filename, pathname); + strcat(filename, "linux,stdout-path"); + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find linux,stdout-path, printing" + " from purgatory is diabled\n"); + goto no_debug; + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat linux,stdout-path, printing" + " from purgatory is diabled\n"); + close(fd); + goto no_debug; + } + read(fd, buff, statbuf.st_size); + close(fd); + strcpy(filename, "/proc/device-tree/"); + strcat(filename, buff); + strcat(filename, "/compatible"); + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find linux,stdout-path/compatible, " + " printing from purgatory is diabled\n"); + goto no_debug; + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat linux,stdout-path/compatible, " + " printing from purgatory is diabled\n"); + close(fd); + goto no_debug; + } + read(fd, buff, statbuf.st_size); + if (!strcmp(buff, "hvterm1") || + !strcmp(buff, "hvterm-protocol")) + my_debug = 1; + close(fd); } +no_debug: for (i=0; i < numlist; i++) { dp = namelist[i]; strcpy(dn, dp->d_name); diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c index 21533cb..65fc42f 100644 --- a/kexec/arch/ppc64/kexec-elf-ppc64.c +++ b/kexec/arch/ppc64/kexec-elf-ppc64.c @@ -41,6 +41,8 @@ uint64_t initrd_base, initrd_size; unsigned char reuse_initrd = 0; const char *ramdisk; +/* Used for enabling printing message from purgatory code */ +int my_debug = 0; int elf_ppc64_probe(const char *buf, off_t len) { @@ -296,6 +298,8 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, toc_addr = my_r2(&info->rhdr); elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); + /* Set debug */ + elf_rel_set_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); #ifdef DEBUG my_kernel = 0; my_dt_offset = 0; @@ -304,6 +308,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, my_stack = 0; toc_addr = 0; my_run_at_load = 0; + my_debug = 0; elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset, @@ -317,6 +322,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); + elf_rel_get_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); fprintf(stderr, "info->entry is %p\n", info->entry); fprintf(stderr, "kernel is %llx\n", (unsigned long long)my_kernel); @@ -329,6 +335,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, fprintf(stderr, "stack is %llx\n", (unsigned long long)my_stack); fprintf(stderr, "toc_addr is %llx\n", (unsigned long long)toc_addr); fprintf(stderr, "purgatory size is %zu\n", purgatory_size); + fprintf(stderr, "debug is %d\n", my_debug); #endif for (i = 0; i < info->nr_segments; i++) diff --git a/kexec/arch/ppc64/kexec-ppc64.h b/kexec/arch/ppc64/kexec-ppc64.h index 920ac46..838c6da 100644 --- a/kexec/arch/ppc64/kexec-ppc64.h +++ b/kexec/arch/ppc64/kexec-ppc64.h @@ -20,6 +20,7 @@ unsigned long my_r2(const struct mem_ehdr *ehdr); extern uint64_t initrd_base, initrd_size; extern int max_memory_ranges; extern unsigned char reuse_initrd; +extern int my_debug; /* boot block version 2 as defined by the linux kernel */ struct bootblock { diff --git a/purgatory/arch/ppc64/Makefile b/purgatory/arch/ppc64/Makefile index aaa4046..40a9e99 100644 --- a/purgatory/arch/ppc64/Makefile +++ b/purgatory/arch/ppc64/Makefile @@ -3,6 +3,7 @@ # ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/v2wrap.S +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/hvCall.S ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/purgatory-ppc64.c ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/console-ppc64.c ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/crashdump_backup.c diff --git a/purgatory/arch/ppc64/console-ppc64.c b/purgatory/arch/ppc64/console-ppc64.c index d6da7b3..78a233b 100644 --- a/purgatory/arch/ppc64/console-ppc64.c +++ b/purgatory/arch/ppc64/console-ppc64.c @@ -20,8 +20,22 @@ */ #include <purgatory.h> +#include "hvCall.h" + +extern int debug; void putchar(int c) { + char buff[16]; + unsigned long *lbuf = (unsigned long *)buff; + + if (!debug) /* running on non pseries */ + return; + + if (c == '\n') + putchar('\r'); + + buff[0] = c; + plpar_hcall_norets(H_PUT_TERM_CHAR, 0, 1, lbuf[0], lbuf[1]); return; } diff --git a/purgatory/arch/ppc64/hvCall.S b/purgatory/arch/ppc64/hvCall.S new file mode 100644 index 0000000..e401f81 --- /dev/null +++ b/purgatory/arch/ppc64/hvCall.S @@ -0,0 +1,28 @@ +/* + * This file contains the generic function to perform a call to the + * pSeries LPAR hypervisor. + * + * Created by M. Mohan Kumar (mohan at in.ibm.com) + * Copyright (C) IBM Corporation + * Taken from linux/arch/powerpc/platforms/pseries/hvCall.S + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define HVSC .long 0x44000022 +.text + .machine ppc64 +.globl .plpar_hcall_norets +.plpar_hcall_norets: + or 6,6,6 # medium low priority + mfcr 0 + stw 0,8(1) + + HVSC /* invoke the hypervisor */ + + lwz 0,8(1) + mtcrf 0xff,0 + blr /* return r3 = status */ diff --git a/purgatory/arch/ppc64/hvCall.h b/purgatory/arch/ppc64/hvCall.h new file mode 100644 index 0000000..187e24d --- /dev/null +++ b/purgatory/arch/ppc64/hvCall.h @@ -0,0 +1,8 @@ +#ifndef HVCALL_H +#define HVCALL_H + +#define H_PUT_TERM_CHAR 0x58 + +long plpar_hcall_norets(unsigned long opcode, ...); + +#endif diff --git a/purgatory/arch/ppc64/purgatory-ppc64.c b/purgatory/arch/ppc64/purgatory-ppc64.c index 93f28d2..0b6d326 100644 --- a/purgatory/arch/ppc64/purgatory-ppc64.c +++ b/purgatory/arch/ppc64/purgatory-ppc64.c @@ -28,6 +28,7 @@ unsigned long stack = 0; unsigned long dt_offset = 0; unsigned long my_toc = 0; unsigned long kernel = 0; +unsigned int debug = 0; void setup_arch(void) { -- 1.6.2.5