2021/09/23

H4CK3D 2021: un desvío innecesario

Recordemos, para la charla necesito comprender como funciona, por una cuestión de completitud y para colaborar con el proyecto edu-ciaa-fpga terminé de incorporar el conector arduino y un pmod como input.

Para el ataque de la charla, viene bien tener un evento de activación, o sea, que no funcione siempre sino en un rango temporal reducido para que sea más difícil que salte en un test, para ello necesito algo que lleve el tiempo, un reloj, independiente del programa, lo llamé ambiciosamente RTC pero es mucho menos, es un trocito de verilog que cuenta los segundos y minutos y desborda a la hora.

¿Por qué no me sirve un reloj por software? pues por que para ello necesitaría software colgado de systick o similar y saber en la RAM donde guarda el valor. Me pareció más sencillo y sin colisión con un escenario real el circuito independiente.

Es muy sencillo:

counter <= counter + 1;
if (counter == COUNT ) begin
  counter <= 0;
  secondsLo <= secondsLo + 1;
  if (secondsLo == 9) begin
    secondsLo <= 0;
    secondsHi <= secondsHi + 1;
    if (secondsHi == 5) begin
      secondsHi <= 0;
      minutesLo <= minutesLo + 1;
      if (minutesLo == 9) begin
        minutesLo <= 0;
        minutesHi <= minutesHi + 1;
        if (minutesHi == 5) begin
          minutesHi <= 0;
        end
      end
    end
  end
end

Te juro que quise tomarlo de uno existente, pero eran muy complicados, quizás no sea una maravilla pero funciona.

En la primera versión tiré eso dentro de un always en el top.sv y lo conecté al arduino, pero era muy sucio y no manifestaba desafío, entonces decidí convertirlo en un módulo y además conectarlo a los buses de datos, direcciones y control como cualquier otro dispositivo.

Como es un módulo, existe en su propio .sv, rtc.sv y en icicle.sv hay que incluirlo:

`include "rtc.sv"

 

Luego hay que agregar el registro donde se lo lee:

logic [31:0] rtc_read_value;

 

y su asignación al registro de lectura:

assign mem_read_value =
       ram_read_value |... | rtc_read_value | ...;

 

Las señales de selección: 

logic rtc_sel;

Las de ready:

logic rtc_ready;
assign mem_ready = ram_ready | ... | rtc_ready | ...;


Mapearlo al direccionamiento:


32'b..001_00000000_000101??: rtc_sel = 1; //0x00010014


Y finalmente a los buses:

 

rtc #(.COUNT(36000000)) rtc (
    .clk_in(clk),
    .reset(reset),
    /* memory bus */
    .address_in(mem_address),
    .sel_in(rtc_sel),
    .read_in(mem_read),
    .read_value_out(rtc_read_value),
    .write_mask_in(mem_write_mask),
    .write_value_in(mem_write_value),
    .ready_out(rtc_ready)
);

 

Puse las señales de escritura tambien, no por copiar y pegar sino por que en el diseño original, en la flash que entiendo no se puede escribir tal como está, están presentes. Intuyo que dado que no se usan no se sintetizan, así que no molestan e igual me queda para el futuro si quisiera cambiar la hora desde el programa.


En el rtc.sv, además de conectar los buses, como la lectura es inmediata:


assign ready_out = sel_in;

Y la lectura proviene de:


assign read_value_out = 

{16'b0,
 sel_in ?
   {minutesHi,minutesLo,secondsHi,secondsLo}
   : 16'b0};


Como usa 16 bits (cuatro para cada dígito), los primeros 16 son ceros. Si está activo sel_in, van los valores apropiados, sino, otros 16 ceros.

Finalmente el programa de ejemplo, lo basé en el hello original para recuperar la uart, el mapeo se hace con:


#define RTC    *((volatile uint32_t *) 0x00010014)


El main lo que hace es leer el RTC, lo copia al conector de arduino para manifestar los valores binarios y 

 

for (;;) {

    uint32_t rtc = RTC;
    ARDUINO = rtc;
    msg[5] = ((rtc & 0xf000 ) >> 12 ) + '0';
    msg[6] = ((rtc & 0xf00 ) >> 8 ) + '0';
    msg[8] = ((rtc & 0xf0 ) >> 4 ) + '0';
    msg[9] = ( rtc & 0xf ) + '0';
    uart_puts(msg);
    LEDS = ~LEDS;
    uint32_t start = rdcycle();
    while ((rdcycle() - start) <= FREQ);
}


Con los msg[x] se parchea un mensaje:

"RTC: ..:..\r\n";


Va tomando de a cuatro bits, los desplaza al nibble menos significativo y lo convierte en el ascii apropiado sumándolo al caracter '0'.


Este mensaje lo tuve que definir con:


    char msg[13];
    msg[0]='R';
    msg[1]='T';
    msg[2]='C';
    msg[3]=':';
    msg[4]=' ';
    msg[5]='.';
    msg[6]='.';
    msg[7]=':';
    msg[8]='.';
    msg[9]='.';
    msg[10]='\r';
    msg[11]='\n';
    msg[12]='\0';

 

En lugar de:

 

char msg[] = "RTC: ..:..\r\n";

 

Debido a este mensaje de error, cuyo significado intuyo pero prefiero no decir nada sin fundamentos para no generar información errónea:

 

/usr/local/lib/gcc/riscv64-unknown-elf/11.1.0/../../../../riscv64-unknown-elf/bin/ld: /usr/local/lib/gcc/riscv64-unknown-elf/11.1.0/../../../../riscv64-unknown-elf/lib/libc.a(lib_a-memcpy.o): ABI is incompatible with that of the selected emulation:
 

target emulation `elf64-littleriscv' does not match `elf32-littleriscv'

 

