Usage of subroutine printascii in Assemble
========================================================================
Related files:
--------------------
[1]linux/arch/arm/kernel/debug.S
[2]linux/include/asm-arm/arch-xxxxx/debug-macro.S
[3]asm/hardware/debug-pl01x.S
[4]linux/arch/arm/kernel/head.S
[5]linux/arch/arm/mach-xxxxx/xxxxx.c
NOTE: "xxxxx" denotes name of certain platform
------------------------------------------------------------
Steps:
[1] In this file, it implements printascii function as follows:
ENTRY(printascii)
addruart r3
b 2f
1: waituart r2, r3
senduart r1, r3
busyuart r2, r3
teq r1, #'\n'
moveq r1, #'\r'
beq 1b
2: teq r0, #0
ldrneb r1, [r0], #1
teqne r1, #0
bne 1b
mov pc, lr
From the implementation of printascii function, we find out that four platform-specific functions are needed to be defined, which are "addruart", "waituart", "seduart", "busyuart".
[2][3] The four functions mentioned are carried out here,let's see the details.
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0x10000000 -----------------------------------> (1) the base hardware address of uart, which are preferred to output message.
assign it according to configuration of your board, the value of which is identical with
".phys_io" of MACHINE_START, the detail will be covered below [4].
movne \rx, #0xf1000000 @ virtual base---------------------> (2) the virtual base address for the uart, which is used after turning on the MMU
assign it according to configuration of your board, the value of which is identical with
".io_pg_offst" of MACHINE_START
orr \rx, \rx, #0x001F0000
orr \rx, \rx, #0x00001000
.endm
#include
.macro senduart,rd,rx
strb \rd, [\rx, #UART01x_DR]------------------------------------> #UART01x_DR is uart send register offset from base address
.endm
.macro waituart,rd,rx
1001: ldr \rd, [\rx, #UART01x_FR]------------------------------------> #UART01x_FR is uart status register offset from base address
tst \rd, #UART01x_FR_TXFF
bne 1001b
.endm
.macro busyuart,rd,rx
1001: ldr \rd, [\rx, #UART01x_FR]
tst \rd, #UART01x_FR_BUSY
bne 1001b
.endm
[4] Implement the maping relationship of virtual and hareware address of assigned uart, the detailed procedures will be descriped in the future.
#ifdef CONFIG_DEBUG_LL
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
ldr r3, [r8, #MACHINFO_PGOFFIO]
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
ldr r3, [r8, #MACHINFO_PHYSIO]
orr r3, r3, r7
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
[5] Assign virtual and hardware address for use in [4]
MACHINE_START(VERSATILE_AB, "ARM-Versatile AB")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.phys_io = 0x101f1000, -------------------------------------------------------------------------------->the same with (1) in [2][3]
.io_pg_offst = ((0xf11f1000) >> 18) & 0xfffc---------------------------------------------------------------->the same with (2) in [2][3]
.boot_params = 0x00000100,
.map_io = versatile_map_io,
.init_irq = versatile_init_irq,
.timer = &versatile_timer,
.init_machine = versatile_init,
MACHINE_END
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Call Style:
1. assemble language
emulate __error_p function implemented as following in "linux/arch/arm/kernel/head-common.S"
place "bl __error_p" where you want to inspect.
.type __error_p, %function
__error_p:
#ifdef CONFIG_DEBUG_LL
adr r0, str_p1
bl printascii
b __error
str_p1: .asciz "\nError: unrecognized/unsupported processor variant.\n"
.align
#endif
2. C language
method[1](recommend)
/* debug
*
* we send the debug to printascii() to allow it to be seen if the
* system never wakes up from the sleep
*/
extern void printascii(const char *);
void pm_dbg(const char *fmt, ...)
{
va_list va;
char buff[256];
va_start(va, fmt);
vsprintf(buff, fmt, va);
va_end(va);
printascii(buff);
}
#define DBG(fmt...) pm_dbg(fmt)
#else
#define DBG(fmt...) printk(KERN_DEBUG fmt)
#endif
eg:
DBG("Bug is here %s, %dn, __FUNCTION__, __LINE__);
or in other files,
extern void pm_dbg(const char *fmt, ...)
pm_dbg("Bug is here %s, %dn, __FUNCTION__, __LINE__);
method[2]
printascill function is nested in printk(kernel/printk.c), then use printk to trace.
extern void printascii(const char*);//kernel/printk.c 516行
printascii(printk_buf);////kernel/printk.c 539行
For more detailed information of nesting printascii in printk() function, pls refer to "linux内核的移植与遭遇问题的解决"
NOTE:
Make sure that the suspend/resume method of Serial and console are marked, and the clock of serial is not gated off, or else you must reinit serial with its clock enabled.