Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to debug using printf an assembly code of linux kernel

This is the file that I am trying to debug by putting printf/printk statements.

The code is assembly.

 79         __HEAD
 80 ENTRY(stext)
 81  ARM_BE8(setend be )                    @ ensure we are in BE8 mode
 82 
 83  THUMB( adr     r9, BSYM(1f)    )       @ Kernel is always entered in ARM.
 84  THUMB( bx      r9              )       @ If this is a Thumb-2 kernel,
 85  THUMB( .thumb                  )       @ switch to Thumb now.
 86  THUMB(1:                       )
 87 
 88 #ifdef CONFIG_ARM_VIRT_EXT
 89         bl      __hyp_stub_install
 90 #endif
 91         @ ensure svc mode and all interrupts masked
 92         safe_svcmode_maskall r9
 93 
 94         mrc     p15, 0, r9, c0, c0              @ get processor id
 95         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid
 96         movs    r10, r5                         @ invalid processor (r5=0)?
 97  THUMB( it      eq )            @ force fixup-able long branch encoding
 98         beq     __error_p                       @ yes, error 'p'
 99 
100 #ifdef CONFIG_ARM_LPAE
101         mrc     p15, 0, r3, c0, c1, 4           @ read ID_MMFR0
102         and     r3, r3, #0xf                    @ extract VMSA support
103         cmp     r3, #5                          @ long-descriptor translation table format?
104  THUMB( it      lo )                            @ force fixup-able long branch encoding
105         blo     __error_lpae                    @ only classic page table format
106 #endif
107 
108 #ifndef CONFIG_XIP_KERNEL
109         adr     r3,

I simply want to put some messages to console from this file. This code is for ARM.

I tried using printk in such code blocks but it fails to compile.

Any suggestions?

like image 366
Uzair Avatar asked Dec 12 '25 19:12

Uzair


1 Answers

The answer is given in ARM booting FAQ. You need to enable the config menu item Kernel Hacking|Kernel low-level debugging functions. For instance, the code __error_p will then display something on your console UART. For instance, elinux.org's Debugging by printing shows an error message when the kernel can not match your machine id. See: ARM booting documentation

DEBUG_LL creates the function extern void printascii(char *); which you can also instrument vprintk_emit() with.

--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1483,6 +1483,8 @@ static size_t cont_print_text(char *text, size_t size)
        return textlen;
 }

+extern void printascii(char*);
+
 asmlinkage int vprintk_emit(int facility, int level,
                            const char *dict, size_t dictlen,
                            const char *fmt, va_list args)
@@ -1541,6 +1543,7 @@ asmlinkage int vprintk_emit(int facility, int level,
         * prefix which might be passed-in as a parameter.
         */
        text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+       printascii(text);

        /* mark and strip a trailing newline */
        if (text_len && text[text_len-1] == '\n') {

This and the kernel command line initcall_debug will be useful to diagnose boot issue.

If you platform doesn't support the DEBUG_LL, it is fairly easy to implement the polled UART routines needed to support it.

Normally, printk() is buffered during early boot and doesn't actually print out until the console driver is active. So, without patching the vprintk_emit(), you can crash/halt anywhere before the UART driver runs and see nothing with a normal configuration.

For debugging of head.S and the like, you need to do this in assembler. The error_p implementation has some sample code and here is another,

 #ifdef CONFIG_DEBUG_LL
         /* Save some registers? */
         adr     r0, prefix
         bl      printascii
         mov     r0, r9
         bl      printhex8
         adr     r0, tail
         bl      printascii
         b       1f
 prefix: .asciz  "\nTrace 1 gives "
 tail:   .asciz  "\n"
 1:  /* Perhaps halt here, due to register clobbers */
 #endif

You are limited in the use of registers depending on the context in head.S. Often the only issue with this code is you didn't do something described in the ARM booting documentation.

You may also use the macros directly which are defined in arch/arm/include/debug. The macros are,

  • addruart - get the addresses of the uarts to parameter 1 (phys), 2 (virt), (3rd is a tmp for macro).
  • senduart - write character param 1 to address (virtual or physical).
  • waituart - parm 1 (tmp), param 2 address (virt or phys). Ready?
  • busyuart - parm 1 (tmp), param 2 address (virt or phys). Empty?

You can see the API in use in the implementation of printasii().

ENTRY(printascii)
        addruart_current r3, r1, r2   @ phys address to r3
        b   2f
1:      waituart r2, r3   @ ready (r2 is tmp, r3 is phys address)
        senduart r1, r3   @ transmit r1 character
        busyuart r2, r3   @ wait for character to finish tx.
        teq r1, #'\n'
        moveq   r1, #'\r' @ insert carriage return if new line.
        beq 1b
2:      teq r0, #0    @ NULL pointer?
        ldrneb  r1, [r0], #1  @ next char
        teqne   r1, #0        @ end of string?
        bne     1b            @ send another.
        mov pc, lr
ENDPROC(printascii)

In the assembler of head.S and others, you can use the macros with various register arguments to avoid clobbers of in-use registers such as r0-r3. They are usually available for use and printascii() can be called directly.

like image 151
artless noise Avatar answered Dec 14 '25 10:12

artless noise



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!