Funciona ok, el programa toma el valor del RTC y lo pone en el arduino, falta video, creeme y lo muestra en la uart:

 

Minutos y segundos transcurridos desde el inicio
Minutos y segundos transcurridos desde el inicio, reset o último overflow

 







2021/09/20

H4CK3D 2021: un desvío necesario

Si venís leyendo los primeros pasos, entiendo que te va a resultar más o menos natural que siga tomando el control de la placa en lugar de ir directo hacia la POC.

Para esta necesito examinar el flujo de ejecución de las instrucciones del programa a atacar, ante mi se abren tres caminos, no necesariamente excluyentes:


Analizar el código fuente generado

 

O tomar el ejecutable y decompilarlo a assembly o pedirle al compilador que genere o conserve el mismo código. Esto último se logra con la opción -save-temps agregada a la siguiente línea del Makefile:


CFLAGS = -march=rv32i -mabi=ilp32 -Wall -Wextra -pedantic -DFREQ=$(FREQ_PLL)000000 -Os -ffreestanding -nostartfiles -g -Iprograms/$(PROGRAM)

Me parece conveniente además quitár ese -Os, que es optimización de tamaño.

 

Generar una simulación

 

Por lo que entendí de la charla FOSS para el desarrollo con FPGA de Rodrigo Melo, verilator es mi amigo, pero fuí a la documentación y está pensada para quien se va a dedicar, no para un turista como yo, aprender a usarlo es un subproyecto tan grande como el resto del proyecto.

 

Instrumentación

 

Puedo volcar los buses a los puertos de salida y capturarlos desde otro sistema, puede ser buen momento para usar la PYNQ, que tiene la cantidad de entradas apropiadas y al correr linux me facilita muchas cosas, pero, también es un subproyecto muy grande.


Tanto en la simulación como en la monitorización, necesito lidiar con:

static inline uint32_t rdcycle(void) {
    uint32_t cycle;
    asm volatile ("rdcycle %0" : "=r"(cycle));
    return cycle;
}

        uint32_t start = rdcycle();
        while ((rdcycle() - start) <= FREQ);

 

No sé si es un mero delay o cumple otra función indispensable, recordemos que cualquier problema de concurrencia se resuelve con un delay().

Mientras barajo todas estas posibilidades, lo mejor es terminar de implementar el acceso al conector arduino y el pmod restante, a modo de práctica y profundización del conocimiento del sistema y de paso es trabajo que se puede aportar al icicle de Pablo, por si alguien luego lo quiere aprovechar.


entonces...

 

De tanto repetir, ya tengo un procedimiento, funciona pero los razonamientos que agrego son muy personales, tomalos con suma precaución:

Identificar los pines y agregarlos al archivo.pcf

Agregar esos pines ya sea como input u ouput al top.

Pasarlos al módulo icicle

Dentro del modulo icicle, tomar los pines

Para cada módulo definir

    logic XXX_sel; 

Este sirve para que luego el mapeador, el casez (mem_address) identique a donde está apuntando la dirección en el bus 

    logic [31:0] XXX_read_value;

Es donde va a quedar escrito el valor leído

    logic XXX_ready;

Avisa que el valor leído puede ser leído. Fijate que casi todos los _ready están conectados a _sel, esto es por que se pueden leer inmediatamente. En los casos de  ram.sv y flash.sv evidentemente hay demoras.

Si notaste que los dispositivos están enmarcado en unos `ifdef asociados a defines que van en edufpga-defines.sv:

 

// Defines for EDU-FPGA
`define PMOD0
`define PMOD1
`define ARDUINO

 

Notarás tambien que en el caso de no estar definido se le asigna cero a la lectura y siempre listo:

 

`ifdef PMOD1
    assign pmod1_read_value = {24'b0, pmod1_sel ? pmod1 : 8'b0};
    assign pmod1_ready = pmod1_sel;
`else
    assign pmod1_read_value = 0;
    assign pmod1_ready = pmod1_sel;
