Table of Contents

Sistemas Embarcados

prof. Jorge Kinoshita quinta 14:00H - 17:40H

1 <2019-09-05 Qui> Objetivo

1.1 qemu

  • dificuldade em criar sistemas embarcados: como disparar sw e hw juntos?
  • custo do hw
  • o conhecimento de hw se desgasta muito mais rapido do que o sw.
  • como criar um aparelho com um sensor novo via hw?
-- criar o sensor
-- conectar o sensor em algum barramento: spi, i2c.
-- criar um driver, tratar da interrupcao.
  • como emular o aparelho com o sensor novo?
-- qemu com uma placa bem testada como a versatilepb
-- emular o dispositivo na gpio
-- criar o driver para o linux tratando das interrupcoes.
  • objetivo: cada equipe deverah criar um dispositivo simples (ex: um botao) ligando-se aa placa versatilepb

1.2 projetos

  • continuar os projetos na disciplina de sistemas embarcados.

1.3 qemu, desafios

  • pouca documentacao.
  • como o qemu funciona?
  • como um timer e um processador podem estar rodando ao mesmo tempo?
  • como algum periferico eh emulado como uma placa de rede, ou o teclado?
  • quando a placa versatile eh emulada, como o linux pode estar vendo um teclado na placa, ou como pode estar se conectando via tty?
  • como o qemu pode receber dispositivos do host?
  • eh possivel gerar um periferico como um processo que se comunica com o qemu via um socket?
  • como o qemu pode emular um periferico num barramento como o spi ou o i2c?
  • como podemos fazer o perferico gerar uma interrupcao? (ou como conectar o periferico na contralora de interrupcoes?).

pretendo coletar as respostas no blog ARM Linux Lab.

1.4 Como achar as respostas?

  • achar o maximo de documentacao sobre "qemu internals".
  • compilar e rodar o qemu na maquina.
  • monitorar como o qemu colocando mensagens
  • estudar como o qemu roda o processador e perifericos (ex: timer) juntos.
  • estudar e rodar algum projeto no github que envolva criacao de perifericos no qemu.
  • desenvolver o proprio projeto.

1.5 horario de aula.

  • 14:00 - 15:50H - qemu.
  • 15:50 - 17:40 - projeto.

1.6 avaliacao:

Q - trabalho com o qemu I - implementacao do projeto. Final = (Q+I)/2

Pontualidade

  • 1 ponto a menos a cada 15 minutos de atraso.
  • avaliacao a cada aula; apresentacao dos resultados a partir das 15:30H.

1.7 Referencias

Livro que usamos no ano passado: Derek Molloy-Exploring Raspberry Pi_ Interfacing to the Real World with Embedded Linux-Wiley (2016).pdf

Livros: Artem Kotovsky - How to Develop Embedded Software Using The QEMU Machine Emulator-Apriorit Inc. (procure na internet) Robert Warnke, Thomas Ritzau - QEMU -BoD (2009)

Estudo do qemu

Sobre como funciona o qemu: http://blog.vmsplice.net/2011/03/qemu-internals-overall-architecture-and.html

Como adicionar um dispositivo

Para hoje:

2 <2019-09-12 Qui> compilar o qemu;

https://www.qemu.org/download/#source

sudo apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev libpython-dev python-pip python-capstone virtualenv

https://en.wikibooks.org/wiki/QEMU/QEMU_Hello_World:_Installing_QEMU_and_getting_it_up_and_running

A compilacao eh muito demorada se gerar todos os qemus para todas as plataformas, assim, procure gerar apenas: ./arm-softmmu/qemu-system-arm

wget https://download.qemu.org/qemu-4.1.0.tar.xz
tar xvJf qemu-4.1.0.tar.xz
cd qemu-4.1.0
./configure --help
./configure --target-list=arm-softmmu,arm-linux-user
make

Observe o binario gerado em /home/professor/Downloads/qemu-4.1.0/arm-softmmu/qemu-system-arm

2.1 coloquem a experiencia de interrupcao do timer rodando no ubuntu normal (fora do docker).

2.1.1 Instale o arm-none-eabi

Vendo, para o ubuntu 16.4 https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/8993/installing-arm-gcc-toolchain-on-ubuntu

sudo apt-get install gcc-arm-none-eabi binutils-arm-none-eabi gdb-arm-none-eabi openocd

Para ubuntu > 16.4: https://marksolters.com/programming/2016/06/22/arm-toolchain-16-04.html

2.2 rodando a experiencia da interrupcao

arm-none-eabi-gdb program.elf 
/home/professor/Downloads/qemu-4.1.0/arm-softmmu/qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel program.bin

Obs: no ubuntu 18.4 nao existe arm-none-eabi-gdb, use gdb-multiarch

2.3 localizem o codigo fonte do timer no qemu

relacionar https://ieeexplore.ieee.org/document/8117087 com o codigo do qemu.

  • quando se escreve em um registrador do timer, qual eh a funcao helper que o qemu dispara?

O timer eh o sp804 e tratado em: file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c

3 <2019-09-19 Qui> estudar o timer e plugar um led virtual

3.1 printf x gdb

  • com o qemu compilado podemos estudah-lo. O objetivo da aula eh observar como o qemu lida com o periferico timer na placa versatilepb. O ideal eh usar o gdb para entender o qemu; mas o grande objetivo da aula de hoje eh entender a interface do qemu com o timer.

3.1.1 printf

  • uma forma simples eh colocar mensagens no qemu para observar se o codigo passa por certos lugares.

3.1.2 gdb

  • para se usar o gdb eh necessario compilar o qemu para ser debugado. o gcc precisa de compilar todo o codigo do qemu com a opcao "-g" para permitir o debug. Felizmente, o qemu jah foi compilado na aula passado (por default) para ser usado com o gdb. Uma vez compilado, o qemu deve ser ativado como:
arm-none-eabi-gdb program.elf 
/home/professor/Downloads/qemu-4.1.0/arm-softmmu/qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel program.bin

