[LINUX] How to print characters to the console before booting on ARM

How can I print characters to the console before booting?

After linux boots, you can specify a console-connected device by specifying it with an option such as console = ttyS0,115200.

But what if I want to see the log before it starts? How do you display it? Let's sort out that.

The survey base is around here.

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm

0. Introduction

Although it is a useful debugging routine when MM problem or printk does not work, It is for debugging only, and it is supposed to be removed in the product kernel.

You need to be careful when handling it!

arch/arm/kernel/debug.S



/*
 * Some debugging routines (useful if you've got MM problems and
 * printk isn't working).  For DEBUGGING ONLY!!!  Do not leave
 * references to these in a production kernel!
 */

1. Specify earlyprintk at startup.

It seems that you should specify ʻearly printk` as the boot option of linux.

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/admin-guide/kernel-parameters.txt#n1135

What happens when you specify this?

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/kernel/early_printk.c

The address of the character to be displayed is passed to the printascii () function.

2. Print characters with print ascii

Click here for the definition of printascii. In the case of ARM assembler, the argument is in r0. In this case, r0 is a pointer to a string.

-\ 0 → Processing is completed. -Convert to \ n → \ r \ n and output characters --Other than that → Character output

I think.

arch/arm/kernel/debug.S



#ifndef CONFIG_DEBUG_SEMIHOSTING

ENTRY(printascii)
        addruart_current r3, r1, r2 // 2.Explained in 1
1:      teq r0, #0
        ldrbne  r1, [r0], #1
        teqne   r1, #0
        reteq   lr
2:      teq     r1, #'\n'
        bne 3f
        mov r1, #'\r'
        waituart r2, r3 // 4.Explained in 2
        senduart r1, r3 // 4.Explained in 3
        busyuart r2, r3 // 4.Explained in 4
        mov r1, #'\n'
3:      waituart r2, r3
        senduart r1, r3
        busyuart r2, r3
        b   1b
ENDPROC(printascii)

<Omitted>

#else

ENTRY(printascii)
        mov r1, r0
        mov r0, #0x04       @ SYS_WRITE0
    ARM(    svc #0x123456   )
#ifdef CONFIG_CPU_V7M
    THUMB(  bkpt    #0xab       )
#else
    THUMB(  svc #0xab       )
#endif
        ret lr
ENDPROC(printascii)

The behavior changes depending on whether DEBUG_SEMIHOSTING is enabled or not.

If it is invalid, it is trying to output characters by making full use of adduart_current / waituart / senduart. This will be a product-specific implementation (see below).

What if it is valid? 0x04 is set in the r0 register, and the character to be displayed is set in the r1 register, and 0x123456 is SVCed in ARM mode. This is a function that can be used regardless of the model, and can be viewed (probably) via a debugger such as JTAG.

2.1 Check adduart_current

I will explain adduart_current, which is a process common to all models.

arch/arm/kernel/debug.S


#ifdef CONFIG_MMU
        .macro  addruart_current, rx, tmp1, tmp2
        addruart    \tmp1, \tmp2, \rx // 4.Explained in 1
        mrc     p15, 0, \rx, c1, c0
        tst     \rx, #1
        moveq       \rx, \tmp1
        movne       \rx, \tmp2
        .endm

#else /* !CONFIG_MMU */
        .macro  addruart_current, rx, tmp1, tmp2
        addruart    \rx, \tmp1, \tmp2
        .endm

#endif /* CONFIG_MMU */

accessible address of rx [out] tmp1 / tmp2 temp1 [tmp]: Temporary use temp2 [tmp]: Temporary use

If the MMU is enabled, switch PHYS / VIRT according to the state of the MMU, If the MMU is disabled, use PHYS.

In other words, the appropriate address for controlling the UART device is assigned to the first register (r3).

3. Decide which model of assembler to read with printascii

Now let's see where each function called in printascii is defined.

arch/arm/Kconfig.debug



# These options are only for real kernel hackers who want to get their hands dirty.
config DEBUG_LL
	bool "Kernel low-level debugging functions (read help!)"
	depends on DEBUG_KERNEL
	help
	  Say Y here to include definitions of printascii, printch, printhex
	  in the kernel.  This is helpful if you are debugging code that
	  executes before the console is initialized.

	  Note that selecting this option will limit the kernel to a single
	  UART definition, as specified below. Attempting to boot the kernel
	  image on a different platform *will not work*, so this option should
	  not be enabled for kernels that are intended to be portable.

choice
	prompt "Kernel low-level debugging port"
	depends on DEBUG_LL

	config DEBUG_ALPINE_UART0
		bool "Kernel low-level debugging messages via Alpine UART0"
		depends on ARCH_ALPINE
		select DEBUG_UART_8250
		help
		  Say Y here if you want kernel low-level debugging support
		  on Alpine based platforms.

These options are for real kernel hackers who want to get their hands dirty

Select Y to define printascii, printch and printhex in the kernel. This will be useful if you want to debug the code that runs before the console is initialized.

Note that selecting this option limits the kernel to a single UART definition, as shown below. Do not enable this option on portable kernels as it will * not work * if you try to boot the kernel image on another platform.

So, it's really solid, so it's a non-portable fix.

If DEBUG_ALPINE_UART0 etc. is defined in the above settings, DEBUG_UART_8250 etc. will be specified and replaced with the definition of which assembler code to include.

arch/arm/Kconfig.debug


config DEBUG_LL_INCLUDE
    string
    default "debug/sa1100.S" if DEBUG_SA1100
    default "debug/palmchip.S" if DEBUG_UART_8250_PALMCHIP
    default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250
    default "debug/at91.S" if DEBUG_AT91_UART
    default "debug/asm9260.S" if DEBUG_ASM9260_UART
    default "debug/clps711x.S" if DEBUG_CLPS711X_UART1 || DEBUG_CLPS711X_UART2
    default "debug/dc21285.S" if DEBUG_DC21285_PORT
    default "debug/meson.S" if DEBUG_MESON_UARTAO
    default "debug/pl01x.S" if DEBUG_LL_UART_PL01X || DEBUG_UART_PL01X
    default "debug/exynos.S" if DEBUG_EXYNOS_UART
    default "debug/efm32.S" if DEBUG_LL_UART_EFM32
    default "debug/icedcc.S" if DEBUG_ICEDCC

As a result of the above, CONFIG_DEBUG_LL_INCLUDE becomes the string "debug / *. S ", which is #included.

arch/arm/kernel/debug.S



#if !defined(CONFIG_DEBUG_SEMIHOSTING)
#include CONFIG_DEBUG_LL_INCLUDE
#endif

Click here for definitions such as CONFOG_DEBUG_UART_PHTS that will be used in 4.1 soon.

arch/arm/Kconfig.debug


config DEBUG_UART_PHYS
    hex "Physical base address of debug UART"
    default 0x01c20000 if DEBUG_DAVINCI_DMx_UART0
    default 0x01c28000 if DEBUG_SUNXI_UART0
    default 0x01c28400 if DEBUG_SUNXI_UART1
    default 0x01d0c000 if DEBUG_DAVINCI_DA8XX_UART1
    default 0x01d0d000 if DEBUG_DAVINCI_DA8XX_UART2
    default 0x01f02800 if DEBUG_SUNXI_R_UART
    default 0x02530c00 if DEBUG_KEYSTONE_UART0
    default 0x02531000 if DEBUG_KEYSTONE_UART1
    default 0x03010fe0 if ARCH_RPC
    :

config DEBUG_UART_VIRT
    hex "Virtual base address of debug UART"
    default 0xc881f000 if DEBUG_RV1108_UART2
    default 0xc8821000 if DEBUG_RV1108_UART1
    default 0xc8912000 if DEBUG_RV1108_UART0
    default 0xe0010fe0 if ARCH_RPC
    default 0xf0000be0 if ARCH_EBSA110
    default 0xf0010000 if DEBUG_ASM9260_UART
    default 0xf0100000 if DEBUG_DIGICOLOR_UA0
    default 0xf01fb000 if DEBUG_NOMADIK_UART
    default 0xf0201000 if DEBUG_BCM2835 || DEBUG_BCM2836
    default 0xf1000300 if DEBUG_BCM_5301X
    default 0xf1000400 if DEBUG_BCM_HR2
    default 0xf1002000 if DEBUG_MT8127_UART0
    default 0xf1006000 if DEBUG_MT6589_UART0
    default 0xf1009000 if DEBUG_MT8135_UART3
    default 0xf1023000 if DEBUG_BCM_IPROC_UART3
    default 0xf11f1000 if DEBUG_VERSATILE
    default 0xf1600000 if DEBUG_INTEGRATOR
    :
    :

4. Assembler actually used in printascii

The actual assembler code is implemented under include / debug respectively. Here, using the bcm code, which seems to have the shortest source code, Easily check the definition of the provided function. 4.1 adduart, rp,rv,tmp

/arch/arm/include/debug/bcm63xx.S


	.macro	addruart, rp, rv, tmp
	ldr	\rp, =CONFIG_DEBUG_UART_PHYS
	ldr	\rv, =CONFIG_DEBUG_UART_VIRT
	.endm

rp [out] : CONFIG_DEBUG_UART_PHYS rv [out] : CONFIG_DEBUG_UART_VIRT tmp [out]: Data passed to waituart / busyuart

This function is also indirectly called from the mmu control. Maybe this won't be used if the iomap table is defined ...

arch/arm/mm/mmu.c



    /*
     * Ask the machine support to map in the statically mapped devices.
     */
    if (mdesc->map_io)
        mdesc->map_io();
    else
        debug_ll_io_init();

:
:


#ifdef CONFIG_DEBUG_LL
void __init debug_ll_io_init(void)
{
    struct map_desc map;

    debug_ll_addr(&map.pfn, &map.virtual);
    if (!map.pfn || !map.virtual)
        return;
    map.pfn = __phys_to_pfn(map.pfn);
    map.virtual &= PAGE_MASK;
    map.length = PAGE_SIZE;
    map.type = MT_DEVICE;
    iotable_init(&map, 1);
}
#endif

arch/arm/kernel/debug.S



#ifdef CONFIG_MMU
ENTRY(debug_ll_addr)
        addruart r2, r3, ip
        str r2, [r0]
        str r3, [r1]
        ret lr
ENDPROC(debug_ll_addr)
#endif

4.2 waituart, rd,rx

/arch/arm/include/debug/bcm63xx.S



	.macro	waituart, rd, rx
1001:	ldr	\rd, [\rx, #UART_IR_REG]
	tst	\rd, #(1 << UART_IR_TXEMPTY)
	beq	1001b
	.endm

rd [tmp]: Temporary use rx [in]: UART device port

Read the memory in OFFSET of UART_IR_REG on the UART device port and loop until it matches # (1 << UART_IR_TXEMPTY).

4.3 senduart, rd,rx

/arch/arm/include/debug/bcm63xx.S



	.macro	senduart, rd, rx
	/* word access do not work */
	strb	\rd, [\rx, #UART_FIFO_REG]
	.endm

rd [in]: String to export rx [in]: UART device port

4.4 busyuart, rd,rx

/arch/arm/include/debug/bcm63xx.S



	.macro	busyuart, rd, rx
1002:	ldr	\rd, [\rx, #UART_IR_REG]
	tst	\rd, #(1 << UART_IR_TXTRESH)
	beq	1002b
	.endm

rd [tmp]: Temporary use rx [in]: UART device port

Read the memory in OFFSET of UART_IR_REG on the UART device port and loop until it matches # (1 << UART_IR_TXTRESH).

Summary

--Added earlyprintk to boot parameter at boot time --Enable DEBUG_LL in KERNEL CONFIG --Added debugging settings to arch / arm / Kconfig.debug. --Added assembler corresponding to arch / arm / Kconfig.debug --Added the corresponding assembler under arch / arm / include --waituart: Waiting for export --senduart: Actually export as FIFO --busyuart: Waiting for export completion

that's all.

Recommended Posts

How to print characters to the console before booting on ARM
How to print debug messages to the Django console
How to overwrite the output to the console
How to erase the characters output by Python
How to put Takoyaki Oishikunaru on the segment tree
How to enjoy Python on Android !! Programming on the go !!
How to use the generator
How to print characters as a table with Python's print function
How to deploy the easiest python textbook pybot on Heroku
Notes on how to use marshmallow in the schema library
How to use the decorator
How to increase the axis
How to start the program
A note on how to check the connection to the license server port
How easy is it to synthesize a drug on the market?
How to use Jupyter on the front end of supercomputer ITO
A command to easily check the speed of the network on the console
How to update the python version of Cloud Shell on GCP
How to calculate the autocorrelation coefficient
How to use the zip function
How to use Dataiku on Windows
Notes on how to use pywinauto
How to install VMware-Tools on Linux
How to install pycrypto on Windows
How to read the SNLI dataset
How to deploy django-compressor on Windows
How to get the Python version
Notes on how to use featuretools
How to install OpenCV on Mac
How to install PyPy on CentOS
[Python] How to import the library
Misunderstanding on how to connect cnn
How to install TensorFlow on CentOS 7
Notes on how to use doctest
How to install Maven on CentOS
How to use the ConfigParser module
Notes on how to write requirements.txt
How to install Go on Ubuntu
How to install music 21 on windows
How to easily switch the virtual environment created by Conda on Jupyter
[C language] How to use the crypt function on Linux [Password hashing]
TLE seemed to be scary depending on how the input was received
How to make only one data register on the Django admin screen
How to make Word Cloud characters monochromatic
How to display the progress bar (tqdm)
I want to output to the console coolly
How to install aws-session-manager-plugin on Manajro Linux
How to read pydoc on python interpreter
How to install drobertadams / toggl-cli on Mac
How to check the version of Django
[Kivy] How to install Kivy on Windows [Python]
How to use mecab, neologd-ipadic on colab
How to solve the bin packing problem
How to set the server time to Japanese time
How to build Hello, World on #Nix
How to manually update the AMP cache
[Linux] How to use the echo command
How to use the Linux grep command
How to update php on Amazon linux 2
How to use Google Assistant on Windows 10
How to erase Python 2.x on Mac.