`endif

Como programador me asusta la línea repetida, pero me parece más clara, así que ahí queda por ahora.

Cuando si está definido, como son los 8 bits menos significativos, se le ponen ceros. Luego, si ha sido seleccionado el valor de los pines, si no, ceros.


Fijate que ese XXX_read_value va a parar a un bruto OR, por eso la asignación debe estar condicionada:

assign mem_read_value = ram_read_value | leds_read_value | buttons_read_value | pmod0_read_value | pmod1_read_value | arduino_read_value | uart_read_value | timer_read_value | flash_read_value;
 

Parecido los XXX_ready:

assign mem_ready = ram_ready | leds_ready | buttons_ready | pmod0_ready | pmod1_ready | arduino_ready | uart_ready | timer_ready | flash_ready | mem_fault;

Luego, hay que copiar/pegar/editar leds o buttons según quieras leer o escribir.

En el caso de arduino con más de 8 bits, hay que implementar los cuatro ifs, es por que... mmh, luego voy a investigarlo mejor, me parece que se puede simplificar, va como tarea al backlog que tengo conectado a /dev/null.

Creo que finalmente, hay que mapear:

La idea sería algo como (quité algunos ceros para que no salte la línea):

32'b..._00000001_00000000_000011??: pmod1_sel   = 1; // 0x0001000c
32'b..._00000001_00000000_000100??: arduino_sel = 1; // 0x00010010

Esos dos ?? son dos bits, cuatro posiciones. Para los pmods sobra, para el arduino está ok.

Para 0x0001000c no hay nada en:

0x0001000d
0x0001000e
0x0001000f

En cambio para arduino, se usan los cuatro bytes (32 bits):

0x00010010
0x00010011
0x00010012
0x00010013

 

Luego en el programa:

#define PMOD1       *((volatile uint32_t *) 0x0001000c)
#define ARDUINO     *((volatile uint32_t *) 0x00010010)

En la carpeta programs hay... programas, ¿qué esperabas? El ejemplo más completo es buttons2ledsArduinoShiftPmodToPmod que no hace falta que te explique que los botones que aprietes se van a manifestar en los leds y que lo que pongas en un pmod saldrá por el otro y que en el conector arduino verás unos bits moviéndose.

 

El shift en el conector arduino
El shift en el conector arduino

El shift es medio raro por como armé el pinout, va en zig-zag. Y el pin 89 estába mal etiquetado, es 82, ya avisé.

 


 

Los switches que uso para el pmod tienen décadas, no andan del todo bien...

 

Si mirás el código en icicle.sv, verás que para leds y buttons estoy usando los parámetros LEDCOUNT y BUTTONCOUNT respectivamente, no así parámetros para los pmods ni arduino. Esto es por que no espero que tengan valores variables. De hecho, esos parámetros son una gentileza para quien quiera adaptar a las otras placas, la edu-ciaa-fpga tiene 4 y 4, listo.

Pude haber hecho bloques de 8 bits o 4 y así combinar inputs con outputs con facilidad y mejor aún llevar el concepto a dos módulos, ponele GPInput y GPOutput, o mucho mejor implementar el módulo GPIO, pero eso me desvía de mi proyecto, yo voy a usar los pines directamente, el archivo de defines lo voy a tener vacío. No quiero ni necesito que haya nada mapeado a la memoria ni que sea accesible desde el programa en ejecución.


El módulo GPIO

 

Este debería tener al menos una máscara de selección de sentido, quizás otra de enable/disable, lo mejor que se me ocurre es agrandar la distancia que hay en el casez (mem_address) entre direcciones y que luego el módulo termine de determinar si la interacción es con las máscaras o con los registros de datos. 

Desde el momento que no hay interrupciones, no vale la pena preocuparse por ellas.

Ya tengo un branch local con el código para pines inout, algún dia...

 

Conclusiones

 

He usado variables desde programas en C, ¿quién no? He usado funciones o macros provistas por el fabricante para configurar y acceder los puertos, magia. He visto que Vivado cuando le dás un dispositivo para usar desde la PYNQ te define direcciones de memoria donde va a estar. (dos seriales, pynq con acelerador, acelerador con serial, acelerador con leak).

Ahora he mapeado dispositivos a posiciones de memoria y lo he hecho de modo incremental.

Para ello, no me he tenido que preocupar mucho por el legacy, son cuatro programas.

Tampoco por unos bytes más o menos ni por el silicio que consuman.

Este proyecto es muy recomendable para poder comprender realmente estos conceptos y porqué a veces los registros están con un orden aparentemente razonable y otra veces no. Lo que te propongo es que te bajes el repo:

git clone https://github.com/ciaa/icicle.git

y te pares donde tomé yo:

git checkout f63f218

y te diviertas reconstruyendo mi camino o tomando el tuyo propio.


Uno de estos días cuando Pablo acepte mi segundo pull request, verás todo. O podés hacer trampa.


 





2021/09/09

H4CK3D 2021: leyendo los botones de la placa

Para entender el contexto, te recomiendo leer antes los primeros pasos.

Habíamos quedado en que necesitaba leer los botones y ya había spoileado que icicle no tiene implementado GPIO.

set_io clk       94
set_io leds[0]   1
set_io leds[1]   2
set_io leds[2]   3
set_io leds[3]   4
set_io leds[4]   7
set_io leds[5]   8
set_io leds[6]   9
set_io leds[7]   10
set_io uart_rx   55
set_io uart_tx   56

 

Lo primero que me llamó la atención es que tuviera en el archivo .pcf declarados 8 leds, considerando que la placa tiene 4. Tras consultar el pinout, no sé si soy corto o no está muy fácil de encontrar, y con el tester comprobé que de 4 a 7 están en un PMOD, ok.

Para ir entendiendo el panorama seguí a rx y tx de la uart, suponiendo que por ser de lectura y escritura me iban a contar algo, pero no. 

Miré el código en el programa, ok, esto me cuenta que los leds están mapeados en la dirección 0x00010000.

 

#define LEDS        *((volatile uint32_t *) 0x00010000)
#define UART_BAUD   *((volatile uint32_t *) 0x00020000)
#define UART_STATUS *((volatile uint32_t *) 0x00020004)
#define UART_DATA   *((volatile  int32_t *) 0x00020008)


Este caminito que te conté no es exactamente el que seguí, lo mío tuvo una trayectoria más cuántica y por más que intenté tomar nota, no deja de ser un relato un poco inventado.

Le cambié los nombres en el .pcf:

set_io leds[0]   1
set_io leds[1]   2
set_io leds[2]   3
set_io leds[3]   4
set_io pmod[4]   7
set_io pmod[5]   8
set_io pmod[6]   9
set_io pmod[3]   10

 

y si sabés más que yo anticiparás que me dió error, pues en algún lugar del HDL dice [7:0] leds y no [3:0] leds.

 

Warning: unmatched constraint 'pmod[4]' (on line 6)
Warning: unmatched constraint 'pmod[5]' (on line 7)
Warning: unmatched constraint 'pmod[6]' (on line 8)
Warning: unmatched constraint 'pmod[7]' (on line 9)
ERROR: IO 'leds[7]' is unconstrained in PCF (override this error with --pcf-allow-unconstrained)
ERROR: Loading PCF failed.
4 warnings, 2 errors
make: *** [arch/ice40.mk:24: top_syn.asc] Error 255

 

Decidí ir al último recurso que es ver la documentación y en el README.md dice que tiene:

 

Current Features
  • Memory-mapped UART and LEDs.

y que no tiene:

Planned features

  • Memory-mapped GPIOs.

 

Esto me cuenta que los leds no están implementados como GPIOs, que no hay manera de leer los botones con lo implementado.

Momento de pánico, casi sale mail al autor solicitando ayuda y a embebidos para proponer un grupo de trabajo, pero eran las 4:30 am, me dije que tenía que escarbar un poco más.

 

Me puse a hacer git greps y encontre'en icicle.sv este código:

/* LEDs */
output logic [7:0] leds,
...

casez (mem_address)
    32'b00000000_00000000_????????_????????: ram_sel = 1;
    32'b00000000_00000001_00000000_000000??: leds_sel = 1;
    32'b00000000_00000010_00000000_0000????: uart_sel = 1;
    32'b00000000_00000011_00000000_0000????: timer_sel = 1;
    32'b00000001_????????_????????_????????: flash_sel = 1;
    default:                                 mem_fault = 1;

endcase

 

Lo que hace es determinar según la dirección de memoria apuntada quien se va a encargar, ahora nos interesa leds_sel por que debe ser lo más parecido a los botones.

Luego tenemos el código de leds, si te fijás esto contrasta con el resto de los componentes, que no están tirados por ahí si no que tienen sus propios módulos.

logic [31:0] leds_read_value;
logic leds_ready;

assign leds_read_value = {24'b0, leds_sel ? leds : 8'b0};
assign leds_ready = leds_sel;

always_ff @(posedge clk) begin
    if (leds_sel && mem_write_mask[0])
        leds <= mem_write_value[7:0];
end

Tras algunas exploraciones un tanto indescriptibles, me puse a modificar.

En boards/edufpga.pcf agregué los pines, una mitad asignada a los botones, la otra a un PMOD, tal como esta con los leds.

set_io buttons[0] 31
set_io buttons[1] 32
set_io buttons[2] 33
set_io buttons[3] 34
set_io buttons[4] 11
set_io buttons[5] 12
set_io buttons[6] 15
set_io buttons[7] 16


En programs/hello/main.c asigné los botones a los leds:

#define LEDS        *((volatile uint32_t *) 0x00010000)
#define BUTTONS     *((volatile uint32_t *) 0x00010004)

while (1) {

  LEDS = ~BUTTONS;

}

En top.sv conecté los botones al exterior por el hecho de declararlos y luego los conecté a icicle:

module top(
    ...
    /* LEDs */
    output logic [7:0] leds,

    /* BUTTONS */
    input [7:0] buttons,
    ...
    );

    icicle icicle (
       .clk(pll_clk),
       ...
       /* LEDs */
       .leds(leds),

       /* BUTTONs */
       .buttons(buttons),
       ...
     );

 

En icicle.sv declaré los botones en la interfaz:


module icicle (
    input clk,
    input reset,
    ...    
    /* LEDs */
    output logic [7:0] leds,

    /* BUTTONS */
    input [7:0] buttons,
    ...
);


Conecté los botones a la salida y a la señal de que es leible:

    ...
    assign mem_read_value = ram_read_value | leds_read_value | buttons_read_value | uart_read_value | timer_read_value | flash_read_value;


    assign mem_ready = ram_ready | leds_ready | buttons_ready | uart_ready | timer_ready | flash_ready |
    ...

    
Agregué al selector de dispositivos por decirle de algún modo, la dirección de los botones:

...
logic leds_sel;
logic buttons_sel;
...
    
always_comb begin
  ...
  leds_sel = 0;
  buttons_sel = 0;    
  ...
  casez (mem_address)
      32'b00000000_00000000_????????_????????: ram_sel = 1;
      32'b00000000_00000001_00000000_000000??: leds_sel = 1;
      32'b00000000_00000001_00000000_000001??: buttons_sel = 1;
      32'b00000000_00000010_00000000_0000????: uart_sel = 1;
      32'b00000000_00000011_00000000_0000????: timer_sel = 1;
      32'b00000001_????????_????????_????????: flash_sel = 1;
      default:                                 mem_fault = 1;

  endcase


y finalmente la lógica, a continuación de la de los leds:

assign leds_read_value = {24'b0, leds_sel ? leds : 8'b0};
assign leds_ready = leds_sel;

always_ff @(posedge clk) begin
    if (leds_sel && mem_write_mask[0])
        leds <= mem_write_value[7:0];
end

logic [31:0] buttons_read_value;
logic buttons_ready;

assign buttons_read_value = {24'b0, buttons_sel ? buttons : 8'b0};
assign buttons_ready = buttons_sel;


    
Asombrosamente, si dejamos de lado algún que otro errorcito de sintaxis, me salió a la primera, nada mal considerando que nunca había visto System Verilog, igual no es muy distinto a Verilog.

Tiene un delay bastante apreciable y necesita un pulso de al menos XXX ms para que se active, no sé si soy yo o es inherente.


Ese pulso lo podría calcular de la siguiente manera, con un microcontrolador generar pulsos cuya longitud esté determinada por la lectura de un potenciómetro y que muestre en el puerto serial la longitud actual, mientras, conecto en el programa los 4 bits de los botones al los leds 5-8 que están mapeados al pmod donde miro con el osciloscopio si se activa o no y si hay ruido. A fines prácticos, estamos listos para continuar, no hace falta el experimento. 

Hay un montón de potencial, como por ejemplo implementar GPIO para poder desde el programa determinar qué es de entrada y qué de salida, tal como está ahora hay que tocar el HDL.

Cuesta verlo, pero tengo apretados los botones de los extremos, prenden los leds de los extremos.

 

Botones en acción
Botones en acción


 

De lo externo a la POC, esto era lo más difícil.

Paciencia, uno de estos días versiono y hago el pull request a ciaa/icicle.

 

H4CK3D 2021: configuración básica

Otra vez tengo el honor, privilegio, gusto, placer, como lo quieras llamar de exponer en H4CK3D, organizado por Securetia y la UP, donde me dicen "exponé lo que quieras", no sé si eso habla bien de ellos, yo no me daría tanta confianza, la vez que me dijeron eso en el trabajo para una charla interna, expuse sobre "chistes nerds", muy divertido pero a la gente más seria no le pareció tan simpático.

El año pasado mostré una POC de malware en hardware (empieza cerca de 0:14:00 y dura hasta 1:13:00), este año tambien, pero será otro "modelo". Esa vez fue un ejemplo basado en un ataque propuesto existente, afectar el bit supervisor según una pista que normalmente tiene poca activida pero una operación infrecuente artificialmente ejecutada de modo desproporcionado termina seteándolo.

La POC de este año será un poco más complicada, los detalles los iré agregando más adelante, esta entrada ya es bastante larga y no quiero spoilers.

Aunque falta bastante tiempo no puedo garantizar que pueda armar la POC, no estoy seguro de que ni sea factible ni que yo pueda realizarla, asi que propuse un Plan B, que fue aceptado.

Pero no, me he dicho, todo o nada, o voy a llegar con una POC exitosa o pondré la cara y explicaré los motivos de la falla. La posibilidad de un Plan B habilitaria el escenario de sucumbir a la desesperación y no esforzarme lo suficiente para lograr que funcione. Además, el Plan B que tengo es bastante triste, no cumple siquiera con mis más bajos standards.

El Plan B es el Plan A.

Mientras, voy a ir exponiendo el backstage, es un montón de tiempo y trabajo y deseo compartirlo, pero es muy heavy para la charla en sí, necesitaría como dos horas. Yo no, pero la mayor parte se aburriría hasta morir.

En esta entrada voy a sentar las bases, hacer funcionar el stack para desplegar en una EDU-CIAA-FPGA una CPU RISC-V RV32I con un programa en su memoria.

En las siguientes, las modificaciones necesarias para implementar la POC. Luego será el evento y finalmente como ya es costumbre, the missing pieces, con todo lo que me haya olvidado de poner en la presentación u olvidado de decir en el momento.

 

Sistema operativo


Una máquina virtual con virtualbox, Linux Mint 20.1 Mate, 2 cores, 8 GB RAM, 30 GB disco.

No respeté mi regla de usar más cores y memoria durante la instalación y volver a algo más reducido en el proyecto, debo haber perdido algunas horas. Tampoco le atiné al espacio de disco y tuve que desmalezar en algún momento. La memoria sobró en todo momento.

 

Stack


Hace meses había hecho unas pruebas sobre las instrucciones del sitio del proyecto, dí mi feedback pero no recuerdo nada, así que tomé como punto de partida la exposición de Rodrigo Melo de FOSS para FPGA, con su presentanción y repositorio, pero no, usa docker y vhdl. Si es tu primer contacto con la EDU-CIAA-FPGA o con FPGA, no desprecies este recurso ni el canal ni toda la info que tiene el proyecto, yo ya ví las charlas y algo sé de antes y tengo una idea para donde ir.

Luego, me apoyé en la exposición de Pablo Ridolfi, RISC-V con EDU CIAA FPGA, su presentación y repositorio, que es un fork de la CPU original adaptado a la placa. Cuando ví esta exposición supe que debía usar RISC-V en lugar de cualquier otra opción, ya volveré sobre eso en futuras entradas.

Me bajé también el repo de ejemplos, por las dudas.

Use este scriptcito para comprobar que había armado bien la jerarquía de repos, pues cuando empecé estaban tirados en el desktop, de paso repasamos un poco de bash:

find . -iname config | while read CONFIG; do
   echo -n "$CONFIG "
   grep url "$CONFIG"
done

No recuerdo en detalle la charla de Pablo, podría revisarla o leer el README.md y mejor aún el EDU-FPGA.md, prefiero tomar el camino más difícil para comprender mejor. Esto sale de la lista de dependencias:

 

  • GNU Make
    • ya viene instalado.
  • vim (for xxd)       
    • ya viene instalado, en realidad vi provee xxd, luego yo uso vim.

  • Icarus Verilog
    • git clone git://github.com/steveicarus/iverilog.git
    • sh autoconf.sh
    • ./configure
    • make -j2
    • 5 minutos
    • sudo make install
  • Project IceStorm or Project Trellis, vamos por icestorm
    • http://www.clifford.at/icestorm/
    • git clone https://github.com/YosysHQ/icestorm.git
    • make -j2
    • 3 minutos
    • sudo make install       
  • nextpnr or arachne-pnr, vamos por nextpnr
    • git clone https://github.com/YosysHQ/nextpnr
    • requiere que antes esté instalado icestorm
    • cmake -DARCH=ice40 -DCMAKE_INSTALL_PREFIX=/usr/local .
    • make
    • 17  minutos
    • sudo make install        
  • Yosys
    • git clone https://github.com/YosysHQ/yosys.git
    • make config-gcc
    • make
    • 45  minutos
    • sudo make install
  • GNU RISC-V toolchain
    • Esto merece párrafo aparte 

 

El repositorio tiene 6.5GB, pude haberlo bajado en lugar de clonarlo o instalar lo que viene con la distro con:

 

sudo apt install gcc-riscv64-unknown-elf

 

Tenía dudas respecto a 32/64 bits, pero por ahí dice:

"The multilib compiler will have the prefix
riscv64-unknown-elf- or riscv64-unknown-linux-gnu-,
but will be able to target both 32-bit and 64-bit systems."

 

Mala idea este camino, al ejecutar:

 

$ make BOARD=edufpga
icepll -q -i 12 -o 36 -m -f pll.sv
icebram -g 32 2048 > progmem_syn.hex
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -Wall -Wextra -pedantic -DFREQ=36000000 -Os -ffreestanding -nostartfiles -g -Iprograms/hello   -c -o programs/hello/main.o programs/hello/main.c
cp start-ram.s start.s
riscv64-unknown-elf-as -march=rv32i -mabi=ilp32  -o start.o start.s
cp arch/ice40-ram.lds progmem.lds
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -Wall -Wextra -pedantic -DFREQ=36000000 -Os -ffreestanding -nostartfiles -g -Iprograms/hello -Wl,-Tprogmem.lds -o progmem programs/hello/main.o start.o
/usr/lib/riscv64-unknown-elf/bin/ld: cannot find -lc
/usr/lib/riscv64-unknown-elf/bin/ld: cannot find -lgloss

 

Hallé en esta conversación de 2017 que libgloss no debería ser incluido, que newlib (uno de los submodules de riscv64)... suficiente, demasiado complicado, tras eliminar gcc-riscv64-unknown-elf, a clonar:
   

  • git clone --recursive \
    https://github.com/riscv/riscv-gnu-toolchain.git
  • ./configure
  • sudo make -j2
  • 130 minutos

      
¿perfecto? No, 30 GB no alcanzan para nada más, terminó y murio.

 

$ cd REPO
find . -type d -iname ".git" -exec du -sh {} \;
217M   ./gitlab/educiaafpga/ejemplos/.git
472K   ./github/ciaa/icicle/.git
4,0G   ./github/riscv/riscv-gnu-toolchain/.git
28M    ./github/steveicarus/iverilog/.git
464K   ./github/grahamedgecombe/icicle/.git
8,9M   ./github/rodrigomelo9/FOSS-for-FPGAs/.git
23M    ./github/YosysHQ/yosys/.git
44M    ./github/YosysHQ/yosys/abc/.git
2,8M   ./github/YosysHQ/icestorm/.git
11M    ./github/YosysHQ/nextpnr/.git



Revisando a manito, todos están en master o main, puedo hacer make clean y volar los .git en todos menos en los que pueda llegar a modificar algo. De un modo u otro recuperé 3GB y pude ir a ciaa/icicle y

 

make BOARD=edufpga

 
¡éxitos!

Si tomaras este camino y estuvieras con poco espacio, te recomendaría este orden:

  • GNU RISC-V toolchain, tras el install, borrar
  • luego
    • Project IceStorm
    • nextpnr, tras el install, borrar ambos
  • luego
    • Icarus Verilog, tras el install, borrar
    • Yosys, tras el install, borrar
  • luego
    • clonar icicle/o el mío

 

Veamos de grabar:

 

make BOARD=edufpga flash

Can't find iCE FTDI USB device (vendor_id 0x0403, device_id 0x6010 or 0x6014).

ufa! apuesto a que hay problemas de permisos y eso que Pablo seguro lo dijo... no, pero yo lo sabía:

 

$ groups
charly adm cdrom sudo dip plugdev lpadmin sambashare
$ ls -l /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Sep  7 18:14 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1 Sep  7 18:14 /dev/ttyUSB1

 

Se arregla muy fácil:

$ sudo addgroup charly dialout
[sudo] password for charly:             
Adding user `charly' to group `dialout' ...
Adding user charly to group dialout
Done.
$ su - charly
Password:
$ groups
charly adm dialout cdrom sudo dip plugdev lpadmin sambashare

 