Em um teste preliminar, observei que o qemu pode sim ser debugado dessa forma atraves do gdb. Agora vamos trabalhar com 2 gdb's:

1) arm-none-eabi-gdb program.elf 
2) gdb --args /home/professor/Downloads/qemu-4.1.0/arm-softmmu/qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel program.bin

3.2 imprimir uma mensagem do qemu quando se escreve em um registrador do timer

  • Coloquem um breakpoint em arm-none-eabi-gdb de tal forma a parar quando vamos escrever algo no timer. Exemplo:

Em ../meuscursos/embed/e10/irq.s existe a rotina timerinit que faz a inicializacao do timer. A instrucao

TIMER0C: .word 0x101E2008 @timer 0 control register
    (...)
    LDR r0, TIMER0C
    LDR r1, [r0]
    MOV r1, #0xA0 @enable timer module
    STR r1, [r0]

Quando se faz STR no TIMER0C temos que a funcao armtimerwrite deve ser acionada.

Observem todas as funcoes chamadas quando se escreve no timer. Voces devem obter algo como:

Thread 3 "qemu-system-arm" hit Breakpoint 1, arm_timer_write (opaque=0x555556b3e9e0, offset=8, value=160) at hw/timer/arm_timer.c:96
96	{
(gdb) bt
#0  arm_timer_write (opaque=0x555556b3e9e0, offset=8, value=160) at hw/timer/arm_timer.c:96
#1  0x000055555588dfc2 in memory_region_write_accessor (mr=0x555556bcd480, addr=8, value=<optimized out>, size=4, shift=<optimized out>, mask=<optimized out>, attrs=...)
    at /home/professor/Downloads/qemu-4.1.0/memory.c:508
#2  0x000055555588bacd in access_with_adjusted_size (addr=addr@entry=8, value=value@entry=0x7ffff3065318, size=size@entry=4, access_size_min=<optimized out>, access_size_max=<optimized out>, 
    access_fn=0x55555588df40 <memory_region_write_accessor>, mr=0x555556bcd480, attrs=...) at /home/professor/Downloads/qemu-4.1.0/memory.c:574
#3  0x000055555589062b in memory_region_dispatch_write (mr=mr@entry=0x555556bcd480, addr=addr@entry=8, data=data@entry=160, size=size@entry=4, attrs=...)
    at /home/professor/Downloads/qemu-4.1.0/memory.c:1509
#4  0x000055555589e83c in io_writex (env=env@entry=0x5555568010c0, iotlbentry=0x555556809510, mmu_idx=mmu_idx@entry=1, val=val@entry=160, addr=addr@entry=270409736, 
    retaddr=retaddr@entry=140737270678637, size=4) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:950
#5  0x00005555558a3d1c in store_helper (big_endian=false, size=4, retaddr=<optimized out>, oi=33, val=160, addr=<optimized out>, env=0x5555568010c0)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:1556
#6  helper_le_stl_mmu (env=0x5555568010c0, addr=<optimized out>, val=160, oi=33, retaddr=140737270678637) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:1660
#7  0x00007ffff306846d in code_gen_buffer ()
#8  0x00005555558ba0d1 in cpu_tb_exec (itb=<optimized out>, cpu=0x7ffff3068380 <code_gen_buffer+4950>) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:173
#9  cpu_loop_exec_tb (tb_exit=<synthetic pointer>, last_tb=<synthetic pointer>, tb=<optimized out>, cpu=0x7ffff3068380 <code_gen_buffer+4950>)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:621
#10 cpu_exec (cpu=cpu@entry=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:732
#11 0x00005555558811ff in tcg_cpu_exec (cpu=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/cpus.c:1435
#12 0x0000555555883340 in qemu_tcg_cpu_thread_fn (arg=arg@entry=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/cpus.c:1743
#13 0x0000555555ce9936 in qemu_thread_start (args=<optimized out>) at util/qemu-thread-posix.c:502
#14 0x00007ffff5ea96ba in start_thread (arg=0x7ffff3066700) at pthread_create.c:333
#15 0x00007ffff5bdf41d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
(gdb)

3.3 como o qemu colocou o registrador do timer em certo endereco?

Obviamente se escrevessemos na posicao 0x1000 nenhum periferico seria ativado. Como o qemu sabe que eh necessario chamar a funcao armtimerwrite ao se escrever em certo endereco? Como o timer foi alocado em certos enderecos na placa versatilepb?

Para isso, para mostrar todas as ocorrencias de "versat" no codigo:

~/Downloads/qemu-4.1.0/hw/arm$ grep -rl versat .

Dentre varias ocorrencias, o arquivo: file:///home/professor/Downloads/qemu-4.1.0/hw/arm/versatilepb.c contem a descricao do hardware da placa versatilepb

Em particular o timer estah como:

sysbus_create_simple("sp804", 0x101e2000, pic[4]);
sysbus_create_simple("sp804", 0x101e3000, pic[5]);
/* 0x101e2000 Timer 0/1.  */
/* 0x101e3000 Timer 2/3.  */

Assim, para se criar um terceiro timer, em um outro endereco poderiamos fazer algo como (observe que nao eh nessa posicao de fato):

#include "hw/sysbus.h"
(...)
sysbus_create_simple("sp804", 0x101e?000, pic[4]); # para usar a mesma interrupcao do timer 0/1

Observando file:///home/professor/Downloads/qemu-4.1.0/include/hw/sysbus.h vemos:

static inline DeviceState *sysbus_create_simple(const char *name,
                                              hwaddr addr,
                                              qemu_irq irq)
{
    return sysbus_create_varargs(name, addr, irq, NULL);
}

sysbuscreatevarargs estah declarado em: file:///home/professor/Downloads/qemu-4.1.0/hw/core/sysbus.c e cria o device atraves de:

dev = qdev_create(NULL, name);
s = SYS_BUS_DEVICE(dev);

