]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
x86/kexec: Add 8250 serial port output
authorDavid Woodhouse <dwmw@amazon.co.uk>
Sat, 23 Nov 2024 09:23:24 +0000 (09:23 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 26 Mar 2025 10:20:06 +0000 (10:20 +0000)
If a serial port was configured for early_printk, use it for debug output
from the relocate_kernel exception handler too.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
arch/x86/include/asm/kexec.h
arch/x86/kernel/early_printk.c
arch/x86/kernel/relocate_kernel_64.S

index fb4537c58b54a9ec75fe9be5d19ace1abdf32634..9601094ac0ba3f41a00acd84eca94c5590b5f316 100644 (file)
@@ -64,6 +64,7 @@ extern unsigned long kexec_pa_table_page;
 extern unsigned long kexec_pa_swap_page;
 extern gate_desc kexec_debug_idt[];
 extern unsigned char kexec_debug_exc_vectors[];
+extern uint16_t kexec_debug_8250_port;
 #endif
 
 /*
index fc1714bad04588abc3468a0e72df26513250162b..b756ebcbd827676c77646b7027d999a9010fd4f3 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/console.h>
 #include <linux/kernel.h>
+#include <linux/kexec.h>
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/screen_info.h>
@@ -144,6 +145,11 @@ static __init void early_serial_hw_init(unsigned divisor)
        static_call(serial_out)(early_serial_base, DLL, divisor & 0xff);
        static_call(serial_out)(early_serial_base, DLH, (divisor >> 8) & 0xff);
        static_call(serial_out)(early_serial_base, LCR, c & ~DLAB);
+
+#if defined(CONFIG_KEXEC_CORE) && defined(CONFIG_X86_64)
+       if (static_call_query(serial_in) == io_serial_in)
+               kexec_debug_8250_port = early_serial_base;
+#endif
 }
 
 #define DEFAULT_BAUD 9600
index 29cb399a34d456fbf47fa144050ec2f740637fdc..21011cd19c48342916f18191cb72e9609dd4b396 100644 (file)
@@ -39,6 +39,7 @@ SYM_DATA(kexec_va_control_page, .quad 0)
 SYM_DATA(kexec_pa_table_page, .quad 0)
 SYM_DATA(kexec_pa_swap_page, .quad 0)
 SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
+SYM_DATA(kexec_debug_8250_port, .word 0)
 
        .balign 16
 SYM_DATA_START_LOCAL(kexec_debug_gdt)
@@ -380,24 +381,50 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
 SYM_CODE_END(swap_pages)
 
 /*
- * Generic 'print character' routine (as yet unimplemented)
+ * Generic 'print character' routine
  *  - %al: Character to be printed (may clobber %rax)
  *  - %rdx: MMIO address or port.
  */
-SYM_CODE_START_LOCAL_NOALIGN(pr_char)
+#define XMTRDY          0x20
+
+#define TXR             0       /*  Transmit register (WRITE) */
+#define LSR             5       /*  Line Status               */
+
+SYM_CODE_START_LOCAL_NOALIGN(pr_char_8250)
        UNWIND_HINT_FUNC
        ANNOTATE_NOENDBR
+       addw    $LSR, %dx
+       xchg    %al, %ah
+.Lxmtrdy_loop:
+       inb     %dx, %al
+       testb   $XMTRDY, %al
+       jnz     .Lready
+       rep nop
+       jmp .Lxmtrdy_loop
+
+.Lready:
+       subw    $LSR, %dx
+       xchg    %al, %ah
+       outb    %al, %dx
+pr_char_null:
+       ANNOTATE_NOENDBR
+
        ANNOTATE_UNRET_SAFE
        ret
-SYM_CODE_END(pr_char)
+SYM_CODE_END(pr_char_8250)
 
 /*
  * Load pr_char function pointer into %rsi and load %rdx with whatever
  * that function wants to see there (typically port/MMIO address).
  */
-.macro pr_setup
-       /* No output; pr_char just returns */
-       leaq    pr_char(%rip), %rsi
+.macro pr_setup
+       leaq    pr_char_8250(%rip), %rsi
+       movw    kexec_debug_8250_port(%rip), %dx
+       testw   %dx, %dx
+       jnz     1f
+
+       leaq    pr_char_null(%rip), %rsi
+1:
 .endm
 
 /* Print the nybble in %bl, clobber %rax */