en teoría desde el su es para no reiniciar la sesión entera

make BOARD=edufpga flash

...nada, lo peor es que con sudo si funciona, grrrrr... no es un problema bloqueante pero me ofende.

Logout/login... sigue fallando, mmmh, interesante, sólo falla iceprog, con gtkterm o con cat no hay problema:

$ cat /dev/ttyUSB1
Hello, world!

Hello, world!

Hello, world!


Un poquito de stackoverflow y solucionado, hay que agregar una regla para udev y reiniciar el servicio y seguimos practicando bash:

 

echo 'ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666"' |\
sudo tee -a /etc/udev/rules.d/70-lattice.rules 1>/dev/null

 

sudo service udev restart

Listo, igual aquí hubo algo que no terminé de entender, me parece que la clave es MODE:="666" y que ya he lidiado con este asunto pero no puedo recordar...


Probé salirme del grupo dialout y alcanza con lo de udev para flashear, pero necesitás los permisos para interactuar desde cat y gtkterm.


 

Hello World
Hello World

Ah, hablando de recordar, una trampa por si venías copiando, pegando y ejecutando antes de leer todo y sin pensar. Antes que nada hay que ejecutar:

$ sudo apt install gperf build-essential cmake \
   python3-dev texinfo vim libboost-all-dev tcl-dev \
   libreadline-dev libffi-dev