A funcao SYSBUSDEVICE estah definida em file:///home/professor/Downloads/qemu-4.1.0/include/hw/sysbus.h como:

#define SYS_BUS_DEVICE(obj) \
     OBJECT_CHECK(SysBusDevice, (obj), TYPE_SYS_BUS_DEVICE)

Uma ideia de como se conecta um cria um dispositivo no qemu estah em: https://www.linux-kvm.org/images/f/fe/2010-forum-armbru-qdev.pdf http://events17.linuxfoundation.org/sites/events/files/slides/ossj-2017.pdf

3.4 plugar um led virtual

Vamos tentar criar um dispositivo "sp805" que eh apenas uma copia do "sp804". Vamos tentar colocar esse "novo" sp805 para rodar da seguinte forma:

#include "hw/sysbus.h"
(...)
sysbus_create_simple("sp805", 0x101e?000, pic[4]); # para usar a mesma interrupcao do timer 0/1

Para isso vamos criar um novo tipo de timer sp805 em: file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c

static void arm_timer_register_types(void)
{
    type_register_static(&icp_pit_info);
    type_register_static(&sp804_info);
    type_register_static(&sp805_info);
}
... sintam-se a vontade de deixar algo do sp804 no sp805
static const TypeInfo sp805_info = {
    .name          = TYPE_SP805,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(SP804State),
    .instance_init = sp805_init,
    .class_init    = sp804_class_init,
};
...

static void sp805_init(Object *obj)
{
    SP804State *s = SP804(obj); <-- alterar
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);

    sysbus_init_irq(sbd, &s->irq);
    memory_region_init_io(&s->iomem, obj, &sp805_ops, s,   # <--- alterando para sp805_ops
                          "sp804", 0x1000);
    sysbus_init_mmio(sbd, &s->iomem);
}


... funcoes que leem e escrevem, se quiser alterar sp804 para uma nova funcao
static const MemoryRegionOps sp805_ops = {
    .read = sp804_read,
    .write = sp805_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

4 <2019-09-26 Qui> led vitual

static void sp804_write(void *opaque, hwaddr offset,
                        uint64_t value, unsigned size)
{
    SP804State *s = (SP804State *)opaque;

    if (offset < 0x20) {
        arm_timer_write(s->timer[0], offset, value);
        return;
    }

    if (offset < 0x40) {
        arm_timer_write(s->timer[1], offset - 0x20, value);
        return;
    }

    /* Technically we could be writing to the Test Registers, but not likely */
    qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n",
                  __func__, (int)offset);
}

Quando se escreve uma porta do sp804, a funcao eh chamada. Observe que existe um espaco onde nao existem portas como entre 0x40 e 0xfff onde nao existe nenhuma porta mapeada; qualquer tentativa de escrita deve retornar uma mensagem de erro. Experimente alterar irq.s de forma a escrever algo com offset 0x50 e observe se aparece uma mensagem de erro devido a

qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n",  __func__, (int)offset);.

Infelizmente nao observamos erro na console ou em algum arquivo (ex: /tmp/qemu.log); mas o debug via gdb mostrou que essa qemulogmask foi chamada.

  • criar uma porta nova no sp805 (ex: no offset 0x50) : Vamos alterar a funcao de escrita no timer de tal modo que:

Quando escrevermos 1 ou 0 numa posicao de memoria (ex: 0x1000) vamos colocar o numero 1 ou zero no arquivo "led". Esse arquivo representa o led virtual que estah acendendo ou apagando.

  • construir um script que continuamente le esse arquivo e mostra um "led" aceso ou apagado.
  • separar o codigo do sp805 num arquivo a parte de file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c, compilar e testar.
  • cada equipe, enviem o codigo no meu email prof…. ; com subject : "embed - led virtual" e componentes da equipe.

5 <2019-10-03 Qui> timer

Objetivo: entender como o qemu simula o timer. Ao simular o timer, o processador e o timer devem andar em "paralelo": o processador executando instrucoes, e o timer sendo decrementado a cada tick de relogio. Quando o valor vai para zero, o timer gera uma interrupcao. Hoje vamos focar em como o timer eh decrementado enquanto o processador executa intrucoes.

5.1 Como o qemu simula o timer?

Altere o programa que gera as interrupcoes de forma que no loop principal, se imprima continuamente o valor do timer. Observar se esse valor do timer vai sendo modificado dentro do loop principal. file:///home/professor/casa/meuscursos/embed/e10/handler.c file:///home/professor/casa/meuscursos/embed/e10/handler_manual.s file:///home/professor/casa/meuscursos/embed/e10/irq.s Observe a struct em file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c

typedef struct {
    ptimer_state *timer;
    uint32_t control;
    uint32_t limit;
    int freq;
    int int_level;
    qemu_irq irq;
} arm_timer_state;

O que vc. acha que significa cada elemento da struct? Olhe o codigo file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c . Olhando o codigo, o valor "timer" corresponde ao valor do contador. No entanto, nao existe no codigo nenhum lugar onde esse campo da struct eh decrementado.

5.2 O que acontece ao se ler o valor do contador do timer?

static uint32_t arm_timer_read(void *opaque, hwaddr offset)
{
    (...)
    case 1: /* TimerValue */
        return ptimer_get_count(s->timer);

Portanto, o valor do timer (s->timer) estah sendo obtido atraves de ptimergetcount e nao diretamente. Coloque um breakpoint ao se fazer a leitura do timer. Faca bt (backtrace) para observar quais funcoes estao envolvidas em quais arquivos.

 ptimer_get_count (s=0x555556bce3d0) at hw/core/ptimer.c:172
#1  0x0000555555b4dda8 in arm_timer_read (opaque=<optimized out>, offset=<optimized out>) at hw/timer/arm_timer.c:60
#2  0x0000555555b4debc in sp804_read (opaque=<optimized out>, offset=<optimized out>, size=<optimized out>)
    at hw/timer/arm_timer.c:219
#3  0x000055555588e21d in memory_region_read_accessor (mr=0x555556bcd480, addr=4, value=0x7ffff30653e0, size=4, shift=0, 
    mask=4294967295, attrs=...) at /home/professor/Downloads/qemu-4.1.0/memory.c:444
