Linux Kernel Debugging com JTAG

- por Sergio Prado

Categorias: Beagleboard-xM, Linux Tags: , , , ,

Por um bom tempo, durante o desenvolvimento do kernel Linux, os desenvolvedores sentiram falta de uma boa e menos intrusiva interface de debugging. Foi só na versão 2.6.26 que o kgdb foi aprovado e entrou no mainline.

A ausência de um mecanismo de debbuging é ainda mais sentida quando pensamos no desenvolvimento embedded, principalmente no trabalho de desenvolvimento de porte do bootloader ou do kernel para outras plataformas. Para quem já desenvolveu este tipo de trabalho, normalmente chamado de “board bring up”, sabe que é onde mora a diversão! :)

E é aí que entra a interface JTAG…

JTAG

A interface JTAG (Joint Test Action Group) foi inicialmente desenvolvida como um mecanismo para testar placas após sua fabricação. Com o tempo, ela se tornou uma ferramenta importante para debugar sistemas embarcados. Com uma conexão JTAG, temos acesso à modulos de debug integrados ao core da CPU, onde podemos colocá-la em halt, inspecionar registradores, memória, colocar breakpoints, etc.

Mas para fazer nossa máquina de desenvolvimento conversar com uma interface JTAG precisamos de alguns componentes adicionais, incluindo:

  1. Hardware com suporte à JTAG.
  2. Adaptador JTAG.
  3. Debugger.
  4. Software de interface entre o adaptador JTAG e o debugger.

Que tal estudarmos estes componentes em mais detalhes?

HARDWARE

Nos meus testes utilizarei a Beagleboard-xM, que provê uma interface JTAG através de um conector de 14 pinos no padrão da Texas Instruments. Por isso, antes de conectar seu adaptador JTAG, tome cuidado com a pinagem do conector. Mais detalhes aqui sobre a pinagem do conector JTAG da Beagleboard-xM aqui.

ADAPTADOR JTAG

O adaptador JTAG é um conversor de sinais JTAG para interfaces de comunicação comuns em PC, como USB, RS232 ou até mesmo porta paralela, sendo que a interface USB é a mais comum.

Existem diversos adaptadores JTAG (tem uma lista bem completa aqui). Nos meus testes utilizarei o Flyswatter da Tin Can Tool.

Antes de comprar o adaptador JTAG, verifique se ele tem os drivers para o seu sistema operacional de preferência, e se é suportado pela sua ferramenta de debugging. Se você for comprar o Flyswatter, aproveite e compre também o cabo adaptador para a Beagleboard-xM.

OPENOCD

O OpenOCD (Open on-chip debugger) é uma ferramenta que provê as funcionalidades de debugging e programação de memórias flash em dispositivos embarcados. Ela fala diretamente com adaptadores JTAG, e suporta diversas arquiteturas como ARM7, ARM9, ARM Cortex-A8, ARM Cortex-A9, ARM Cortex-M, XScale, MIPS, etc. Uma lista completa dos adaptadores suportados encontra-se aqui.

O OpenOCD também é um middleware entre o debugger (GDB no nosso caso) e o adaptador JTAG (Flyswatter no nosso caso). Para isso, ele basicamente implementa um gdbserver para receber e converter os comandos do GDB e enviar estes comandos para o adaptador JTAG.

Uma documentação completa do OpenOCD pode ser acessada aqui.

GBD

O GDB (GNU Debugger) é o debugger padrão para sistemas Linux. Normalmente, o GDB é usado de forma standalone, mas ele também pode trabalhar numa arquitetura cliente/servidor. Neste caso, temos o GDB client (frontend), rodando na máquina de desenvolvimento, e o GDB server (backend), rodando na máquina-alvo (hardware que se deseja debugar).

O GDB server controla a aplicação, e o GDB client provê a interface, se comunicando com o servidor via TCP/IP ou serial. Esta é a configuração que você usaria para debugar normalmente uma aplicação rodando em um dispositivo com Linux embarcado.