libeigen3-dev
 

Es lo que recolecté resultado de ir fallando en cada paso. La lista de Pablo es más generosa, pero yo quiero sólo lo indispensable para este proyecto y él además, estoy seguro que avisó que como venía probando, quizás sobraban cosas.

Si te faltaba espacio, probablemente los *-dev se puedan desinstalar luego de las compilaciones de las tools.

 

No requiere gran talento haber llegado hasta acá, sólo hice una libre interpretación de instrucciones bastante completas. Sin embargo lleva bastante tiempo, sirve para ir familiarizándose con las herramientas y es indispensable, es parte del camino no docker.

Ahora me queda pendiente leer un botón, en ciaa/icicle/boards/edufpga.pcf hay pines pero sólo de salida, seguramente me podré inspirar al ladito en icebreaker que está muy completo.

Tambien tendría que agregar algo que parezca un reloj, un divisor de frequencia conectados a unos leds, independiente de la CPU. No va a hacer falta un debouncer, ni por hardware ni por software, el programa no va a usar interrupciones para leer los botones ni le afectarán los rebotes.


Spoiler Alert: parece que icicle no tiene implementado GPIO!!!


2021/09/06

Cómo firmar documentos sin imprimirlos

Que quede claro que esta firma no necesariamente es legal, viene a ser como si lo imprimieras, lo firmaras y lo volvieras a digitalizar. Me imagino que como es la otra parte la que tiene que demostrar que vos firmaste, no es problema de uno.