#4  0x000055555588bacd in access_with_adjusted_size (addr=addr@entry=4, value=value@entry=0x7ffff30653e0, size=size@entry=4, 
    access_size_min=<optimized out>, access_size_max=<optimized out>, access_fn=0x55555588e1e0 <memory_region_read_accessor>, 
    mr=0x555556bcd480, attrs=...) at /home/professor/Downloads/qemu-4.1.0/memory.c:574
#5  0x0000555555890450 in memory_region_dispatch_read1 (attrs=..., size=4, pval=0x7ffff30653e0, addr=4, mr=0x555556bcd480)
    at /home/professor/Downloads/qemu-4.1.0/memory.c:1431
#6  memory_region_dispatch_read (mr=mr@entry=0x555556bcd480, addr=addr@entry=4, pval=pval@entry=0x7ffff30653e0, size=size@entry=4, 
    attrs=...) at /home/professor/Downloads/qemu-4.1.0/memory.c:1452
#7  0x000055555589e185 in io_readx (env=env@entry=0x5555568010c0, iotlbentry=0x555556809510, mmu_idx=mmu_idx@entry=1, 
    addr=addr@entry=270409732, retaddr=retaddr@entry=140737270682333, access_type=access_type@entry=MMU_DATA_LOAD, size=4)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:909
#8  0x000055555589fb3b in load_helper (full_load=0x55555589f8a0 <full_le_ldul_mmu>, code_read=false, big_endian=false, size=4, 
    retaddr=<optimized out>, oi=<optimized out>, addr=<optimized out>, env=0x5555568010c0)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:1308
#9  full_le_ldul_mmu (env=0x5555568010c0, addr=<optimized out>, oi=<optimized out>, retaddr=140737270682333)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cputlb.c:1423
#10 0x00007ffff3069340 in code_gen_buffer ()
#11 0x00005555558ba0d1 in cpu_tb_exec (itb=<optimized out>, cpu=0x7ffff3069280 <code_gen_buffer+8790>)
    at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:173
#12 cpu_loop_exec_tb (tb_exit=<synthetic pointer>, last_tb=<synthetic pointer>, tb=<optimized out>, 
    cpu=0x7ffff3069280 <code_gen_buffer+8790>) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:621
#13 cpu_exec (cpu=cpu@entry=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/accel/tcg/cpu-exec.c:732
#14 0x00005555558811ff in tcg_cpu_exec (cpu=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/cpus.c:1435
#15 0x0000555555883340 in qemu_tcg_cpu_thread_fn (arg=arg@entry=0x5555567f7fd0) at /home/professor/Downloads/qemu-4.1.0/cpus.c:1743
#16 0x0000555555ce9936 in qemu_thread_start (args=<optimized out>) at util/qemu-thread-posix.c:502
#17 0x00007ffff5ea96ba in start_thread (arg=0x7ffff3066700) at pthread_create.c:333
#18 0x00007ffff5bdf41d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
#0  arm_timer_tick (opaque=0x555556b3e9e0) at hw/timer/arm_timer.c:147
#1  0x0000555555ce3cee in aio_bh_call (bh=0x555556bce3a0) at util/async.c:89
#2  aio_bh_poll (ctx=ctx@entry=0x5555567da670) at util/async.c:117
#3  0x0000555555ce7200 in aio_dispatch (ctx=0x5555567da670) at util/aio-posix.c:459
#4  0x0000555555ce3bce in aio_ctx_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>)
    at util/async.c:260
#5  0x00007ffff6bb2197 in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#6  0x0000555555ce6291 in glib_pollfds_poll () at util/main-loop.c:218
#7  os_host_main_loop_wait (timeout=<optimized out>) at util/main-loop.c:241
#8  main_loop_wait (nonblocking=<optimized out>) at util/main-loop.c:517
#9  0x0000555555a08235 in main_loop () at vl.c:1791
#10 0x0000555555835cb6 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4473

file:///home/professor/Downloads/qemu-4.1.0/util/async.c

file:///home/professor/Downloads/qemu-4.1.0/util/main-loop.c

5.3 Como o valor interno do timer eh decrementado a cada tick de relogio?

A funcao ptimergetcount estah em (vide debug acima): file:///home/professor/Downloads/qemu-4.1.0/hw/core/ptimer.c Ao observar o codigo de ptimergetcount observamos :

int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);

onde a variavel now deve indicar o momento atual. Colocando um breakpoint em qemuclockgetns caimos em: file:///home/professor/Downloads/qemu-4.1.0/util/qemu-timer.c O valor do timer provem de:

QEMUTimerListGroup main_loop_tlg;
qemu_clock_run_timers

Procurem localizar o codigo em C que apresenta quando o timer eh decrementado.

5.4 Como o timer gera interrupcao ao final da contagem?

5.5 O qemu utiliza uma thread para simular o timer?

static arm_timer_state *arm_timer_init(uint32_t freq)
 ... bh = qemu_bh_new(arm_timer_tick, s);

5.6 Como o qemu simula o caso "one shot" e "periodic".

5.7 Observacao

O qemu nao atualiza continuamente o tick do timer a fim de evitar muito processamento. Ele usa o relogio da maquina e faz uma aproximacao do valor do tick do timer. Se dentro dessa aproximacao, houver uma interrupcao do timer; entao ela eh gerada.

6 <2019-10-10 Qui> chave

6.1 - criar uma chave como periferico usando o sp805.

crie uma porta de entrada no sp805. O codigo deve ler um numero (zero ou um) de um certo arquivo. crie um pequeno codigo assembly que continuamente le de essa porta de entrada e imprime zero ou um.

6.2 - interrupcao / signal handler