No caso de debugging com JTAG, a diferença aqui é que o OpenOCD e o adaptador JTAG simulam o gdbserver.

Que tal agora um pouco de ação?

INSTALANDO AS FERRAMENTAS

Todos os testes foram realizados em uma máquina rodando o Ubuntu 12.04. Talvez você precise adaptar os procedimentos abaixo se estiver utilizando uma outra distribuição Linux.

Primeiro instale as dependências das ferramentas que iremos utilizar:

$ sudo apt-get install build-essential gcc gdb libusb-1.0.0 libconfuse0 libtool autoconf texinfo libusb-dev

O nosso adaptador JTAG usa um chip da FTDI para fazer a conversão USB->JTAG, e para que o OpenOCD consiga conversar com ele, precisamos instalar a biblioteca libftdi. Para isso, execute os comandos abaixo:

wget http://www.intra2net.com/en/developer/libftdi/download/libftdi-0.20.tar.gz
tar zxfv libftdi-0.20.tar.gz
cd libftdi-0.20/
./configure --libdir=/usr/local/lib64
make
sudo make install

Remova o parâmetro libdir no comando de configure se sua máquina for de 32 bits.

Agora vamos instalar o OpenOCD:

wget http://downloads.sourceforge.net/project/openocd/openocd/0.6.0/openocd-0.6.0.tar.bz2
tar jxfv openocd-0.6.0.tar.bz2
cd openocd-0.6.0/
./configure --libdir=/usr/local/lib64 --enable-ft2232_libftdi
make
sudo make install
mkdir ~/.openocd
cp -av tcl/* ~/.openocd/

Remova também o parâmetro libdir no comando de configure se sua máquina for de 32 bits.

TESTANDO

Agora vamos fazer tudo isso funcionar. Você vai precisar da sua Beagleboard-xM com o bootloader e o kernel gravados no cartão. E vai precisar também de um toolchain e do kernel da Beagleboard-xM compilado em um diretório da sua máquina.

Conecte a Beagleboard-xM ao adaptador JTAG, à console e à alimentação. A minha ficou assim:

Primeiro alimente a Beagleboard-xM e interrompa o boot no U-Boot.

Agora abra um novo terminal e inicie o OpenOCD:

sudo openocd -f interface/flyswatter.cfg -f board/ti_beagleboard_xm.cfg

Assim que você iniciar o OpenOCD, ele vai esperar por conexões de clientes, que podem ser realizadas de diversas formas, incluindo telnet e GDB client.

Para testar a conexão com o OpenOCD, abra um novo terminal e se conecte por telnet:

$ telnet localhost 4444

Você deverá receber a interface de linha de comando do OpenOCD. Digite “help” para ver a quantidade de comandos disponíveis. Você pode por exemplo listar todos os registradores da CPU com o comando “reg“:

> reg
===== ARM registers
(0) r0 (/32): 0x00000002 (dirty)
(1) r1 (/32): 0x00000002
(2) r2 (/32): 0x48004D20
(3) r3 (/32): 0x00B71B00
(4) r4 (/32): 0x00000205
(5) r5 (/32): 0x4020162C
(6) r6 (/32): 0x402015C8
(7) r7 (/32): 0x00000010
(8) r8 (/32): 0x00000003
(9) r9 (/32): 0x00000001
(10) r10 (/32): 0x402015B8
(11) r11 (/32): 0x00000000
(12) r12 (/32): 0x0001D622
(13) sp_usr (/32)
(14) lr_usr (/32)
(15) pc (/32): 0x402009B8
...

Mais exemplos sobre os comandos do OpenOCD estão disponíveis aqui.

Encerre a seção de Telnet, e de dentro do diretório do kernel, inicie o gdb client:

$ arm-linux-gdb vmlinux

E insira um breakpoint no ponto de entrada do kernel:

(gdb) hbreak *0x80008000
Hardware assisted breakpoint 1 at 0x80008000: file arch/arm/kernel/head.S, line 94.

Se você tiver problemas com o comando acima, pode ser que sua imagem vmlinux não esteja com o endereço do ponto de entrada do kernel (load address) correto. Para corrigir isso, abra o linker script em “arch/arm/kernel/vmlinux.lds“, altere o load address para 0x80008000, e recompile o kernel. Você pode conferir se o load address esta correto com o comando abaixo:

$ readelf -S vmlinux | grep head.text
[ 1] .head.text PROGBITS 80008000 008000 00019c 00 AX 0 0 4

Agora volte ao U-Boot e carregue o kernel:

OMAP3 beagleboard.org # boot

Automaticamente, o gdb irá parar no breakpoint configurado, que é a primeira instrução executada pelo kernel:

Breakpoint 1, stext () at arch/arm/kernel/head.S:94
94              setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode

Agora é só debugar o kernel! Divirta-se!

(gdb) step
96		mrc	p15, 0, r9, c0, c0		@ get processor id
(gdb) list
91	 THUMB(	.thumb			)	@ switch to Thumb now.
92	 THUMB(1:			)
93	
94		setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
95							@ and irqs disabled
96		mrc	p15, 0, r9, c0, c0		@ get processor id
97		bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
98		movs	r10, r5				@ invalid processor (r5=0)?
99	 THUMB( it	eq )		@ force fixup-able long branch encoding
100		beq	__error_p			@ yes, error 'p'

Happy hacking!

Sergio Prado

  • Josenivaldo Benito Junior

    Mais um post super didático desmistificando um assunto muito requisitado!

    Valeu Sergio! G+ 

  • Fernando zatt

    Sou estudante e atualmente trabalho com linux embarcado. Gostaria de uma indicação de algum livro que exemplifique o processo de colocação de um linux em alguma placa. Grato.

  • Jorge Ventura

    Sergio,
    Estive me aprofundando no assunto e gostaria de salientar que seu artigo embora bem intencionado eh muito incompleto. Existe um problema extremamente complexo que voce omitiu que eh o fato de inicialmente a MMU estar desabled e neste caso o enderecamento da tabela de simbolos nao coincide com o enderecamento fisico.

    Tentei alterar o endereco conforme sua orientacao mas isto tambem nao funciona, quando o kernel eh recompilado, uma nova copia daquele arquivo se restabelece com o antigo endereco. Posteriormente percebi que isto seria obvio por que aquele arquivo nao eh o fonte. Acho que vc nunca fez isto antes.

    Infelizmente seu artigo faz parecer que oproblema esta resolvido mas na verdade esta longe disto. Procure ter informacoes mais precisas quando escrever este tipo de artigo e descreva sempre coisas que voce tem absoluta certesa de que estao corretas.

    Voce nao precisa publicar o que escrevo como nao fez da primeira vez mas pelo menos aceite as minhas recomendacoes ja estara numa boa direcao.

    Sinceramente,
    Jorge Ventura
     

    • Olá Jorge,

      Obrigado pelo feedback. 

      Sim, o linker script é gerado durante uma compilação limpa. É por isso que ele é sobreescrito se você recompilar (do zero) o kernel.

      Concordo plenamente que o assunto é extremamente complexo, e minha abordagem foi bem simplista. Neste caso em especifico, a MMU realmente dificulta bastante o trabalho com debugging via JTAG.

      Mas meu objetivo no blog não é desenvolver um how-to completo sobre o assunto, mas sim desmistificar alguns temas e instigar a curiosidade em quem me acompanha para aprender mais sobre o assunto.

      Sobre os comentários, por favor me indique em qual post seu comentário não foi publicado. Eu não modero os comentários aqui no blog, a não ser que tenham algum link (estes são moderados automaticamente pelo Disqus para evitar spam). 

      Peço então a gentiliza de que me envie novamente este comentário que ele será devidamente publicado.

      Obrigado e um abraço.

      • Jorge Ventura

         Obrigado pela pronta resposta. Meu primeiro post esta bastante desatualizado mas vou te dar uma posicao do problema, talves vc tenha algo para acrescentar e me ajudar.

        Eu ja consigo fazer o trace antes da MMU ser ativada; o truque eh criar uma outra imagem atraves do objcopy relocando o ponto de entrada e as sessoes da imagem. uma vez que o trace para na rotina __mmu_on, troco a imagem pela original usando symbol-add-file dentro do gdb e coloco outro break em __mmap_switched, a partir deste ponto consigo continuar trabalhando por que o arquivo de simbolos contem o endereco virtual que sao os enderecos do vmlinux. Basicamente uso uma imagem alterada para o enderecamento real e outra para quando o sistema esta operando com enderecamento virtual. Isto esta funcionando com algums poucos problemas mas existe um que nao consegui ainda resolver.

        Quando uso objcopy para relocar os segmentos, esta relocacao nao atinge a “dwarf line table”, as sessoes da imagem que associam as linhas do fonte com os enderecos na memoria ou seja; antes da MMU estar habilitada eu so consigo trabalhar em assembly mas nao tenho ligacao com o fonte. Eu ate vejo os symbolos quando faco o “disassembly” o que ja eh de grande ajuda. So depois, quando a MMU eh habilitada e eu troco a imagem e entao volto a ter a associacao com o fonte pq a partir desta ponto todo enderecamento coincide com a “dwarf line table”.

        Eu acho que a unica saida neste caso eh insistir com a sua ideia de regerar uma imagem no endereco 0x80008000 e manter a outra original no 0xc0008000, isto dispensaria o procedimento de objcopy. Se eu tiver as duas imagens, com o procedimento que mencionei acima eu consigo fazer o debug completo na situacao pre-MMU e pos-MMU. Neste momento ainda nao sei como fazer isto e voltei para reler seu artigo quando encontrei meu ultimo post publicado.

        Sinceramente,
        Jorge Ventura

        • Jorge Ventura

          Sergio,
          Um último update a  respeito deste assunto. Estou utilizando o openocd 0.6.1 e até o momento não existe suporte para software breakpoint em thumb-2, que é excencial para ter o debug funcionando 100%. A única forma que na minha opinião é bastante limitada é utilizar hardware breakpoint o tempo todo. Quando voce utiliza sb e as instruções são thumb a coisa vai mas quando uma instrução thumb-2 demanda um gdb breakpoint de 32 bits (thumb-2) o gdb não consegue instalar o breakpoint.

          Abaixo está um post que coloquei na lista do openocd para ver se alguém se habilita a implementar. Eu venho tentando mas não creio que consiga pois demanda um conhecimento que eu não tenho.

          Sinceramente,
          Ventura

          //==============================================
           I have been trying to figure out how to give support for software breakpoints for arm cortex-8 in thumb state.
          The hardware I am using for tests is TI OMAP3 beaglebone.
          I am not the best person to do that but anyway while no other volunteer decide to help I will keep trying.

          What do I know about the problem.

          1. openocd knows that the board is running in thumb state.

          Debug: 1481 22479 armv4_5.c:395 arm_set_cpsr(): set CPSR 0x400001b3: Supervisor mode, Thumb state

          2. While gdb find thumb instructions, the breakpoints succeed.

          3. gdb find a thum-2 instruction and requires a 32 bits software breakpoint.

          Debug: 1629 24548 gdb_server.c:2050 gdb_input_inner(): received packet: ‘Z0,80008100,3’
          Debug: 1630 24548 gdb_server.c:1394 gdb_breakpoint_watchpoint_packet(): -Debug: 1631 24548 gdb_server.c:1413 gdb_breakpoint_watchpoint_packet(): =======> gdb watch/breakpoint type(0)

          Debug: 1632 24548 cortex_a.c:1986 cortex_a8_read_memory(): Reading memory at address 0x80008100; size 3; count 1Debug: 1633 24548 cortex_a.c:1939 cortex_a8_read_phys_memory(): Reading memory at real address 0x80008100; size 3; count 1

          Error: 1634 24548 cortex_a.c:1408 cortex_a8_set_breakpoint(): Error @ cortex_a8 read_memory:-601Error: 1635 24548 breakpoints.c:96 breakpoint_add_internal(): can’t add breakpoint: unknown reasonDebug: 1636 24548 gdb_server.c:1163 gdb_error(): Reporting -601 to GDB as generic error

          And here is the key point: gdb is trying to install a break point
          type 3 (32-bit Thumb-2 mode breakpoint) that at this time is not
          supported by openocd.My first simple idea was consider type 3
          breakpoint as the same like type 4 so that openocd can handle memory in
          the same way but unfortunately it doesn’t work and I don’t know why. Any
          suggestion or explanation is welcome.

          • Obrigado pelo feedback Jorge. Não sabia da ausência de suporte à software breakpoint em thumb-2 no openocd. Vou fazer mais alguns testes aqui e te aviso.

            Um abraço.

          • Jorge Ventura

             Olá Sergio,
            Tenho boas notícias. Consegui implementar a correção para single-step usando thumb-2 e está funcionando bem, já consigo fazer exatamente o que queria desde o início, trace do kernel passo a passo desde o início.

            Estou fazendo ainda muitos testes e pretendo submeter o patch para o pessoal do openocd esta semana. A única restrição é que só implementei para o Cortex-A8, vou precisar ver se a mesma estratégia se aplica para as outras plataformas ARM.

            Não sei se você conhece o problema da instrução de assembly “it”, esta instrução causou vários problemas para a sua implementação no gdb e levou quase 6 meses para ela ser finalmente implementada pelo desenvolvedor Daniel Jacobowitz. Logo nas primeiras linhas do kernel existe esta instrução e consegui executar passo a passo sem problemas. Acho que a correção que fiz está correta.

            Um abraço.

          • Legal Jorge,

            Pode me enviar o patch para que eu possa dar uma olhada?

  • allyson10r

    Bom dia Sergio,

    Comprei o JTAG Debugger Flywatter2, mas estou com uma duvida.
    – Minha placa não está na lista de boards do openocd, ainda assim existe a possibilidade de debugar usando o openocd?
    OBS: o processador é suportado, no meu caso um AM3517

    • Olá Allyson, como vai?
      Qual versão do openocd você esta usando? Porque as últimas versões já tem suporte ao Flywatter2. Sugiro você baixar a última versão e compilar. Você deve encontrar o arquivo de configuração do Flywatter2 em “tcl/interface/flyswatter2.cfg”.

      Um abraço.

      • allyson10r

        Bom dia,
        Por aqui está tudo ótimo! E com vc?
        Eu concegui emprestado com um amigo uma beagleboard e baixei o openocd 0.6.0 (exatamente como no seu post)
        Ainda assim não funciona.
        Veja as msg que aparecem aqui:
        —————————————————————————————–
        ~/openocd-0.6.0$ sudo openocd -f interface/flyswatter2.cfg -f board/ti_beagleboard.cfg

        Open On-Chip Debugger 0.6.0 (2012-10-23-18:06)
        Licensed under GNU GPL v2
        For bug reports, read
            http://openocd.sourceforge.net/doc/doxygen/bugs.html
        Info : only one transport option; autoselect ‘jtag’
        RCLK – adaptive
        Warn : omap3530.dsp: huge IR length 38
        RCLK – adaptive
        trst_only separate trst_push_pull
        Info : max TCK change to: 30000 kHz
        Info : RCLK (adaptive clock speed)
        Error: couldn’t read enough bytes from FT2232 device (0 < 81)
        Error: couldn't read from FT2232
        in procedure 'runtest'
        Error: JTAG scan chain interrogation failed: all ones
        Error: Check JTAG interface, timings, target power, etc.
        Error: Trying to use configured scan chain anyway…
        Error: couldn't read enough bytes from FT2232 device (0 < 2)
        Error: couldn't read from FT2232
        Warn : Bypassing JTAG setup events due to errors

        ————————————————————————————————————
        Obrigado pela ajuda!!!!

  • Olá Marcos,

    Nunca tinha ouvido esta expressão, mas pelos exemplos que me mandou, são a mesma coisa.

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.