No sé si le dá valor legal, pero si tomarás el resultado del proceso que luego expondré y le calcularas el hash, quizás sería más legal. Si además lo mandaras por mail, me parece que le suma puntos.

El proceso que voy a explicar es para el formato pdf, para word/libreoffice/similar es mucho más sencillo, sólo abrís el documento, llenás los textos y le pegás la firma elaborada según lo explicado luego.

El documento pdf se puede abrir con libreoffice writer, supongo que también con office, pero en el primer caso al menos puede haber reflujo de los textos y romperse.

Creo recordar que con Acrobat Distiller, no sé si sigue existiendo, también se puede hacer, al menos agregar texto, pero me parece que es pago y ni he buscado si hay para linux.

Lo primero y por única vez, es armarte la firma. Esta se construye a partir de una buena foto o scan sobre fondo blanco. Podrías usarla directamente así, dejando el fondo en blanco, pero necesitarías un rectángulo libre del tamaño rectangular de la firma en el documento a firmar y deberá coincidir el blanco tuyo con el del documento.

Mejor es convertir ese fondo en transparente. Se hace de modo muy sencillo con gimp, abrís la foto y:

Firma capturada
Firma capturada

 

  • select -> by color (en el fondo blanco)
  • color -> color to alpha
  • export as -> firma.png