Vamos criar, futuramente, uma forma do usuario criar um periferico virtual que gera uma interrupcao. vamos criar um signal handler no qemu de forma que geremos uma interrupcao para o nosso periferico virtual. Para isso:

6.2.2 Insira um signal handler no codigo do qemu

Experimente apenas criar um signal handler no codigo do qemu, quando por exemplo, o sp805 eh registrado. Iremos simular uma interrupcao quando enviarmos um kill para o qemu.

7 <2019-10-17 Qui> gerar interrupcao

static void arm_timer_update(arm_timer_state *s)
{
    /* Update interrupts.  */
    if (s->int_level && (s->control & TIMER_CTRL_IE)) {
        qemu_irq_raise(s->irq);
    } else {
        qemu_irq_lower(s->irq);
    }
}

portanto para levantar ou abaixar a interrupcao basta fazer:

qemu_irq_raise(s->irq);
qemu_irq_lower(s->irq);

onde s deve vir de:

typedef struct SP804State {
    SysBusDevice parent_obj;

    MemoryRegion iomem;
    arm_timer_state *timer[2];
    uint32_t freq0, freq1;
    int level[2];
    qemu_irq irq;
} SP804State;

Para a aula de hoje, vamos tentar:

  • inicializar o sp805 e faze-lo trabalhar gerando as interrupcoes.
  • capturar s->irq de sp805
  • funciona?! (obviamente deve funcionar).

Depois:

  • desabilitar a interrupcao do sp805 (desabilita em um registrador)

Veja como nao gerar a interrupcao mexendo em TIMER0C (ex: nao disparando a contagem); para habilitar temos: file:///home/professor/casa/meuscursos/embed/e10/irq.s

LDR r0, TIMER0C
LDR r1, [r0] @ realmente nao faze sentido isso em irq.s...
MOV r1, #0xA0 @enable timer module
STR r1, [r0]

Para desabilitar, estude http://infocenter.arm.com/help/topic/com.arm.doc.ddi0271d/DDI0271.pdf

  • gere a interrupcao atraves colocando
qemu_irq_raise(s->irq);

no tratamento do signal do qemu.

A seguir temos que estudar passo a passo como o sp805 inicializa o s->irq para nao depender dessa inicializacao.

Estudar como o sp805 desabilita a interrupcao na leitura do registrador TIMER0X. Vamos tentar fazer o mesmo para criar um botao totalmente independente do sp805.

file:///home/professor/Downloads/qemu-4.1.0/hw/input

8 <2019-10-24 Qui> driver linux simples

8.1 retomar driver

8.2 retomando fase 3 de SO, rodando dentro do docker.

Colocar o trabalho da equipe do Shibata, fase 3, rodar dentro docker: http://linux-kernel-lab.blogspot.com/2018/06/blocking-device.html

8.3 fase 3 de SO, fora do docker.

Fazer o mesmo trabalho da equipe do Shibata fora do docker. Observem: Em: file:///home/professor/Downloads/pcs3746-sistemas-operacionais/1/docker/Dockerfile temos os comandos para subir o ubuntu 16.4 e instalar as toolchains necessarias para compilar o linux :