Firma con alpha channel
Firma con alpha channel

 

Observá que el formato nativo de gimp es .xcf, no la imagen. Esto es porque gimp no sólo guarda la imagen, también puede tener un montón de información extra como layers, channels, paths y otras cosas.

Te conviene guardar esta firma, llamémosla original y derivar cada vez que firmes una con la medida apropiada, quizas ir salvando esas medidas para no hacerlo cada vez.

El siguiente paso es separar todas las páginas, pdfseparate provisto por poppler-tools es lo que yo uso.


$ pdfseparate original.pdf partes%d.pdf 

 

Luego identificar las páginas que hay que firmar. A cada una de estas páginas, abrirla con gimp y con la herramienta de texto llenar donde haya que llenar.

Finalmente, pegar la firma y overwrite.

Documento firmado
Documento firmado

Observá que el blanco de la firma no tiene conflicto con la línea de subrayado.


Finalmente, con pdfjoin volvés a armar el pdf.

$ pdfjoin parte01.pdf parte02.pdf

Esto te lo va a dejar en parte02-joined.pdf



2021/09/05

Jugando con PDFs

Hace mucho mucho tiempo, bue, ni tanto, 2017, resolví un problema, relacionado con la impresión de documentos, en particular de pdfs, dos páginas por hoja en modo landscape y doble faz.

¿Cuál era el problema? Que al doblar la hoja al medio se te rompe el orden de las hojas. 

cuadernillo
cuadernillo
 

Para resolverlo escribí un programita en C para que, dada una cantidad de hojas a imprimir y un comienzo, genere la secuencia que luego te sirve para usar en la impresora o en un convertidor de pdfs.

Por ejemplo, si querés imprimir las ocho primeras páginas en dos hojas, la secuencia es 8,1,2,7,6,3,4,5

 

Páginas por hoja y orientación
Páginas por hoja y orientación
 

Si quisieras saltearte las cuatro primeras y dárselo a la impresora, a la que le dijiste que imprima en doble faz y dos páginas por hoja: 12,5,6,11,10,7,8,9

 

Páginas y orden
Páginas y orden
 

Este recurso medio que ha dejado de servirme, en parte por imprimo cada vez menos y en parte por que veo cada vez menos...

Claramente esto sólo sirve para impresiones, carece completamente de utilidad en un pdf para leer, pero es útil quizás tener un pdf listo para imprimir en estas condiciones. Se puede usar la impresión a pdf o convert y un poco de bash:

for PAGE in 8 1 2 7 6 3 4 5; do
   ORDER="ORDER page_$PAGE.png "
done
convert $ORDER output.pdf

Ojo con que no hayan espacios en los nombres de las imágenes.

No generalicé el problema a imprimir en una o más de dos hojas porque... no recuerdo, quedó ahí, la lógica hardcodeada.


Hace menos tiempo, a ciertas personas relacionadas, les ha ocurrido que han necesitado imprimir, fundamentalmente apuntes fotocopiados que vienen de a dos páginas por hoja y desean tenerlo en una página por hoja, ya sea impreso o en un pdf, más cuando hay rotaciones. Lo contrario a lo anterior.

Para ello elaboré unos scripts utilizando pdf2ppm que en Linux Mint/Ubuntu está provisto por poppler-utils. Lo que hace pdf2ppm es extraer de un pdf cada página pedida a una imagen png, tanto entera como un región.

La idea entonces es tomar una hoja, obtener las medidas y luego partir por la mitad, ya sea vertical u horizontal según la orientación del documento de entrada. Luego ir extrayendo de cada página cada hoja.

Si mirás los scripts en github, podrás apreciar que hay dos extracciones:

# extract half of the pages
pdftoppm -x 0 -y 0 -W "$WIDTH" -H "$HEIGHT" -png input.pdf page0

# extract the other half
pdftoppm -x "$WIDTH" -y 0 -W "$WIDTH" -H "$HEIGHT" -png input.pdf page1


Luego, podría haber alguna rotación, para ello hay que agregar  

# rotate pages
DIR=90
for PNG in $(ls *png | sort  ); do
  convert -rotate $DIR $PNG $PNG
  DIR=$(( $DIR * -1 ))
done

No llega a ser una FSM, se arregla con el truquito de multiplicar por -1. Fijate que el orden de la salida del ls es el natural, a diferencia de la generación del orden:

INPUT_PNG=$(ls *png | sort -t "-" -k 2 )

Esto es por que hay dos lotes de páginas, el orden está dado por los números a la derecha del "-".

Relacionado con el cuardernillo, hay que reordenar. Lo más sencillo que se me ocurrió fué renombrar los archivos, esta vez sí usando una FSM:

# resort pages
STATE="invert0"
for PNG in $(ls *png | sort -t "-" -k 2 ); do
  case $STATE in
    "invert0")
      mv $PNG tmp
      PREV="$PNG"
      STATE="invert1"
    ;;
    "invert1")
      mv "$PNG" "$PREV"
      mv tmp "$PNG"
      STATE="skip0"
    ;;
    "skip0")
      STATE="skip1"
    ;;
    "skip1")
      STATE="invert0"
    ;;
  esac
done

Y eso es todo, si surge algúna nueva complicación, cuando la resuelva la agregaré al código. Te anticipo que cualquier complicación debe ser regular, esto es, si hay páginas rotadas arbitrariamente la resolución deberá ser manual.




2021/09/04

Notificación de claves

En el marco de la cursada de una diplomatura de desarrollo seguro en UNSTA donde soy docente también, alguien avisó que:

 

Reporte inicial

 

Bueno, no es tan terrible, no es una credencial transacccional, es para facilitar el onboarding, hay mucha gente que si le complicás la cosas no va.


Pero, está claro que dista de lo ideal, se me ocurren un montón de mitigaciones, pueden ser superpuestas o incoherentes, sin ningún orden en particular, apoyándome en la información que tengo hasta ahora

  • Avisarle al usuario que entre y cambie la contraseña lo antes posible, disminuís el tiempo de exposición y ya le estás enseñando algo.
  • Forzar el cambio de crendencial al entrar. Con ello disminuís el tiempo de exposición y de paso cumplís con una buena práctica.
  • Enviar la clave en otro mail, le permite al usuario borrarlo sin perder el link original, le estás tirando la responsabilidad al usuario.
  • Enviar la clave con fondo del mismo color, al menos protege de shoulder surfing. Shoulder surfing es que te saquen información mirando por encima del hombro. Debería existir alguna expresión para la pérdida de información debido a mostrarla en pantalla al compartirla en una videoconferencia. De hecho, he fallado y varias veces.


Hasta aquí, medidas de seguridad pura, contemplando un poco la usabilidad y sin prestar atención a las necesidades de negocio.

Para replicar el escenario, fuí al sitio:


Situación TLS
Situación TLS, excelente


Luego me convertí en asistente a una charla y tener más información del contexto, veamos:


Me inscribí, me preguntó lo de siempre, incluso ingresar la clave, mmmh, interesante, en el reporte no decía que la clave la habías ingresado vos, se deducía que la había generado el sistema para vos.


Ok, te pide una contraseña más fuerte que la que autogenera firefox. Ahora ya sabés parte de mi contraseña: ****************-

Ufa, tiene errores de usabilidad, se olvida de mi país y las preguntas si/no, tras varios intentos debido a que no le atinaba a agregar el "-" a la clave casi desisto... esta es una denegación involuntaria de servicio.


Hice logout y falló la redirección, ahí noté, no lo había hecho antes, que es un wordpress. No voy a ejecutar un wpscan pues ya estaríamos entrando en el terreno de un Ethical Hacking y si le pido permiso a ML no voy a terminar nunca esta nota que comenzó como un comentario en un grupo de whatsapp. Además es trabajo y ahora no estoy trabajando.

Mmmh, peor aún, no puedo cerrar sesión, voy a compartir esta nota con ML. Para continuar, voy a entrar en modo incógnito.


Auń no me llegó el mail que inició todo esto, un problema pues no anoté la clave, voy a cambiarla... y sí, me autocompleta la clave. Mirando muy rápido con devtools no veo que esté el valor a la vista, pero si ha circulado, con burp o más paciencia lo encontraría si está, quizás sólo sea que firefox la recordó por que le pedí, sólo voy a cambiarla, no me la toma. Ni dice ok ni se manifiesta al entrar por el login.

Voy a recuperar... pruebo con un usuario inexistente y no puedo ver el mensaje pues se ha roto en maquetamiento, pero es:


No existe un usuario con este nombre de usuario
o correo electrónico. Por favor verifica tus datos
e intenta de nuevo
.


Mal, esto es un oráculo, fuga de información.


El mail de recuperación es un link, parece ok, pero se ha roto completamente la interfaz, ya se está convirtiendo en trabajo. Hasta acá llegué, no me toma la nueva clave. Más que problemas de seguridad, hay problemas previos de calidad. La cosas primero deben funcionar, luego ser seguras.


Volviendo al problema original, no lo pude reproducir. De ser así indicaría que la clave podría estar siendo almacenada en lugar de hasheada.


Voy a reportar... no funciona el "sobre" (about), me puse a buscar en ML si había algo donde reportar... suficiente, es sábado AM, tengo muchas cosas que hacer, me está tomando más trabajo reportar que lo que ya hice, si vos sabés a quién avisar, hacelo por favor.


Y esto nos lleva nuevamente al asunto original, habiendo supuesto una clave autogenerada que te la envíe. Podría mandártela por otro canal, esa es un buena excusa para pedirte el teléfono y mejorar su base de datos de usuarios/clientes.