apt-get update && apt-get install --no-install-recommends -y bc binutils-arm-linux-gnueabi \
    build-essential cpio gcc-arm-linux-gnueabi gdb-arm-none-eabi libc6-dev-armel-cross \
    libelf-dev locales qemu-system-arm sudo && \
    rm -rf /var/lib/apt/lists/*

Em:

file:///home/professor/Downloads/pcs3746-sistemas-operacionais/1/docker/default_cmd.sh temos o comando para compilar o linux usando o toolchain instalada pelo Dockerfile e ativar o qemu.

Alem disso, para a fase 3, a equipe do Shibata criou um init para disparar uma shell, o "busybox sh".

9 <2019-10-31 Qui> driver simples

  • Devido a problemas na rede, a grande maioria dos grupos nao conseguiu rodar a experiencia 3 dentro do docker, e muito menos fora do docker.
  • O Renan gentilmente disponibilizou uma imagem na amazon contendo a experiencia 3 (dentro e fora do docker): http://www.pcs.usp.br/~jkinoshi/2019/Tutorial%20AWS.docx
  • Para hoje:

9.1 driver no linux para uma versatilepb, compilando-o como um modulo a ser inserido via busybox.

Veja como a equipe do Shibata compilou o driver e faca de forma semelhante.

9.2 alterar driver para acender um led virtual

Usando o qemu modificado com o sp805, alterar o driver simples para acender um led.

teste o driver fazendo algo como:
echo "1" > /dev/led # acende led
echo "0" > /dev/led

10 <2019-11-07 Qui> teclado

10.1 Objetivo:

  • entender como funciona o loop principal do qemu
  • estudar como as teclas sao enviadas para o driver do linux observando o loop principal. A placa versatile pode receber dados do teclado da maquina hospedeira atraves da serial ou do teclado (ver arquivo de versatilepb.c). Quando o linux estah usando o busybox shell deve estar vendo dados vindos pela serial.

A transparencia: http://events17.linuxfoundation.org/sites/events/files/slides/Improving%20the%20QEMU%20Event%20Loop%20-%203.pdf mostra 3 fases no loop principal: prepare, poll, dispatch.

Ao teclar supomos (nao tenho certeza) que nas fases: 1-prepare: o evento eh dectado. Como o evento foi detectado? Por epollwait, epollpwait, select, pselect? 2-poll: o qemu escolhe dentre varios eventos qual vai ser tratado. Supondo que tivesse ocorrido o estouro de um timer junto com o aperto de uma tecla, qual seria tratado primeiro? 3-dispatch: o qemu deve gerar uma interrupcao para o verstile informando de que houve a interrupcao.

  • gerar um relatorio informando o que foi descoberto sobre o funcionamento do qemu.

Se entendermos bem esse loop principal, acreditamos que poderemos criar um dispositivo no qemu que gere interrupcoes. Exemplo: o teclado poderia ser visto pelo versatile apenas como um botao que gera as interrupcoes (um grande desperdicio de teclado); ou o qemu poderia ser modificado para quando teclassemos algo como Control-Alt-1, entao o botao emulado geraria a interrupcao.

10.2 Metodo

  • A transparencia mostra o nome de funcoes nas fases prepare, poll e dispatch. Colocar printfs.
  • entenda a system call epoll fazendo :

https://medium.com/@copyconstruct/the-method-to-epolls-madness-d9d2d6378642

https://suchprogramming.com/epoll-in-3-easy-steps/

Verifique se de fato eh o numero 41.

(gdb) catch syscall 41
(gdb) r
  • Colocar breakpoints no lugar onde a tecla foi apertada e o qemu detectou. Usando "bt" obervar quais foram as funcoes chamadas, o que deve estar na fase prepare.
  • Colocar breakpoints quando o aperto da tecla gerar interrupcoes de teclado para o versatile, o que deve estar na fase dispatch.
  • Observar no codigo qual eh a interrupcao gerada pelo teclado para o linux. Qual eh o numero da interrupcao dado pelo qemu?
  • do lado do linux: descobrir qual driver eh acionado quando o busybox recebe uma tecla.
  • qual rotina de interrupcao eh acionada? colocar um breakpoint quando o driver recebe uma tecla.

10.3 desafio

  • gerar uma interrupcao para o sp805 via teclado, por exemplo, quando o usuario teclar "1".

https://www.xml.com/ldd/chapter/book/ch08.html

11 <2019-11-14 Qui> botao e led - I

Livro texto: Derek Molloy-Exploring Raspberry Pi_ Interfacing to the Real World with Embedded Linux-Wiley (2016) codigo: git clone https://github.com/derekmolloy/exploringrpi.git

11.1 instalar raspbian

  • gravar imagem no micro sdcard; De preferencia que cada equipe tenha um micro sdcard.

11.2 montagem de led

fig. 6.2 b) Observar a frequencia maxima possivel acendendo e apagando leds.

11.3 bash script

fig 6.1

11.4 montagem do botao

fig. 6.5

11.5 testar entrada

pg 225

11.6 Conectando o GPIO

Responda no relatorio:

  • Qual o objetivo de resistores internos pull down e pull up? pg 226
  • Como verificar se a saida do GPIO eh pulldown ou pullup?
  • Para que serve o optocoupler? pg 228
  • Pesquise: O que é o sysfs no linux?

11.7 lendo o botao por sysfs

  • Leia "C++ Control of GPIOs Using sysfs"
  • Rode o programa

12 <2019-11-21 Qui> botao e led - II

12.1 linux preemptivo e cyclictest

Responda no relatorio:

  • O que o livro fala sobre o linux kernel ser preemptivo? pg 223
  • como verificar se o linux kernel eh preemptivo ou nao? Faca testes com sua maquina e a raspberry pi.
  • O que faz o cyclictest e o que tem a haver com o kernel ser preemptivo? Veja: http://people.redhat.com/williams/latency-howto/rt-latency-howto.txt
  • Rode o cyclictest no raspberry pi3 e em sua maquina normal.

12.2 C++ Control of GPIOs Using sysfs, pg 229, Sobre C++

  • rode o exemplo de funcao callback pg 237
  • posix threads

Responda:

  • O que eh o sysfs?
  • Como pode ser usado para controlar perifericos na GPIO?

12.2.1 GPIO.h

  • rode testsyspool.cpp pg 242.
  • Como funciona inGPIO.waitForEdge() ?
  • Rode o segundo exemplo, observando que inGPIO->waitForEdge(&activateLED) chama uma funcao de callback.

12.3 Memory-Based GPIO Control, pg 245,

  • GPIO visto como memoria, via devmem2
  • Estude o item Memory-Based GPIO Control
  • rode o codigo /chp06/memoryGPIO/LEDflash.c pg 249

Responda:

  • Como o GPIO pode ser acessada em C?
  • Mostre exemplos de como isso ocorre em devmem2.
  • Crie um onda quadrada com a maior frequencia possivel usando C. Qual o periodo?

12.4 WiringPi pg 252

  • Como usar python para acessar os leds?
  • Gere uma onda quadrada o mais rapido possivel usando python. Qual o periodo?

12.5 GPIOs and Permissions pg 270

Como usar udev o bit setuid para controlar aplicacoes que usam o GPIO rodando em modo usuario?

13 <2019-11-28 Qui> cp 16k interface com o kernel

Veja o video em: http://exploringrpi.com/chapter16/

Faca o cap 16 do livro e coloque as respostas das perguntas no relatorio:

13.1 Introduction, livro pg 647

13.1.1 Why Write Kernel Modules?

Para interagir com o gpio podemos usar:

  • sysfs
  • drivers
  • i/o mapeado em memoria

Quais as vantagens e desvantagens de cada ideia? O livro recomenda que se escreva drivers para dispositivos?

13.1.2 Loadable Kernel Module (LKM) Basics

Quais as diferencas de codigo rodando em modo kernel e modo usuario?

13.2 A First LKM Example

Execute o codigo para rodar um exemplo de modulo rodando no ubuntu normal e depois no raspberry.

13.2.1 The LKM Makefile

13.2.2 Building the LKM on a Linux Desktop Machine

13.2.3 Building the LKM on the RPi

13.2.4 Testing the First LKM Example

13.3 An Embedded LKM Example

13.3.1 Interrupt Service Routines (ISRs) pg 661

O que faz a rotina de interrupcao? Como o kernel ficou notificado de que ela existe? A qual numero de interrupcao essa rotina estah amarrada? Esse numero pode variar?

13.3.2 Performance 665

Pedir ajuda para o Daniel, para observar o tempo de resposta.

14 <2019-12-05 Qui> KLM

Respondam no relatorio

14.1 fizeram a onda quadrada usando python?

Se nao fizeram, facam.

14.2 fizeram a onda quadrada em C atraves de I/O mapeado em memoria?

Se nao fizeram, facam.

14.3 sysfs

Estudar: http://pradheepshrinivasan.github.io/2015/07/02/Creating-an-simple-sysfs/ Modificar o modulo para contar o numero de vezes que o botao foi pressionado e permitir com que esse valor seja visto no sysfs. Exemplo cat /sys/botao

Alterar o codigo para permitir com que se resete o valor do contador fazendo algo como: echo 0 > /sys/botao

14.4 Resposta: Enhanced Button GPIO Driver LKM livro pg 665

O que sao kobjects? Como eles sao utilizados no driver? O que eh armazenado no kobject que estah no driver? Como o driver apresenta o numero de vezes que o botao eh pressionado em /sys ? Que funcao utiliza? A qual variavel estah "amarrada"?

14.4.1 The kobject Interface

14.5 Enhanced LED GPIO Driver LKM

14.5.1 Kernel Threads pg 674

Qual o objetivo da thread? Quando a kthread espera por um tempo, o que faz o kernel do linux ?

14.6 driver simples para botao e chave.

Construir um driver para um dispositivo de entrada composto de botao e chave. a cada borda de subida do botao, o valor da chave eh lido e armazenado. Na primeira versao, quando o driver eh lido, apenas o valor da ultima posicao da chave eh apresentada. Se nao houve entrada de dados, interrupcao, entao o driver deve ficar bloqueado. Numa segunda versao, ao ler do dispositivo, o driver deve retornar a string de zeros e uns que foi formada. Se nao houver nada vindo do dispositivo, o processo que faz a leitura do driver deve ficar bloqueado numa fila de espera.

15 Racunho

16 <2019-10-23 Qua> Tentando entender: o qemu quebra ao tentar gerar a interrupcao de dentro de um signal handler.

https://lists.nongnu.org/archive/html/qemu-devel/2011-07/msg00842.html

o armtimertick soh acontece porque eh feito:

bh = qemubhnew(armtimertick, s);

Quando ocorre o armtimertick tenho que isso vem do mainloopwait

acho que dava o erro de referencia invalida porque o armtimertick foi chamado diretamente e nao estava atrelado ao loop principal. Eu deveria fazer o mesmo para o sp805; soh nao sei como ativar o tick pelo loop principal.

0 armtimertick (opaque=0x555556b3e9e0) at file:///home/professor/Downloads/qemu-4.1.0/hw/timer/arm_timer.c :145 #1 0x0000555555ce3cee in aiobhcall (bh=0x555556bce3a0) at file:///home/professor/Downloads/qemu-4.1.0/util/async.c :89 #2 aiobhpoll (ctx=ctx@entry=0x5555567da670) at file:///home/professor/Downloads/qemu-4.1.0/util/async.c :117 #3 0x0000555555ce7200 in aiodispatch (ctx=0x5555567da670) at file:///home/professor/Downloads/qemu-4.1.0/util/aio-posix.c :459 #4 0x0000555555ce3bce in aioctxdispatch (source=<optimized out>, callback=<optimized out>, userdata=<optimized out>) at file:///home/professor/Downloads/qemu-4.1.0/util/async.c :260 #5 0x00007ffff6bb2197 in gmaincontextdispatch () from /lib/x8664-linux-gnu/libglib-2.0.so.0 #6 0x0000555555ce6291 in glibpollfdspoll () at file:///home/professor/Downloads/qemu-4.1.0/util/main-loop.c :218 #7 oshostmainloopwait (timeout=<optimized out>) at file:///home/professor/Downloads/qemu-4.1.0/util/main-loop.c :241 #8 mainloopwait (nonblocking=<optimized out>) at file:///home/professor/Downloads/qemu-4.1.0/util/main-loop.c :517 #9 0x0000555555a08235 in mainloop () at file:///home/professor/Downloads/qemu-4.1.0/vl.c :1791 #10 0x0000555555835cb6 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4473

https://terenceli.github.io/%E6%8A%80%E6%9C%AF/2018/09/06/qemu-interrupt-emulation

https://stackoverflow.com/questions/14869317/arm-interrupt-handling-in-qemu

qemusetirq() is used for doing the modelled equivalent of "my device just set its outbound 'interrupt' signal to 1". Typically the board model will connect that signal wire up to an interrupt controller. The interrupt controller in turn will have outbound wires that connect to the CPU's IRQ and FIQ inputs. So the device raises its interrupt line; the interrupt controller will handle that in the same way the hardware would (depending on how the guest has programmed it to set the interrupt priority, whether that interrupt is enabled/disabled, etc, whether the line is configured to raise IRQ or FIQ), and might raise either the IRQ or FIQ line to the CPU, or might do neither immediately. Then the CPU model handles having its IRQ or FIQ input set, by doing what a CPU does in that situation: it causes the guest to stop doing what it was doing and start executing at the appropriate entry point for an interrupt handler. The tcgexitreq flag is part of QEMU's internal mechanism for causing the emulated CPU to stop doing whatever it was doing. When the CPU reaches the beginning of the next basic block it will check that flag and break out of its "execute code" inner loop, at which point it will notice that it has a pending exception to take.

https://stackoverflow.com/questions/47410387/how-to-make-qemu-generate-external-interrupt-and-jump-to-isr-bare-metal-code

You shouldn't be calling cpuinterrupt() yourself (it is an internal function within QEMU's CPU model code, not one intended to be called from a board or device model). The Arm M-profile CPU has a very tightly integrated interrupt controller (the NVIC), which in QEMU's emulation is also closely connected to the CPU. You should have your board model wire up the UART's interrupt line to the NVIC, and then the NVIC will take care of telling the CPU which interrupt needs to be taken (based on interrupt priorities, masking, etc). The error message you're getting is because your incorrect call to cpuinterrupt() is effectively telling one half of this system (the cpu) about the interrupt but not the other half (the NVIC) – so when the cpu asks the NVIC about this interrupt the NVIC says "never heard of it" and aborts. If you use the proper interface for telling the NVIC about interrupts it will work correctly. (The existing QEMU M profile board models will have code which does this.)

Important note: the M profile interrupt handling was significantly rewritten in QEMU 2.9, as it was quite buggy before then, and we fixed some further exception related bugs in 2.10. If you are using M profile I strongly recommend using 2.10 or later. I can tell you aren't because that particular error message is part of the old and buggy code.

Look at the existing M profile device and board models, but basically the device just calls qemusetirq(), and because the board model has connected that line up to the NVIC that's the only thing the device needs to do.

Eu deveria para o barramento gerar uma interrupcao, mas

0 qemusetirq (irq=0x555556bce830, level=1) at hw/core/irq.c:41 #1 0x0000555555ce3cee in aiobhcall (bh=0x555556bce3a0) at file:///home/professor/Downloads/qemu-4.1.0/util/async.c :89 #2 aiobhpoll (ctx=ctx@entry=0x5555567da670) at util/async.c:117 #3 0x0000555555ce7200 in aiodispatch (ctx=0x5555567da670) at util/aio-posix.c:459 #4 0x0000555555ce3bce in aioctxdispatch (source=<optimized out>, callback=<optimized out>, userdata=<optimized out>) at util/async.c:260 #5 0x00007ffff6bb2197 in gmaincontextdispatch () from /lib/x8664-linux-gnu/libglib-2.0.so.0 #6 0x0000555555ce6291 in glibpollfdspoll () at util/main-loop.c:218 #7 oshostmainloopwait (timeout=<optimized out>) at util/main-loop.c:241 #8 mainloopwait (nonblocking=<optimized out>) at util/main-loop.c:517 #9 0x0000555555a08235 in mainloop () at vl.c:1791 #10 0x0000555555835cb6 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4473

Uma forma de fazer a interrupcao: aiobhcall(bh)

e isso deve causar o tratamento da interrupcao chamando qemusetirq . Como descobrir onde estah bh?

sp804init contem o estado do dispositivo e como ele se liga ao barramento, inclusive interrupcao.

#0 sp804init (obj=0x555556bcd170) at hw/timer/armtimer.c:294 #1 0x0000555555bfbfbd in objectinitializewithtype (data=data@entry=0x555556bcd170, size=<optimized out>, type=type@entry=0x555556726f50) at qom/object.c:466 #2 0x0000555555bfc080 in objectnewwithtype (type=0x555556726f50) at qom/object.c:635 #3 0x0000555555a61811 in qdevtrycreate (bus=0x0, type=0x555555dc97d3 "sp804") at hw/core/qdev.c:144 #4 0x0000555555a618c1 in qdevcreate (bus=bus@entry=0x0, name=name@entry=0x555555dc97d3 "sp804") at hw/core/qdev.c:123 #5 0x0000555555a674e9 in sysbuscreatevarargs (name=name@entry=0x555555dc97d3 "sp804", addr=addr@entry=270409728) at hw/core/sysbus.c:233 #6 0x000055555596546a in sysbuscreatesimple (irq=<optimized out>, addr=270409728, name=0x555555dc97d3 "sp804") at /home/professor/Downloads/qemu-4.1.0/include/hw/sysbus.h:126 #7 versatileinit (machine=0x5555567db400, boardid=387) at /home/professor/Downloads/qemu-4.1.0/hw/arm/versatilepb.c:298 #8 0x0000555555a694c3 in machinerunboardinit (machine=0x5555567db400) at hw/core/machine.c:1132 #9 0x0000555555835829 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4348

sp804realize -> complementa a inicializacao gdb) bt #0 sp804realize (dev=0x555556bcd170, errp=0x7fffffffd1b0) at hw/timer/armtimer.c:305 #1 0x0000555555a62ae9 in devicesetrealized (obj=<optimized out>, value=<optimized out>, errp=0x7fffffffd2a0) at hw/core/qdev.c:834 #2 0x0000555555bfb55e in propertysetbool (obj=0x555556bcd170, v=<optimized out>, name=<optimized out>, opaque=0x555556b3ea10, errp=0x7fffffffd2a0) at qom/object.c:2079 #3 0x0000555555bffabf in objectpropertysetqobject (obj=obj@entry=0x555556bcd170, value=value@entry=0x555556bcde70, name=name@entry=0x555555e14df9 "realized", errp=errp@entry=0x7fffffffd2a0) at qom/qom-qobject.c:26 #4 0x0000555555bfd405 in objectpropertysetbool (obj=0x555556bcd170, value=<optimized out>, name=0x555555e14df9 "realized", errp=0x7fffffffd2a0) at qom/object.c:1337 #5 0x0000555555a61a82 in qdevinitnofail (dev=dev@entry=0x555556bcd170) at hw/core/qdev.c:321 #6 0x0000555555a67519 in sysbuscreatevarargs (name=name@entry=0x555555dc97d3 "sp804", addr=addr@entry=270409728) at hw/core/sysbus.c:235 #7 0x000055555596546a in sysbuscreatesimple (irq=<optimized out>, addr=270409728, name=0x555555dc97d3 "sp804") at /home/professor/Downloads/qemu-4.1.0/include/hw/sysbus.h:126 #8 versatileinit (machine=0x5555567db400, boardid=387) at /home/professor/Downloads/qemu-4.1.0/hw/arm/versatilepb.c:298 #9 0x0000555555a694c3 in machinerunboardinit (machine=0x5555567db400) at hw/core/machine.c:1132 #10 0x0000555555835829 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4348

professor@LABMICRO-B1:~/casa/meuscursos/embed/e10$ /home/professor/Downloads/qemu-4.1.0/arm-softmmu/qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel program.bin Hello world! timerload timercontrol timerintclr

Da primeira vez que ocorre o signal, o qemu dispara sem eu chamar:

init init init init write recalibrate upadate write recalibrate upadate tick upadate write upadate Hello World!

Author: Professor

Created: 2019-12-05 Qui 13:46

Validate