Quantcast
Channel: Seguridad Agile
Viewing all 264 articles
Browse latest View live

Teclado Hexadecimal USB: Versión 1 funcionando

$
0
0

Venís de la intro y un mini POC.

 

Tras mirar bastante el código  de PS2Keyboard, que es la clase que lee el teclado PS2 y observar el comportamiento de Keyboard, que es la clase que se hace pasar por un teclado USB, he logrado comprender bastantes cosas, empezando por las limitaciones.

                           [PS2Keyboard]
                               /   \
  PC <-- USB -->[Keyboard] Teensy   <-- ps2 --> Teclado
                               \   /
                               [ps2]


Usando PS2Keyboard y Keyboard, ya tengo un segundo prototipo, que funciona con lo básico, pero no puedo hacer que me funcionen las demás teclas como las flechas ni el NumLock.
 

PS2Keyboard

PS2Keyboard es de más alto nivel, toma las secuencias de código que provienen del teclado y las convierte en caracteres de 8 bits. Mapea las teclas especiales como "print screen" y "capslock" a un código arbitrario.

Se usa asi:

 if (keyboard.available()) {
    // read the next key
    char c = keyboard.read();

Si oprimis la tecla "4", recibís un "4" (ascii 52 o 0x34).

Tocando el código de la librería:

[$ARDUINO_FOLDER/hardware/teensy/avr/libraries/PS2Keyboard/PS2Keyboard.cpp]

 

     while (1) {
           s = get_scan_code();
           if (!s) return 0;
           if (s == 0xF0) {
                   state |= BREAK;
                   Serial.println("BREAK");
           } else if (s == 0xE0) {
                   state |= MODIFIER;
                   Serial.println("MODIFIER");

 

Si oprimís "4", en el serial emitís un BREAK, si oprimís flecha arriba, MODIFIER, MODIFIER, BREAK, pero para consumir unos caracteres inútiles por ahora.

 

Toqué un poco en el .h:

[$ARDUINO_FOLDER/hardware/teensy/avr/libraries/PS2Keyboard/PS2Keyboard.h]

// Every call to read() returns a single byte for
// each keystroke.  These configure what byte will
// be returned for each "special" key.  To ignore
// a key, use zero.
#define PS2_TAB                      9
#define PS2_ENTER                   13
#define PS2_BACKSPACE                7
#define PS2_ESC                     27
#define PS2_INSERT                  65     // 0
#define PS2_DELETE                  68     // 127
#define PS2_HOME                    66     // 0
#define PS2_END                     69     // 0
#define PS2_PAGEUP                  67     // 25
#define PS2_PAGEDOWN                70     // 26
#define PS2_UPARROW                 11
#define PS2_LEFTARROW                8
#define PS2_DOWNARROW               10
#define PS2_RIGHTARROW              21
 

 

 Y con eso tengo las letras A-F en donde quería.

 

 La versión actual queda:


include <PS2Keyboard.h>

const int DataPin = 5;
const int IRQpin =  3;

PS2Keyboard keyboard;

void setup() {
  keyboard.begin(DataPin, IRQpin);
  delay(1000);
}

void loop() {
 if (keyboard.available()) {
    char c = keyboard.read();
    Keyboard.print(c);
  }
}

 

El resto

ps2

ps2 es de bajo nivel, recibe sin interpretar cada código desde el teclado:

  for (;;) { /* ever */ 
    code = kbd.read();
    Serial.println(code, HEX);

Si oprimís la tecla "4", recibís:

6B
F0
6B

Aparentemente 6B es 4-keypress y F06B es 4-keyrelease

Si oprimís flecha arriba:

E0
75
E0
F0
75

Afirmaría que E075 es upArrow-keypress y E0F075 upArrow-keyrelease.

A ver, qué pasa si hago:

  • oprimir 4  
  • oprimir flecha
  • soltar flecha
  • soltar 4

6B -> 4 keypress
E0
75 -> flecha keypress
E0
F0
75 -> flecha keyrelease
F0
6B -> 4 keyrelease

Bien, si ahora oprimo el 4 y espero, aparece el 6B, si no me apuro en soltar, se llena de 6B. Ya está bien clarito, llegó el momento de mapear todo...

 


key presskey release
printscreenE012E07C



E0F07CE0F012
scroll lock7E






F07E



pauseE11477E1F014F077





backspace66






F066



num lock77






F077



insertE070





E0F070


homeE06C





E0F06C


page upE07D





E0F07D


deleteE071





E0F071


endE069





E0F069


page downE07A





E0F07A


up arrowE075





E0F075


left arrowE06B





E0F06B


down arrowE072





E0F072


right arrowE074





E0F074


esc76






F076



/E04A





E0F04A


*7C






F07C



-7B






F07B



76C






F06C



875






F075



97D






F07D



+79






F079



46B






F06B



573






F073



674






F074



169






F069



272






F072



37A






F07A



enterE05A





E0F05A


070






F070



.71






F071



Intuyo que si pudiera activar el NumLock, las teclas marcadas en verde enviarían otros códigos.


El código referenciado está en

$ARDUINO_FOLDER/libraries/ps2/ps2.h

$ARDUINO_FOLDER/libraries/ps2/ps2.cpp

 

Próximos pasos

Tengo que examinar Keyboard para ver si puedo enviar esos códigos directamente o tengo que hacer alguna conversión y quizás prender y apagar el LED. En otras palabras, que el teclado funcione de modo transparente salvo los reemplazos de teclas objetivo del ejercicio.



Teclado hexadecimal USB: una pausa para los aspectos físicos

$
0
0

Como he logrado un éxito parcial y mi teclado presenta las funciones básicas, augurando sino un éxito completo al menos esquivar el fracaso, ya podría ir prestando atención a como voy a poner el Teensy dentro del gabinete del teclado, para lo cual debo considerar los siguientes aspectos:


  • Debe entrar en el lugar que tengo pensado sin que toque al circuito existente.
  • Debe exponer al exterior el conector.
  • Debe quedar accesible el botón de reprogramación sin necesidad de abrir el gabinete.
  • Debe quedar fijo, tal que al conectar el cable USB o al oprimir el botón de reprogramación no se suelte.

 

Lo que no tendré en cuenta pues supera mi capacidad y tiempo, es el tema de interferencias electromagnéticas y la disipación de la temperatura. Con respecto a lo primero, apostaré a que no pase nada y a lo último, tendré la precaución de que dejar espacio entre el chip y el gabinete.

 

Extra: quitar los pines

 

Por ahora nada me ha llevado más tiempo y producido más frustración que quitar los pines. Primero, intenté con paciencia quitar el plástico de los pines, para contrarrestar el efecto "la unión hace la fuerza", cada pin por separado es evidentemente más sencillo que la tira completa. Nada, estaba muy firme, le dí con el disco de corte de torno de mano a un lado y pude quitar todo el plástico.

Para desoldar los pines, tuve que desarrollar una técnica que comparto:

En lugar de calentar la soldadura, calentar el pin desde el lado contrario, se puede hacer con dos a la vez para compensar los movimientos, la punta del soldador queda trabada. Cuando comienza a moverse y recién en ese momento, con una pinza lo retirás. No toques con la pinza antes pues se enfría.

 [foto]

Pese al método, tuve un brote de impaciencia y perdí uno de los through holes, por suerte el de GND, hay otro por ahí.

 

Colocación del Teensy


Para medir el lugar pude haber hecho un esfuerzo intelectual y con un calibre medir con gran precisión, pero me agarró un cierto infantilismo y me pareció más pintoresco poner un poco de plastilina debidamente protegida con un plástico y poner el circuito en su posición, para que ésta se aplaste y tome la medida del hueco:


Plastilina antes
Plastilina antes

 
 
Aún así tenía la esperanza de poder usar el calibre, hace pocos meses adquirí uno mediocre pero de metal tras haber pasado casi treinta años de haber perdido el excelente que tenía, pero no hizo falta medir, a ojo se vé claramente que sin los pines la plaquita entra holgadamente y al estar levemente separada por el conector del plástico, cumple quizás con una leve ventilación:
 
 
Plastilina despues
Plastilina despues
 


Salida para el conector


Para exponer el conector al exterior un lamentable y desprolijo orificio alcanza. 

 

Acceso al botón de reprogramación


Otro triste agujero, con el beneficio extra de que al entrar la pieza en éste, produce una traba que va a servir para que al conectar el cable la placa se mantenga en su lugar.


Botón de reprogramación
Botón de reprogramación


 

La fijación


Había pensado usar cuatro pines y "clavarlos" en el plástico del fondo pero ya viste que lo mío no es el trabajo de precisión, va con la pistola plástica adhesiva.


 

Extra: periférico

 

En otro cajón tengo un touchpad cuyo rescate y adaptación a USB me ha servido de base para esta experiencia. Tuve la fantasía de quitarle las teclas de las flechas y ponerlo ahí, pero es una atrocidad y le quita lo retro. Luego pensé en una especie de cajoncito y que salga por un costado, pero sería frágil y desprolijo. Me quedé entonces con la idea de una ampliación, poner el touchpad en algún gabinetito copado y hacelo conectable.

Ya me conozco y si voy por ese camino no voy a terminar nunca. Lo que hice entonces es dejar un conector RJ-1, el del teléfono, que era lo que tenía a mano y tiene cuatro cables. El cajón de la basura sigue rindiendo frutos, alguien dijo "las noticias de hoy envolverán el pescado de mañana", yo puedo decir que la basura del ayer encontrará conmigo su destino, en algún momento.

No bien terminé de poner la masilla en el gabinete del teclado me dí cuenta de que pude haber puesto un conector ps/2, con lo cual me serviría para conectar cualquier legacy, pero bueno, ya pasó, si lo necesito fabricaré el cable adaptador.

Queda para cuando implemente el programa más completo del teclado incorporar el touchpad, no creo que implique ninguna dificultad.

Siguiendo el manual, ya dejo soldados los cables en los pines apropiados.


BoardData PinClock Pin (IRQ)
Teensy 3.6
Teensy 3.5
Teensy 3.2
Teensy 3.1
Teensy 3.0
Any
except 13 &
analog-only
pins
Any
except 13 &
analog-only
pins
Teensy LCAny
except 13
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 14, 15, 20, 21, 22, 23
Teensy 2.0Any, except 115, 6, 7, 8
Teensy 1.0Any0, 1, 2, 3, 4, 6, 7, 16
Teensy++ 2.0Any, except 60, 1, 2, 3, 18, 19, 36, 37
Teensy++ 1.0Any0, 1, 2, 3, 18, 19, 36, 37


Interior final con puerto para ampliación
Interior final con puerto para ampliación


 

Retoque final


Un poco de LEM y no te digo que nuevo, pero al menos parece limpio. 

Final
Final


En lo físico lo único que me ha faltado es conectar el switch de AP/X al Teensy para... no sé para qué, cualquier cambio de modo lo voy a hacer apretando varias veces NumLock.


Y aquí tenés la prueba de que funciona:

0123456789ABCDEF






El gusanito revive en un trabajo práctico: intro

$
0
0

El gusanito


Hace unos años me sentía frustrado por que personas menores que yo con mejor motricidad obtenían mejores resultados en el juego Slither.io pese a yo tener una mejor visión estratégica, fenómeno que tambien ocurre con UrbanTerror y con Fornite y me imagino que con cualquier otro video juego de tiempo real.


Con respecto a Urban Terror tengo una idea que en algún momento concretaré, pero relacionado a Slither.io pasé de la idea a la práctica y llegué bastante lejos, te resumo lo que hice antes:


El gusanito va hacia donde esté el cursor del mouse, lo cual a mi me complica pues tengo que ver el escenario y a la vez dónde está el cursor.


Slither.io
Slither.io


Por la naturaleza de la interfaz, que viene a ser rotativa como el Gyruss, me parece que conviene usar un controlador de tipo Paddle y más o menos lo implementé con un encoder rotativo y una placa EDU-CIAA-NXP tal que se hace pasar por un mouse.

A medida que el encoder gira, el programa transforma en movimientos de mouse tal que hacen una circunferencia pero no terminé de hacer la matemática y algún otro problema tuve y le ganó otra prioridad y cayó en el olvido.

Todo eso está mucho más largo en:


Slither - 1 - Intro
Slither - 2 - encoder con edu-ciaa-nxp
Slither - 3 - usb mouse con edu-ciaa-nxp
Slither - 4 - usb mouse y encoder con edu-ciaa-nxp

 

El trabajo práctico


En la materia Introducción a los Sistemas Embebidos del CEIoT se nos encomendó desarrollar una aplicación tal que:
  1. Debe emplearse la placa NUCLEO
  2. Puede ser una aplicación diferente (no necesariamente domótica)
  3. Puede utilizar los sensores vistos o agregar los nuevos
  4. Debe usar al menos una entrada analógica
  5. Debe usar comunicación serie
  6. Realizar la temporización del sistema basada en interrupciones y uso de timers

La placa NUCLEO es la que estamos usando para todos los ejercicios

Los sensores vistos, al momento de escribir esto fueron botones, sensor de gas vía i2c, potenciómetro y sensor de temperatura vía entrada analógica y supongo que luego veremos teclado matricial y sensor de movimiento al menos.

La verdad es que aunque no trabajo de esto, ya tengo bastante experiencia y sólo el punto 6 me resulta un desafío, así que podía inventarme una aplicación boba que cumpliera para poder aprobar o buscarme algo más difícil para aprender y ahí recordé al gusanito, me aferré al segundo requerimiento y pensé en cómo rescatarlo para el trabajo práctico.

 

Una reflexión muy personal

 

Como alumno, uno puede estar cursando algo por el conocimiento o la certificación, esto último se traduce en la aprobación y si querés entrar en detalles, la nota. Yo soy partidario de cursar, hacer trabajos prácticos, laboratorios y rendir exámenes por el conocimiento y que la certificación sea un efecto colateral.

En esta materia en particular, la verdad verdad es que voy a profundizar el conocimiento, pues ya he tomado varios cursos que más o menos cubren lo mismo y he practicado mucho por mi cuenta. Aunque no me interesa mayormente, en términos objetivos el resultado más importante de esta cursada resulta ser la certificación.

Como docente, entiendo que distintas personas sepan más o menos del tema de antemano y tendría la tentación de pedirle a cada una más o menos según su condición. A mi y a los ingenieros electrónicos que asisten les pediría más que a los estudiantes de sistemas, que pueden estar en el horno por que todo es nuevo.

Y podría hacerlo en términos de acompañar su aprendizaje, pero no en términos de certificación, no le puedo pedir más a quien más sabe, pues estaría discriminando y o forzando mi visión sobre como debe ver la vida esa persona. De todos modos para otorgar la certificación lo que importa en última instancia es lo que la persona sabe, no si lo acquirió ahora o antes.

Entonces, si alguien ya sabe todo, para aprobar le pediría lo mismo que a quien entra sin saber nada. Digo "pediría" pues por más que soy docente aún no he logrado por falta de experiencia y agravado por lo remoto poder discernir claramente cuáles alumnos saben más que otros.

Ok, pero cuando se trata de mí, hace mucho que no soy mero alumno, acompaño al docente y soy docente de mi mismo, entonces, si tengo tiempo, debo elevar la vara, tal como hice con el trabajo práctico de la reciente materia Basic Digital Design


Volviendo al trabajo práctico


Lo que me he propuesto es:

  • Usar la placa NUCLEO (requerimiento 1).
  • Un encoder rotativo para determinar el giro (requerimientos 3 y 6).
  • Un potenciómetro para el radio de giro (requerimiento 4).
  • Un potenciómetro opcional para la resolución (requerimiento 4).
  • La conexión serie para implementar algún protocolo de mouse (requerimiento 5).

El requerimiento 2 más que un requerimiento es una libertad, lo cumple la aplicación misma.

Los problemas

Encoder rotativo

Lo del encoder rotativo ya lo había resuelto antes, tengo que ver si me sirve y quizás corregir. Tambien agregar los potenciómtros.

¿Qué es lo de la resolución? Si jugás un rato observarás que si ponés el cursor bien cerca del bicho a ojo diría que tenés como unos cinco grados de resolución, si lo ponés bien lejos, un grado. El problema es que el encoder rotativo que tengo es de unos 10 grados. Lo que puedo hacer es que no tenga, no sé como se dice, ¿la misma relación de giro? O sea, esos 10 grados se traduzcan en un solo grado.

Lo puedo lograr por sofware, claro, o con mecánica, introduciendo un componente extra con un engranaje que haga la reducción. Comprenderás que esto está muy alejado de mi torpeza, no será así.

Para el trabajo práctico me quedaré con los 10 grados pero quizás agregue un potenciometro para cambiar la escala, veremos.

Radio de giro

Esto tiene que ver con la resolución pero no en grados, en el perímetro recorrido.

Sobre este tema y la resolución me falta reflexionar e investigar bastante y probablemente quede fuera del trabajo práctico tal profundidad y me contente con algo básico

Comunicación serie

Podría negociar con la cátedra usar USB, que es más difícil que lo que voy a hacer, pero la realidad es que ya lo hice con la EDU-CIAA-NXP y mientras exploraba encontre que existe inputattach que permite recibir por comunicación serie un protocolo de mouse o teclado y conectarlo como si fuera tal dispositivo, soporta un montón de protocolos

Me dije entonces, ha llegado la hora de aprender a hacer ingeniería inversa de protocolos seriales de mousee y como probablemente sea el punto crítico, será el primer tema a resolver, a publicar en unos días o si fracaso, enterrar en el olvido algunos años más hasta que reviva con otra excusa.



mbed.os desde línea de comando

$
0
0

Pasos para compilar y debuggear un proyecto mbed desde línea de comando.

 

En el marco de la cursada y el trabajo práctico de la materia Introducción a los Sistemas Embebidos del CEIoT y apoyándome en un documento privado de Sergio Burgos que explica los caminitos para eclipse y STM32CubeIDE, estas son las instrucciones para compilar y debuggear la placa desde línea de comando.

 

¿Qué es mbed os? 

 

mbed os es un sistema operativo de tiempo real para procesadores Cortex-M, que tiene un simpático entorno web de programación muy completito. Lo estoy usando en esa materia para programar una placa NUCLEO-F429ZI de ST con un montón de periféricos, seguí el link si te interesa el detalle.

 

Compilar con gcc

 

Desde tu entorno de mbed exportá el proyecto con toolchain Make-GCC-ARM:


Exportar...
Exportar...

...como Eclipse-GCC-ARM
...como Eclipse-GCC-ARM


Mirá que un blinky pesa 37 MB... ok, esto no es un bare metal, es un sistema operativo entero.

Te deja un comprimido, descomprimilo...

Tomá el Makefile y comentá las siguientes líneas:

ASM_FLAGS += -include$
ASM_FLAGS += /filer/workspace_data/exports/...


o ejecutá el siguiente comando:

sed Makefile -i -e 's/^ASM_FLAGS += -include$//' \
-e 's/ASM_FLAGS += \/filer\/workspace_data\/exports\/.*//'


Instalá con el gestor de paquetes de tu distro o bajate el compilador y openocd.


Si no instalaste con el gestor de tu distribución, buscá donde está dejaste el compilador y agregá su carpeta bin al PATH, por ejemplo para el compilador:

export PATH=$PATH:/home/$USER/bin/gcc-arm-none-eabi-9-2020-q2-update/bin/


Ejecutá make, si te aparece un error como este, probablemente sea por tener un gnu-arm viejo, volvé dos casilleros para atrás:

../mbed-os/platform/cxxsupport/mstd_type_traits:1123:12: error: 'std::is_trivially_copyable' has not been declared  using std::is_trivially_copyable;

Me ocurrió con gcc-arm-none-eabi-4_9-2015q1.

Un ratito despues, varios minutos si es la primera vez, tenés una carpeta BUILD de 55 MB con el resultado,

link: Blinky.elf
arm-none-eabi-objcopy -O binary Blinky.elf Blinky.bin
===== bin file ready to flash: BUILD/Blinky.bin =====
arm-none-eabi-objcopy -O ihex Blinky.elf Blinky.hex



Nos interesa el .bin para grabar en la placa mediante arrastrar a la carpeta que se abre cuando conectás la placa y el .elf para debug.

cp BUILD/Blinky.bin "/media/$USER/NODE_F429ZI"

Si empezás a agregar archivos, vas a tener que tocar el Makefile. Por ejemplo si tuvieras una carpeta con módulos y en esta uno nuevo llamado "alarm":

OBJECTS += modules/alarm/alarm.o

INCLUDE_PATHS += -I../modules/alarm


y opcionalmente

ASM_FLAGS += -I../modules/alarm

 

debug con openocd y gdb: el camino difícil

 

Creá el archivo de configuración en la raiz del proyecto:

cat << EOF > stlink.cfg
source [find interface/stlink.cfg]
source [find target/stm32f4x.cfg]
reset_config srst_only srst_nogate
EOF


Y hasta aquí llegué con el documento. De openocd no sé nada, pero intuyo que si le doy a ese archivo de configuración y lo mismo que eclipse hace por defaut, debería funcionar.

El problema es que eclipse usa pyocd, que aparte de parecer el nombre de un antipiojos, debe hacer lo mismo que openocd.

Como configuración de referencia tengo la del firmware de la edu-ciaa-nxp https://github.com/ciaa/firmware_v3, arrancando por el Makefile:

TARGET=$(OUT)/$(PROGRAM_NAME).elf
OOCD_SCRIPT=scripts/openocd/q

# DEBUG with Embedded IDE (debug)
.debug:
        @echo DEBUG
        $(Q)$(OOCD) -f $(OOCD_SCRIPT) 2>&1

# DEBUG with Embedded IDE (run)
.run: $(TARGET)
        $(Q)$(OOCD) -f $(OOCD_SCRIPT) &
        $(Q)socketwaiter :3333 && arm-none-eabi-gdb -batch $(TARGET) -x scripts/openocd/gdbinit


Siendo el contenido de gdbinit:


target remote :3333
mon arm semihosting enable
mon reset halt
load
continue
quit


Me intriga mucho el socketwaiter

socketwaiter
socketwaiter



La otra es la de la carpeta eclipse-extras, en particular los archivos:

NUCLEO_F429ZI_Blinky_debug.launch
NUCLEO_F429ZI_Blinky_debug_pyocd_settings.launch

que son muy muy parecidos si los mirás con kdiff3.


Tendría que hacer el mapeo entre los valores de eclipse y la sintaxis de la ciaa. O mejor, aprender a usar openocd, una de las mil tareas pendientes que tengo.

¿Por qué, Charli, no te instalás el eclipse, usas su debugger y te dejás de joder? Es que pregunté en un chat si alguien tenía solucionado esto y mientra me llega la respuesta, o me pongo a investigar o me voy a ver la tele y a comer de modo indebido.

Podría instalarme el eclipse y terminar de ver como se manifiestan esos valores de los archivos o incluso ver si me tira pistas en la consola.


¿Por qué, Charli, no buscás la documentación de mbed a ver que dice respecto a openocd? Buena idea, dice, adaptando a mis ubicaciones:

openocd \
  -f /home/carlos/bin/openocd-0.10.0/tcl/board/stm32f429discovery.cfg \
  -f /home/carlos/bin/openocd-0.10.0/tcl/interface/stlink-v2-1.cfg \
  -c init -c "reset init"



se puso a titilar led de programación y además en la terminal apareció lo que debía, vamos bien. Le digo cual es la imagen y le pongo un par de breakpoints:

(gdb) file BUILD/slitherUSBMouse.elf
Reading symbols from BUILD/slitherUSBMouse.elf...done.

(gdb) break Debouncer::checkFall
Breakpoint 2 at 0x800c394: file ../classes/Debouncer.cpp, line 27.
(gdb) break Debouncer::fall
Breakpoint 3 at 0x800c438: file ../classes/Debouncer.cpp, line 44.


y ahora? run?

Don't know how to run.

Revisando como se hace con edu-ciaa-nxp:

(gdb) target remote localhost:3333
(gdb) file BUILD/slitherUSBMouse.elf
(gdb) continue

 

Breakpoint 1, Debouncer::rise (this=0x20006698 <_main_stack+2672>)
at ../classes/Debouncer.cpp:37
37       if (! rising) {

Funciona, recordá que había puesto unos breakpoints, ahora a pelear con el programa. 

Me falta explorar algunas opciones que ví por ahí y no presté atención, como:


  • -c "set mem inaccesible-by-default off"
  • La posibilidad de incorporar las opciones a algún archivo.
  • Meter todo en el Makefile poder hacer make debug, lo cual resolvería el nombre de programa automáticamente.

 

debug con openocd y gdb: el resultado fácil


Ok, necesitás lo resultados y no cómo transitar el proceso:

openocd \
  -f $OPENOCD_PATH/tcl/board/stm32f429discovery.cfg \
  -f $OPENOCD_PATH/tcl/interface/stlink-v2-1.cfg \
  -c init -c "reset init"

En otra ventana:

arm-none-eabi-gdb

(gdb) target remote localhost:3333

(gdb) file BUILD/prog.elf

(gdb) breakpoint class::method

(gdb) continue



El gusanito revive en un trabajo práctico: Parte 2: el modelo choca con la realidad.

$
0
0

Habiendo fracasado rotúndamente en usar inputattach en la "Parte 1: inputattach" que ni siquiera he podido publicar, he aceptado rebajarme a usar USBMouse.

 

Esta placa es de 3.3 v "tolerante a 5v" en los pines marcados como FT en la hoja de datos, o sea, prácticamente todos. Siendo que el encoder usa 5v, me facilita la vida, la parte analógica siempre me asusta.


Como en otras tantas ocasiones, lo que voy a contar no es la crónica exacta de lo que transité ni necesariamente respetaré el orden de lo que hice, pues te aseguro que te resultaría completamente incomprensible. En su lugar voy a hacer una especie de collage significativo.

El la introducción hay más contexto.

 

Conectar un botón y que se comporte como botón izquierdo


Quizás un poco contrario al Diseño Emergente, como ya tenía una idea intuitiva de hacia donde pensaba ir, no hice la implementación más sencilla que funcione, si no que medio que me anticipé.

Lo que hice fue usar una InterruptIn y consideré el rebote mediante un Timeout, tal que si llega una transición, espere un ratito y compruebe un momento despues en qué estado quedo y en caso de corresponder recién ahí actúe.

Como actuar es pedirle al Mousepress() o release(), eso no lo puedo invocar en el Callback de InterruptIn o Timeout pues esos métodos probablemente usan un mutex, cosa que no se puede hacer en un Interrupt Handler y además no es buena práctica ejecutar mucho código durante una interrupción, se lo doy a una EventQueue y en algún momento lo hace y aparentemente funcionó muy bien.



setup
setup


 


click
click

 

Estos diagramas están en el repo en la carpeta doc, necesitás astah para verlos.

Dicho de otra manera, tenemos una clase genérica Debouncer<> que recibe un objeto y dos de sus métodos para rise() y fall(). Cuando Debouncer recibe por ejempo un rise(),  le pide a Timeout que llame a un método propio checkRise() que verifica que esté risen y encola una llamada a otro método propio notifyRise() que va eventualmente a llamar al método asociado al objeto provisto.

 

Conectar el encoder rotativo

 

Un encoder rotativo tiene dos pines, que van diciendo 00->01->11->10 si lo girás para un lado y lo inverso si es para el otro, usando un Gray Coding. Si estuviste prestando atención, no podés dejar de notar que es como si estuvieses haciendo clicks en dos botones, asi que si asocio dos InterruptIn a un mismo objeto pero con cuatro métodos, en teoría debería funcionar.

Fijate, esta es la instanciación de un click izquierdo asociado al botón azul de la placa:


Debouncer<MouseClick> leftButtonDebouncer(
   BUTTON1,
   &queue,
   &leftButton,
   &MouseClick::press,
   &MouseClick::release
);


y esto para el Encoder. objservá que es el mismo objeto encoder:

Debouncer<Encoder> clkEncoderDebouncer(
   PC_9,
   &queue,
   &encoder,
   &Encoder::clkUp,
   &Encoder::clkDown
);
Debouncer<Encoder> dtEncoderDebouncer(
   PC_8,
   &queue,
   &encoder,
   &Encoder::dtUp,
   &Encoder::dtDown
);

De hecho, funciona bastante bien usando una leve adaptación de la lógica del ejemplo de uso de USBMouse (en ./mbed-os/drivers/USBMouse.h), pero...

 

Muy excéntrico e irreversible
Muy excéntrico e irreversible

En negro es el giro para un lado, ponele que es aceptable, pero cuando invertís, se destroza. Esto tiene que ver con pequeños errores que debería tratar con un conocimiento intermedio de trigonometría y análisis matemático que me falta, así que lo voy a resolver mediante ingenio. Además, no me quiero anticipar pues prometí que mi camino iba a ser instructivo a costa de la verácidad, hay un error con el tratamiento de los tiempos y los estados de las interrupciones, luego veremos.

Va mi primera POC, qué pasa si en lugar de calcular posiciones según los ángulos, dado que el encoder tiene 30 posiciones precalculo los desplazamientos. Comenzaré con sólo cuatro y girando siempre para el mismo lado.

 

cuatro puntos
cuatro puntos

Ahora para una lado y para el otro, para pescarle la lógica.

 

ambos sentidos de giro
ambos sentidos de giro

En amarillo en el código la modificación para invertir el sentido, en el dibujo un leve movimiento del cursor del mouse para evidenciar que es correcto.

Ahora resta precalcular para 30 puntos.


30 pasos ambos sentidos
30 pasos ambos sentidos

Ese leve desplazamiento a la derecha es para diferenciar un sentido del otro.


30 pasos ambos sentidos
30 pasos ambos sentidos

Ya sé, ya se que le falta un montón de optimizaciones como calcular un solo cuadrante y usarlo de modo espejado, pero la verdad es que a esta placa le sobran bytes y ciclos de reloj, va a quedar así.

Aparte, el STEPS en realidad es 60, pues se generan dos interrupciones por paso.

 

Links útiles


https://os.mbed.com/blog/entry/Simplify-your-code-with-mbed-events/

El código está versionado y corresponde al commit "math fixed, still excentric"

Hackathon Global 2021

$
0
0

En mi trabajo hay cada tanto hackathones, en este caso de utilización de IA y otra vez me ha tocado ser jurado. ¿Por qué jurado? Porque hay un gran proyecto de capacitacion interna llamado "proyecto ninja" dentro del cual está encuadrada la misma competencia y como estoy cerca de la cima tengo este derecho, privilegio u obligación, como lo quieras ver.

¿Por qué jurado? Me sirve bastante ver todos los proyectos propuestos, son varios paises con diversidades, son tecnologías y metodologías de vanguardia que se están explorando y adoptando gradualmente.

¿Por qué no participante? El rol de participante no me resulta muy satisfactorio, pues es una experiencia agotadora y ya estoy medio viejo, las veces que he participado lo he hecho con equipos que han aceptado:


  • venimos a aprender
  • sin expectativas de ganar
  • no vamos a matarnos

Quizás lo de viejo no sea tan importante, ya tenía ideas parecidas hace... bastante.

Para contrastar, he sabido de personas que en un hackathon pretérito han alquilado una habitación cerca del lugar del evento para pasar la noche programando. Ese mismo equipo se sintió muy estafado cuando quedaron cuartos entre cinco, siendo los quintos evidentemente nosotros.

No digo que esté mal, obvio, no pensamos todos igual, pero mi sed de victoria es muy moderada.

Si no fuera por el cansancio, preferiría siempre ser participante, cada tanto viene bien exponerse a la presión, incluso hubo un hackathon en el que fuí jurado por que no pude armar equipo, no por mis condiciones sino por falta de gente.

 

Con respecto a esta experiencia en sí, tengo algunas notas para compartir, algunas útiles sólo para quienes participaron de este evento, pero la mayoría espero para cualquiera en cualquier otro hackathon.

 

Para los equipos participantes


  • Alguien debe velar por los aspectos técnicos, como controlar el tiempo y silenciar a quien no esté hablando, quizás deberia comenzar la última persona en exponer y que tome la posta la primera al finalizar su parte.
  • Recordar que no siempre quien más sabe ni que tiene la capitanía es la mejor persona para exponer.
  • Para evitar retroalimentación de audio, o silenciarse o usar auriculares. Podría ocurrir que como fuente de sonido este configurado el audio mismo en lugar de sólo el microfono, en ese caso seria necesario silenciarse.
  • Cuando hay fallas como las de gestión del silencio o algo de la demo, no dar explicaciones ni pedir disculpas, eso come tiempo y la lástima no da puntos.
  • Recordemos que en las presentaciones no es buena idea poner mucho texto.

Este es el resumen de fallas y su frecuencia sobre seis equipos:

  • Mala gestión del silencio: 2
  • Texto excesivo: 2
  • Mala gestión del tiempo: 3
  • No mostrar la aplicación: 2 (casi 3, un equipo preguntó al comienzo si hacian demo) 

Las dos últimas afectaban los criterios de calificación de modo explícito, las dos primeras son más subjetivas.

Si tuviera que hacer una única recomendación, sería:

Hacer algunos ensayos de la presentación con personas ajenas


Notas de color


Uno de los equipos propuso acceder al home banking mientras se maneja. Acá es ilegal interactuar con un dispositivo mientras se conduce un vehículo, no sé si en algún lugar del mundo no lo es.

No le bajé puntos a este equipo por este motivo por que, bueno, quien usa dispositivos mientras conduce no sólo es negligente, imprudente y falto de respeto por la integridad y vida tanto propia como ajena, sino que está enferma, no lo puede evitar.

No, mentira, no estaba en los criterio de evaluación... ¿o si?, ¡oh, no! ¡Me equivoqué!, uno de los criterios decía:

"Es viable llevar a cabo la idea desde un punto de vista económico, legal, de sostenibilidad e incluso moral"

¡ufa!, si me hubiese dado cuenta los hacia moco.


Otro equipo propuso un sistema de recompensas, en el cual si se detecta que alguien podría por cortar la relación, acercarle algun incentivo para que se quede, también recompensar a quien manifieste tal satisfación que se detecte (no recuerdo si dijeron exactamente cómo) que sería "evangelizador". Si se trata de una medición meramente declarativa, o sea, sin relacionar a eventos reales, esto incita al fraude. Esta técnica no es para nada novedosa, ¿quién no ha llamado al cable a solicitar un descuento esgrimiendo un me voy para otro lado?

 

Para la organización

 

  • Habría que tener otro meet que haga de recepción, algunos integrantes entraban antes de tiempo por respetar demasiado el horario o equivocarse.
  • Los formularios con las calificaciones deberían enviarse al final todos juntos, esto tiene que ver con...

 

La esencia de esta nota


Hay un problema al calificar que ya me había ocurrido antes en otras calificaciones en las que participé, es un proceso que conlleva un cierto aprendizaje.

Hay criterios que son bastante absolutos, como por ejemplo si se ha respetado el tiempo de exposición o si se mostró una demo. Pero otras son medio relativas, como si la idea representa algo de valor o incluso medio intermedias como si se cumple con el enunciado del ejercicio.

Esta última te puede parecer rara, pero cuando el tema es sobre como en este caso la aplicación de IA a ciertos problemas, si uno no es experto en IA y en esos ciertos problemas, quizás necesites ver varios ejemplos hasta terminar de comprender qué cumple y cuánto cumple.

Para la vez anterior y esta repetí desarrollé la técnica que paso a relatar:

Al primer equipo le puse 7 en todos los aspectos relativos.

A los equipos siguientes, ajusté lo mismo, más o menos mediante comparación.

Tuve una dificultad con el criterio de uso del tiempo, el primero lo hizo bien y le puse un 10, ya que me parecía un criterio absoluto, pero cuando ví al segundo me dí cuenta que debí haberle puesto 9 o dicho de otra manera, el problema es si a alguien inicialmente le ponés un 10 y luego aparece un posterior merece un 11.

¿Por qué 7 y no 5? Si ponemos 7 tenemos mas libertad para ir para abajo, como que estamos apostando a que el primero no lo hizo tan mal.

Una optimización si estás en mejores condiciones de calificar, sería usar 7 o 5 pero ya aplicado una cierta calificación, como si el primer equipo en realidad fuera el segundo.

 

Para graficar la situación base:

  • criterio relativo 1: 5
  • criterio relativo 2: 5
  • criterio relativo 3: 5
  • criterio relativo 4: 5
  • criterio absoluto 1: 10

y la optimización:

  • criterio relativo 1: 6
  • criterio relativo 2: 4
  • criterio relativo 3: 5
  • criterio relativo 4: 6
  • criterio absoluto 1: 10

 

De un modo u otro, no importa si los puntos que le estás dando parecen bajos, es justo en relación a los otros equipos ya que no estás sesgando, pero si hubiera habido un equipo al cual el resto del jurado le puso 10 y vos le hubieras puesto también 10, o sea, el equipo al que mas valoraste, le vas a manchar ese 10.


Obviamente, al adquirir experiencia, este recurso es un poco menos necesario.

Mientras escribo esto, descubro que he cometido un importante error metodológico, con una muy leve influencia en los resultados, al punto que casi cancelé la nota, pero me parece que lo que puede aportar ésta tiene más valor que ocultar mi error. Sólo es detectable si sos insider,  a ver si te das cuenta...




Para estudiar Designing Video Game Hardware in Verilog

$
0
0

El libro cuenta la evolución inicial de los video juegos, desde la nada de un generador de barrido y unos simples circuitos digitales hasta una CPU de 16 bits, pasando por sprites por hardware y video juegos hechos completamente sin CPU.

Para hacer los ejemplos tiene una plataforma de emulación online excelente https://8bitworkshop.com/ muy muy buena, que permite no tener que saber casi nada y concentrarse en la esencia del libro que no es FPGA ni Verilog.

Si hiciste la transición de programación (no)estructurada a objetos, esta es la inversa, partís de objetos, la pelotita del pong son unos circuitos, no unos valores abstractos en memoria.

 

Ejemplos

 

Como en otras ocasiones, hago la adaptación a Vivado 2018 con Nexys4ddr y algún bonus si se me ocurre. Uso testbenchs, que el framework de la plataforma ofrece implícitamente. Todos los ejemplos que están para CRT y algunos para VGA a 25MHz. Yo hago todo con VGA, así hay algunas adaptaciones extra.



Todo versionado en github.

 

Capítulo 7, clock divider

 

Divisor de reloj
Divisor de reloj



Sin dificultades, salvo que hay que inicializar los valores de los divisores para que funcione en el emulador.

 

Capítulo 8, binary counter


Contador binario

Nada especial, con elegir en el bus de salida del contador "waveform style" -> "analog" se consigue esa formita de dientes de sierra.

 

Capítulo 9, video sync generator

 

Acá hay que usar VGA@25Mhz en el selector de plataforma y usar estilo "analog" en vsync y hsync.



hsync se vé, vsync apenas
hsync se vé, vsync apenas



hsync no se distingue, pero si siete frames en vsync
hsync no se distingue, pero si siete frames en vsync

 

Capítulo 10, test pattern 

 

La magia del framework provisto resuelve la frecuencia del clock de base, hay que meter un divisor para la realidad. Siendo de 25MHz y mi placa 100MHz aunque me llevó un rato darme cuenta, con dividir el reloj por cuatro estamos ok, así que tomo prestado el prescaler que ya había tomado prestado de antes y miro que las frecuencias estén ok o pruebo directo con el monitor de descarte que ya está bastante roto de todos modos.

Tiene que dar

  • vsync 57.8 Hz
  • hsync 30 kHz

Capítulo 11, digits


Desde mi punto de vista este es el más importante pues hace algo que no termino de entender y a la vez hay que adaptar a VGA, por que lo que sale en la pantalla apesta:

Klingon
Klingon


Tuve que pasar de tres bits (RGB) a seis bits (RGGGGB) para prender al máximo el verde. La evolución fue:



assign GREEN   = display_on && bits[~xofs];


Siendo GREEN el pin VGA_G[0]

Luego, cambié a los nombres del xdc y asigné de modo repetido

assign VGA_G[0] = display_on && bits[~xofs];
assign VGA_G[1] = display_on && bits[~xofs];
assign VGA_G[2] = display_on && bits[~xofs];
assign VGA_G[3] = display_on && bits[~xofs];


y finalmente lo hice bien:

assign VGA_G = {4{display_on && bits[~xofs]}};

Y post finalmente volví a RED, GREEN, BLUE y generé un módulo de adaptación, al cual tambien moví los pines para ver hsync y vsync en el osciloscopio, así queda limpio el módulo.


  assign HS_probe = hsync;
  assign VS_probe = vsync;
  assign VGA_R    = {4{RED}};
  assign VGA_G    = {4{GREEN}};
  assign VGA_B    = {4{BLUE}};


Y me facilita levemente el próximo paso, que es ejecutar una simulación y su salida pasarla por un programa que me muestre el render, pues esto de estar regenerando todo lleva mucho tiempo.

De paso, me ocurrió que comenzaron a haber errores incomprensibles relativos a los constraints, seleccionando explícitamente el top module se corrigió.

 

Retroceso táctico

 

Para diagnosticar mejor me propuse simular y no me funcionaba para nada la simulación. Entonces volví al ejemplo anterior, tampoco.

La corregí agregando un bloque initial donde puse todos los reg a cero, pero no en el testbench sino en el módulo mismo, video_sync_generator.v.

Teniendo entonces un ejemplo que funciona y su simulación tambien, le puse un $strobe para obtener en ???.sim/sim_1/behav/xsim/simulate.log
los valores y poder hacer un render por software.

 always @(*)
   $strobe("%b %b %d", simul_hsync, simul_hvync, GREEN );


Son cerca de 30 MB por frame.


Lo que necesito es un programita que lea el log y me muestre lo que hubiese aparecido en la pantalla, ya sea un frame o una secuencia de frames, incluso que pueda conectarlo a tail -f y verlo en vivo.



El programa es muy sencillo, toma el standard input y  lo parsea y lo comento en VGA Renderer

 

Capítulo 11, digits, otra vez

 

Teniendo el renderer la situación es esta:


VGA_G undefined
VGA_G undefined


Esas franjas verde claras son XX:


tail -F .../simulate.log | tr "x""7" | wish render.tcl

Mirando la waveform:


En rojo VGA_G undefined
En rojo VGA_G undefined




Comento de paso con respecto al VGA Renderer que lo bueno de usar tail en lugar de un archivo es que se puede ir cambiando la simulación y para patrones repetitivos como lo que ha habido hasta ahora se hace render parcial:


Resultado de ir cambiando los bits de xofs
Resultado de ir cambiando los bits de xofs

Si fuera por la adaptación de resolución de CRT a VGA la simulación online debería estar ok, no? No sé como poner el waveform para este ejemplo, así que lo simulé localmente y...

Green undefined en el original
Green undefined en el original



...hay XXs en el ejemplo original.

No logré entender cómo arreglarlo ni si tiene relación con el resultado real, por el momento sigo con el próximo.

 

Capítulo 11, bitmaps

 

En la simulación anda ok salvo que sigue generando undefineds, todo el fondo verde claro.


Mal fondo
Mal fondo


En la FPGA, horrible, ni vale la pena mostrarlo.


En https://8bitworkshop.com/v3.4.2/?platform=verilog-vga&file=chardisplay.v

 

Capítulo 12, Ball Motion

 

Falló horriblemente, me rendí un poco, dejé de hacer la adaptación y me concentré en comprender.

 

Final


Uno de los temas trata de una CPU de 8 bits que se programa con su propio assembly, claro, con el compilador en el simulador online, no recuerdo si había manera de compilar afuera. Luego una CPU de 16 bits, etc, mirá el libro, vale la pena.

Llegue hasta el capítulo 28 creo y abandoné un año, lo retomé y sorprendentemente entré en sintonía muy rápido y terminé el libro.

Algo muy interesante es que sugiere utilizar el icestick para los ejemplos, lo cual implica que la edu-ciaa-fpga es más que suficiente. Con respecto al video el clock le dá si usas (si conseguís) un CRT, pero para VGA se complica.

 

Este termina básicamente con dos frases:

In our simulator, control inputs are simply bit values sent to a module. The real world is a bit more messy.

Y otra que no tengo la paciencia de buscar parecida:

Al trasladar a una FPGA real pueden haber errores de sincronización que el emulador ignora.

¡Qué bueno que abandoné en ese momento! Sabiendo este detalle, dejo de confiar ciegamente en el código del libro y uno de estos días (meses, años) lo retomo.












Primeros pasos en la toma de control de una Commodore 128D

$
0
0

Sucia pero...
Sucia pero...
 

De chico era un marginal, en lugar de tener una Commodore 64 como cualquier hijo de vecino, tenía una Atari 130 XE, debido a seguir un consejo técnico. Esto me dejó fuera de sintonía con el resto de mis conocidos, en un nicho donde era difícil conseguir software y afectó de modo profundo e impredecible qué soy hoy, supongo que si no quedé afuera del juego fue porque tuve acceso a una IBM PC muy poco tiempo despues por un pariente. De hecho, estoy escribiendo con el teclado Modelo M de 1987 de esa máquina.

La misma persona que me dió este consejo y otro más que tambien falló (comprar un microprocesador 6809E en lugar de un 6809) sin embargo fué decisiva en mi rumbo y formación, me hizo mi primera fuente 200 AC a 12 y 5 DC, me regaló mi primer osciloscopio, me ayudó a elegir y comprar mi primer tester, me explicó el funcionamiento del ULA de la sinclair y fuzzy logic y muchas más cosas que no puedo identificar, asi que nada tengo que reclamarle, nadie es perfecto.

Esa "E" en 6809 significa que el reloj necesita unos componentes externos para funcionar y eso produjo un primer impedimiento en que lo hiciera funcionar que nunca pude superar, sigue en mi lista de asuntos pendientes desde ya hace 35 años.

El consejo técnico debe haber venido por el lado de que la Atari tenía 128KB como la C128, corría a 1.8Mhz en lugar de 1 Mhz de la C64, debía ser más barata que la C128 y yo le debo haber aportado que se veía mucho más linda, cosas de chicos, pero un hecho indudable:

Atari 130XE
Atari 130XE gana

Commodore 128
Commodore 128 parece más seria

Commodore 64
Commodore 64 no llega a ser horrible...


Conclusión, ninguna novedad,  las decisiones puramente técnicas (y estéticas) no suelen ser las más apropiadas, hay que tener en cuenta otros factores, como la facilidad para quien intenta aprender algo, la disponibilidad de herramientas y una comunidad que le acompañe.

Quince años después de haberme desecho de la Atari 130XE, estaba en la feria del Parque Los Andes y veo algo raro, una C128D, que viene a ser una C128 pero en un gabinete con disketera y el teclado separado.

Es el mismo mother, incluso el teclado no es externo, está externalizado, tiene la misma interfaz paralela como si estuviera adentro, un cable de como veinte hilos.

Si mirás la entrada de la wikipedia, verás que en realidad parece  que se trata de una C128DCR, Cost Reduced, el gabinete metálico en lugar de plástico y el ROM de 1986, me falta abrirla para ver si tiene el chip de video 8568 que sería el tercer factor de confirmación.

La conecté y funcionó ok, fantástico, la almacené. Tiempo después me hice un ratito y la conecté a 220 V siendo de 110 V.

Ahora, quince años después, no recuerdo bien el contexto pero podría fijarme en el chat que tuvimos, un compañero de trabajo, pasabamos por temas nostálgicos cuando le conté de esta máquina y me alentó a hacerla funcionar.

Siguiendo su indicaciones, me fijé que los capacitores estuvieran ok. Le conté que recordaba haber visto un fusible intacto y cuando estaba en ello reparé en otro fusible, bien cerca de la entrada de alimentación, nada intacto. Debía estar tan ofuscado y desmoralizado en el momento del error que no reparé en este.


El fusible era el de abajo a la derecha
El fusible era el del centro abajo a la derecha
 

Me llevó dos horas netas a lo largo de varios días encontrar los adaptadores. El primero, el que además es selector de entrada, apareció rápido y lo que es notable es que en este momento no recuerdo donde lo encontré. El segundo tenía que estar en un frasco con los splitters, atenuadores y fichas de cable, como corresponde, pero nada, no lo podía hallar y cuando apareció ese frasco, no estaba. Por suerte, cuando ordené originalmente mantuve una cierta disciplina y en el mismo cajón estaban unas antenas de TV y estaba conectado a una de ellas.

La conecté con gran pánico y encendió el led de arranque, prendió y apagó el de la disketera, vamos bien. Conecté a la entrada de TV con unos mínimos sufrimientos y acá está:

 

Primer arranque
Primer arranque

Esas letritas raras tras el ready se deben a que tenía el keylock apretado, son caracteres gráficos.

Tras palpar un poco determiné que en gran medida la baja calidad se debe al adaptador selector.

 

Adaptadores
Adaptadores

 

¿Qué se puede hacer con esto?


Primero limpiarla un poco.

La verdad verdad es que no me interesa para jugar, para eso están los emuladores y si en todos estos años no los he usado, algo debe significar. Tampoco programarla, para eso están los emuladores y otro montón de otras arquitecturas más actuales y provechosas.

 

Video


Tengo que encontrarle un propósito y como lo que más me interesa ahora es FPGA y gracias a DVGHV aprendí mas o menos a lidiar con señales VGA, lo que me resultaría provechoso e interesante es tomar la señal y superponer la pantalla en una ventana, utilizando la placa PYNQ que tiene entrada y salida HDMI. Debería tomar cada cuadro de la C128 y superponerlo en algún lugar encima de lo que venga de la computadora por HDMI, tipo como eran las primeras placas aceleradoras 3D.

Para esto no necesito cargar ningún programa, sólo descifrar cual de las tres interfaces me conviene, tiene olor a megaproyecto, es más de lo que estoy dispuesto a hacer en estos días.

 

CP/M vía diskete

 

Me produce una atracción perversa arrancar en modo CP/M, pero esto requiere cargar desde floppy. Esta es mi cadena de dependencias:

  • Comprobar que desde PC se pueden formatear y grabar diskettes para Commodore.
  • Conseguir una fuente AT.
  • Armar una máquina con disketera de 5 1/4 con el resto de la basura que tengo, es que hace un par de años tiré demasiadas cosas.
  • Obtener la imagen de CP/M.
  • Obtener el sistema operativo y software para grabar el disco.
  • No sé si la disketera funciona.

 

CP/M vía emulador de disketera


Tengo un camino mejor y que representa ingenio a diferencia de lo anterior que es sólo copiarse de esfuerzos ajenos: descifrar como con un microcontrolador simular una disketera 1571 o 1581.

  • Obtener la especificación de comunicación.
  • Implementarla en un microcontrolador.
  • Obtener la imagen de CP/M.
  • Ponerla en la eeprom del microcontrolador.
  • Servirla en el momento apropiado.

Hay menos factores librados al azar. Pero debe ser increiblemente difícil, por más que los detalles de bajo nivel los debe estar resolviendo el microprocesador 6502 de la disketera.

Este proyecto no es novedoso, hay varios modelos dando vueltas por ahí, incluido un emulador para Raspberry Pi 3 que no descarto utilizar.

Para cargar otros programas, tengo otras posibilidades adicionales.

 

Carga vía cartridge

 

Este es bastante interesante, usar un microcontrolador que se haga pasar por un cartridge:

  • Obtener el pinout, protocolo y timing.
  • Implementar la emulación con el microcontrolador
  • Obtener algún programa
  • Ponerlo en la eeprom del microcontrolador
  • Conectar 

Con un ATMega328p me parece que no alcanza aunque corra a 20Mhz. Quizás si podría con la placa NUCLEO que vengo usando en un trabajo práctico y corre a 180Mhz, más que es tolerante a 5V, pero tendría que ver bien, anticipándome y viendo EasyFlash3 y C64-Pi parece que entramos en terrenos de FPGA. De un modo u otro me parece que me queda grande.

 

Carga vía emulador datassette


Conseguir archivos guardados en cassettes en formato wav o mp3 y meterlos no por el datassette que tengo, que no sé si funciona sino desde la placa de sonido:

  • Identificar la entrada de audio.
  • Comprobar que tenga un nivel electrónico apropiado para la salida de una compu o mejor aún un reproductor de mp3, como puede ser un celular viejo.
  • Armar algún tipo de conector.
  • Obtener algún programa.
  • Ponerlo en el reproductor
  • Cargarlo.

Este es de los menos interesantes pero quizás esté a mi alcance y prefiero terminar esta nota habiendo hecho algo concreto, no sólo planes que quién sabe cuándo y sí los ejecutaré.

Y ya empezamos mal, no sé por qué pensé que iba a ser como con la Spectrum que podías usar cualquier reproductor de cinta. No logro encontrar documentación, lo mejor es la existencia de un adaptador llamado C2N232 que permite hacer que una PC se haga pasar por el datassette mediante una conexión RS-232.

Si tomara ese camino el único mérito sería portar el firmware a un ATMega328p, que no sería poco porque está escrito en unas 700 líneas de assembly, mmmh.

Quizás estoy cometiendo el error de pensar que por que la comunicación es analógica, tal como indica sound input/output en el pinout del cassette de MSX tomado de un foro que hallé mientras buscaba algo que desambigüe el sentido de CASSETTE READ/WRITE, lo que me llevó al foro correcto.


Pinout MSX
Pinout MSX

Llevaré adelante el siguiente experimento, cuyo objetivo es determinar si la interfaz entre la CPU y el datassette es digital o analógica.

  1. Identificar la entrada y la salida de datos.
  2. Desarmar el datassette para poder medir sin armar conector.
  3. Encontrar un cassette.
  4. Escribir un programa
  5. Salvarlo
  6. Capturar la señal
  7. Analizarla

 

1 Pinout

+----+----++----+----+----+----+
| A1 | B2 || C3 | D4 | E5 | F6 |
+----+----++----+----+----+----+

 

Pinout Datassette C64
Pinout Datassette C64

 

2 Punto de medición

 

Interior Datassette
Interior Datassette

Con conector
Con conector

 
Mesa de trabajo
Mesa de trabajo

3 Cassette

 

Listo

 

4 Programa

 

10 A=A+1
20 PRINT A
30 GOTO 10

 

5 Salvarlo

 

SAVE "ONE",1,2

 

Proceso de grabación completo
Proceso de grabación completo

De paso observá que se ve muy bien, esos adaptadores y el cable de audio RCA evidentemente nada aportaban, le soldé una ficha RCA a un cable coaxial, RG11 o similar, el patrón de interferencia es de la cámara del celular. Es un motivo menos de preocupación, se veian unas rayitas horizontales muy cortas que en mi experiencia con PC indicaban fallo de la memoria de video, ahora no están más.

 

6 Capturas

 

Captura completa salvo...
Captura como 35 segundos completa salvo...


...el comienzo, donde se ve la bajada del sensor
...el comienzo, donde se ve la bajada del sensor



Todo igual...
Todo igual...

Detalle pulsos
Detalle pulsos


Para ver si ganaba información capturé apretar play/stop sin intervención de la compu, nada.

Play Stop
Play Stop

 

Ampliación de las zonas marcadas en orden cronológico:


Inicio sensor y motor
Inicio sensor y motor

Pulsos iniciales de lectura
Pulsos iniciales de lectura

Pulsos finales de lectura
Pulsos finales de lectura

No se si habrás observado, el violeta que es 6.2 V no regulados, no sé si lo que cáe es normal, a mirar el datassette por dentro.

Le conecté 4 V regulados y el motor anduvo, aún bajando hasta 3 V, no sé si a la velocidad apropiada, falla al rebobinar. Esto es un problemilla, C128 no está proveyendo el voltaje o la corriente apropiada. 

Volví a conectarlo a la C128 y obtuve resultados distintos, no hay señal salvo unos ruiditos del datassette a la C128. De paso capturé dos programas de distinta longitud:

Programa corto
Programa corto



Programa largo
Programa largo
Si te fijás bien, verás que los bloques del medio son distintos.

 

7 Análisis

 

No funcionó la grabación en sí, ya que parece que la señal "cassette motor" no es suficiente, me falta electrónica para lidiar con esto, supongo que debería poner un transistor que aumente la corriente.

La C128 ni se enteró, tras el save desactivó el video unos segundos, demasiados quizás. Luego dijo "ok". Esto significa que si quiero capturar, tengo que poner "cassette sense" a tierra.

No hubo ninguna manifestación de cambio, ni en el pin de ida ni el de vuelta, sólo esa señal cuadrada con período 380 uS, 2.64 khz, 400 uS, 250 khz en el segundo intento. Esto es bueno, la señal parece digital, con un microcontrolador debería poder capturarla con un GPIO y reproducirla luego.

El microcontrolador candidato preferido es el ATMega328p debido a manejarse con 5V, pero tiene muy poca RAM.

Como no puedo leer, no sé si la reproducción debería ser igual a la grabación, voy a apostar a que si. O quizás sería buena idea buscar algún manual. Si alguien que sabe me quiere tirar alguna pista, no me ofende.

Prometo que si hago algo más lo publicaré, lo que pasa es que esta nota es producto de mi alta procrastinación.



 




 





 

 







Notas del curso Advanced Linux: The Linux Kernel

$
0
0

Max CPUs de s390 y powerpc
Max CPUs de s390 y powerpc

 

Preparándome para regular la velocidad de un cooler en una Raspberry PI para reducir el ruido, tema recurrente en mi vida, gracias a mi trabajo que me dá LinkedIn Learning, tomé algunos cursos, también como parte de mi campaña de recertificación y reaprendizaje continuo.

En el caso de Advanced Linux: The Linux Kernel, buena parte del curso ya la sé, pero siempre hay detalles nuevos y necesarios refrescos.

Comparto algunas cositas que me parecieron interesantes y algunas de mis pruebas.

 

El operador |&

 

Este no lo conocía, te junta stderr en stdout, fantástico.

 

view

 

Es una versión de vim en modo readonly, ¡que estúpido! llevo como veinticinco años mirando archivos con less que no tiene highlight, vos quizás pienses que lo estúpido es usar vim/view, cuestión de gustos y origen de varias flame wars.

 

initramfs


En el curso dice que el initramfs se abre con cpio y quizás gunzip, pero no, escarbando un poco en man cpio:

$ cpio --no-absolute-filenames -i < initrd.img-5.4.0-58-generic

 

sólo trae esto:

tree kernel/
kernel/
└── x86
    └── microcode
        └── AuthenticAMD.bin

   

Recordando mis fracasos con el router dañando por un rayo, usé binwalk:

$ sudo apt install binwalk

En particular en Mint Mate 20.1 no resuelve en las dependencias lzop, así que te enterás en el medio que falta, no importa, rm -r,

$ sudo apt install lzop

$ binwalk -e initrd.img-5.4.0-58-generic

Esto es más compeletito:


_initrd.img-5.4.0-58-generic.extracted/
├── 0.cpio
├── 17D6ECC
├── ....
├── 1DCEE71
├── 1DCEE71.zlib
├── 1E2C581
├── 1E2C581.zlib
├── 220582A.zip
├── 26DCC87.gz
├── ...
├── 3E9AA31.gz
├── 3FE8E9D.gz
├── 43CD22.gz
├── 4A69999.gz
├── 4B60465.xz
├── 4D6447.lzo
├── 4D64CC.xz
├── 51D939A
├── 51D939A.zlib
├── 7C00.cpio
├── cpio-root
│   └── kernel
│       └── x86
│           └── microcode
│               └── AuthenticAMD.bin
├── cpio-root-0
│   └── kernel
│       └── x86
│           └── microcode
│               └── GenuineIntel.bin
└── T1:X3_101115_1_8_1_expROM_FW_uni_template_eeprom0.bin

 

No me gusta para nada, estoy seguro que falta de todo, pero bueno, no voy a analizar esto con profundidad, ya que excede el alcance del curso, sólo completando...

Pero, si te aguantás dos minutos más, presenta el comando unmkinitramfs que hace exactamente lo que hace falta

 $ tree -L 2
.
├── early
│   └── kernel
├── early2
│   └── kernel
└── main
    ├── bin -> usr/bin
    ├── conf
    ├── etc
    ├── init
    ├── lib -> usr/lib
    ├── lib32 -> usr/lib32
    ├── lib64 -> usr/lib64
    ├── libx32 -> usr/libx32
    ├── run
    ├── sbin -> usr/sbin
    ├── scripts
    ├── usr
    └── var

 

¡Qué falta de paciencia y qué acertado no haber intentado profundizar! Me hubiese llevado semanas o para siempre, mirá el contenido de unmkinitramfs que es bash.

 

Modules

 

Busqué rápido y no encontré como identificar de los 5000 módulos disponibles cuáles están en uso (lsmod) y cuáles han sido usados (supongo que en dmesg) para eliminarlos del disco como medida de hardening y recuperación de espacio. La medida de hardening es blacklist. Queda para investigar algún  día...

 

Kernel Source

 

Si te falla make ctags con 

xargs: ctags: No such file or directory
sed: can't read tags: No such file or directory

Es por que te falta ctags:

$ sudo apt install   universal-ctags

 

Building

 

Si al hacer make xconfig te dice:

* Could not find Qt via pkg-config.
* Please install either Qt 4.8 or 5.x. and make sure it's in PKG_CONFIG_PATH

Dale :

$ apt install qtbase5-dev

Si querés ver las opciones para otras arquitecturas, por ejemplo ARM:


$ make ARCH=arm xconfig


Igual no te olvides que necesitás toolchain y setear la variable de cross compilation, ya volveré sobre este tema en otra entrada a futuro.

 

Detalles ejercicios


Para entrar en GRUB en Linux Mint (Ubutnu) es con shift, recordá si estás en virtual box que Right Ctrl te hace recuperar el punteroo del mouse. Recordá que el layout del teclado de GRUB no necesariamente corresponde con lo que tengas.


En el ejercicio propone comparar dmesg con apic=debug, pero por algún motivo mi cerebro lo convirtió en acpi=debug.

La mejor táctica es ir probando con las diversas opciones (apic, acpi, nada) y luego tomar de /var/log/dmesg.X.gz los resultados, renombrar acordemente y ahí comparar con:

for FILE in dmesg.*; do
  for DEBUG in ACPI APIC; do
    echo -n "$FILE $DEBUG "
    grep "$DEBUG""$FILE" | wc -l
  done
done

dmesg.acpi ACPI 50
dmesg.acpi APIC 7
dmesg.apic ACPI 50
dmesg.apic APIC 58
dmesg.original ACPI 50
dmesg.original APIC 7


Hay un ejercicio de agregar una entrada a grub.cfg, que se hace con update-grub. Como nunca lo hice le desconfío, así que comencé por regenerar grub.cfg al lado como referencia.

$ sudo grub-mkconfig -o grub.cfg.original

Si la salida de diff te parece muy rústica y  no querés instalar kdiff3:

$ diff --side-by-side grub.cfg.original /boot/grub/grub.cfg -W160 | less

Lo importante es que agregó $vt_handoff a los parámetros del kernel y tocó un par de etiquetas, ok, esto indica que hay un cierto desfasaje entre la configuración y la realidad.

Luego fuí mirando cada archivo en  /etc/grub.d, su stdout va a parar a /boot/grub.cfg

Estaría bueno que fuera generando comentarios para poder mapear de dónde sale cada cosa, ah, me adelanté, hay unos separadores en /boot/grub.cfg que indican cuál fué el archivo generador.

Para hacer el ejercicio tenemos dos caminos, agregar una custom entry en /etc/grub.d y ejecutar e proceso de conversión o editar directamente grub.cfg, hice esto último pues el ejercicio no pide que persista.

No te voy a contar lo que ocurre pero si que si agregás rdinit=/bin/sh te tira un busybox, interesante...

 

Curiosidades

 

Estos son algunos de los números de CPUs que soportan algunas arquitecturas:

arm: 32

x86: 64 

s390: 512

powerpc: 8192

 

Conclusiones

 

El curso en general está bueno y me ha servido, pero no me parece tan advanced, quizás me perdí los intro/basic/intermediate.

Los ejercicios han sido útiles, pero el tiempo previsto no me corresponde a lo que le invertí, tipo que el primero lo hice en 15 minutos y decía 60 y el segundo decía 45 y estuve un par de horas.

Algunos ejercicios tienen leves discrepancias que me han ayudado a que los haga de modo no automático, sacando ventaja de los errores...





Notas del curso Linux Device Drivers

$
0
0

En el marco de silenciar el cooler de una Raspberry Pi, vengo capacitándome tal como cuento en el curso anterior de linux "avanzado". Este, Linux Device Drivers , del mismo autor es de... drivers.

Los drivers en forma de módulos son la manera de no tener que estar recompilando y rebooteando al sistema operativo ante agregados y cambios.

El curso me ha servido mucho para comprender la arquitectura, estaba teniendo una idea equivocada del alcance de las responsabilidades del driver, cuando lo haga concretamente entraré en más detalles.

La parte de gestión está un poco repetida respecto al curso anterior, pero se vé con mayor profundidad, por ejemplo la parte de configuración para modprobe, el blacklisting, que en /sys/module/$MODULE/parameters están los parametros y serán legibles o incluso escribibles según los permisos de module_param().

Respecto a escribir módulos muy básicos el curso está ok, pero hay muchos temas muy interesantes que sólo menciona de modo muy superficial, por ejemplo:

  • debugfs
  • tracing
  • crash

Amerita cada uno de estos temas un buen tiempo, hoy no va a ser...

Me hubiera gustado que en el ejercicio de char devices, que es un dispositivo donde guardar un string, como parte del enunciado hubiera pedido que el tamaño del buffer se determinara en el momento de la carga en lugar del momento de compilación, pero eso hubiese significado usar memoria dinámica.

Tambien que se pudiera lidiar con >>, esto es, poder agregar información, el comportamiento actual es:

$ echo "hola"> /dev/charDevice

$ cat /dev/charDevice

hola

$ echo "chau">> /dev/charDevice

$ cat /dev/charDevice

chau 

 

Y lo que deseo es:


$ echo "hola"> /dev/charDevice

$ cat /dev/charDevice

hola

$ echo "chau">> /dev/charDevice

$ cat /dev/charDevice

hola

chau

 

Hagamos mi deseo realidad


La parte del append tiene dos aspectos, detectarlo y lidiar correctamente con el buffer.

Tras una caótica exploración de las estructuras en uso (léase, no recuerdo que hice) parecida a pensar que en el momento del write debería haber algún indicio de si estamos en el contexto de un > o un >>, hallé que file provee unos flags donde dice el modo de apertura. el campo se llama f_flags. y está en la struct file en fs.h y


struct file {
...
unsigned int f_flags;
...
}


con un if (filep->f_flags &O_APPEND) bien posicionado, ya podemos elegir el comportamiento.


Este es el código base:

 

ssize_t charDevice_write( struct file *filep,
                          const char *buf,
                          size_t nbytes, loff_t *ppos){
  int failed, transferred;
  if (debug)
    printk("WRITE : %d flags: %x\n",
           (int)nbytes, filep->f_flags);

  if (!nbytes)
    return 0;

  if (nbytes >= BUFFER_SIZE)
    nbytes =  BUFFER_SIZE-1;

  if (filep->f_flags & O_APPEND)
    printk("WRITE : Appending %ld bytes\n",nbytes);
  else
    printk("WRITE : Saving %ld bytes\n",nbytes);
    
  failed = copy_from_user(char_buffer, buf, nbytes);
  transferred = nbytes - failed;

  if (!transferred)
    return -EFAULT;

  char_buffer[transferred] = '\0';

  buffer_length = transferred;
  return transferred;

}

 

Este es el código intermedio ya funcionando:

 

ssize_t charDevice_write( struct file *filep,
                          const char *buf,
                          size_t nbytes, loff_t *ppos){
  int failed, transferred;
  if (debug)
    printk("WRITE : %d flags: %x\n",
           (int)nbytes, filep->f_flags);

  if (!nbytes)
    return 0;

  if (filep->f_flags & O_APPEND) {
    printk("WRITE : Appending %ld bytes\n",nbytes);
    if (nbytes >= BUFFER_SIZE - buffer_length)
      nbytes =  BUFFER_SIZE - buffer_length -1;

    failed = copy_from_user(
              char_buffer + buffer_length, buf, nbytes);
    transferred = nbytes - failed;

    if (!transferred)
      return -EFAULT;

    buffer_length += transferred;

    char_buffer[ buffer_length] = '\0';
  } else {
    printk("WRITE : Saving %ld bytes\n",nbytes);
    if (nbytes >= BUFFER_SIZE )
      nbytes =  BUFFER_SIZE-1;

    failed = copy_from_user(char_buffer, buf, nbytes);
    transferred = nbytes - failed;

    if (!transferred)
      return -EFAULT;

    buffer_length = transferred;
    char_buffer[transferred] = '\0';
  }
  return transferred;
}


Este es el código final refactorizado, notando que todo se trata de buffer_length:

 

ssize_t charDevice_write( struct file *filep,
                          const char *buf,
                          size_t nbytes, loff_t *ppos){
  int failed, transferred;
  if (debug)
    printk("WRITE : %d flags: %x\n",
            (int)nbytes, filep->f_flags);

  if (!nbytes)
    return 0;

  if (filep->f_flags & O_APPEND) {
    printk("WRITE : Appending %ld bytes\n",nbytes);
  } else {
    printk("WRITE : Saving %ld bytes\n",nbytes);
    buffer_length = 0;
  }

  if (nbytes >= BUFFER_SIZE - buffer_length)
    nbytes =  BUFFER_SIZE - buffer_length -1;

  failed = copy_from_user(
            char_buffer + buffer_length, buf, nbytes);
  transferred = nbytes - failed;

  if (!transferred)
    return -EFAULT;

  buffer_length += transferred;

  char_buffer[ buffer_length] = '\0';
  return transferred;
}

Este es mi script de prueba de mi script de prueba:

 

sudo rmmod charDevice
sudo insmod charDevice.ko debug=1 major=197
echo "one"> charDevice2
cat charDevice2
echo "two">> charDevice2
cat charDevice2
dmesg | tail


Y esta es la traza de la ejecución:

 ./go.sh
[sudo] password for carlos:             
one
one
two
[971144.117183] WRITE : 4 flags: 8001
[971144.117187] WRITE : Saving 4 bytes
[971144.119726] READ     : 131072 bytes  ppos  : 0
[971144.119958] READ     : 131072 bytes  ppos  : 4
[971144.119959] READ     : 1
[971144.120169] WRITE : 4 flags: 8401
[971144.120171] WRITE : Appending 4 bytes
[971144.120987] READ     : 131072 bytes  ppos  : 0
[971144.125827] READ     : 131072 bytes  ppos  : 8
[971144.125831] READ     : 1


El código completo en github.

Conclusiones

El curso está bien, empieza mejor de lo que termina, en el tercer tema que es network devices como que pierde el foco, pues explica algo muy específico, quizás ya estaba cansado y la limitación fué mía.







Notificación de claves

$
0
0

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 variasveces.


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.

Jugando con PDFs

$
0
0

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.




Cómo firmar documentos sin imprimirlos

$
0
0

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



H4CK3D 2021: configuración básica

$
0
0

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!!!

H4CK3D 2021: leyendo los botones de la placa

$
0
0

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: un desvío necesario

$
0
0

Si venís leyendo los primerospasos, 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.

 





H4CK3D 2021: un desvío innecesario

$
0
0

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

 

El código está en github







H4CK3D 2021: sigue el desvío, para bien

$
0
0

Esto más que H4CK3D debería llamarse... no sé, queda H4CK3D pues todas estas entradas son lo que no me alcanzaría jamás el tiempo para mostrar en una charla de una conferencia, es toda la investigación previa, la familiarización con el entorno y las herramientas.


Además, se viene la charla y quizás esté un poquito retrasado, eso me pone en modo procrastinador productivo, no hago lo que debería pero si algo útil en su lugar.

Lo anterior:

 

 

La excusa de hoy es haber agregado un módulo de servo. ¿Qué es un servo? Sos una de las pocas personas que lee este blog y calculo que deberías estar en sintonía con estos temas, así que si no sabés, podés buscar mejores detalles en Internet, pero te hago un resumen.

 

Es un motorcito con unos engranajes que dependiendo de una señal se posiciona en un ángulo. Esa señal es un pulso que debe cumplir con que el período sea de 20ms y el pulso mida entre 1 y 2 ms.

 

Servo al máximo
Servo al máximo

Servo al mínimo
Servo al mínimo

Esa forma se puede obtener en un microcontrolador con este sencillo programa,


#define PERIOD    0.020
#define MIN_WIDTH 0.001
#define MAX_WIDTH 0.002
float period = PERIOD;
float width = MIN_WIDTH;
int main() {
    DigitalOut servo1(PC_8);   
    while(1) {
       for(int i=0; i< 200; ++i) {
          servo1=1;
          wait(MIN_WIDTH);
          servo1=0;
          wait(PERIOD - MIN_WIDTH);
       }    
       for(int i=0; i< 200; ++i) {
          servo1=1;
          wait(MAX_WIDTH);
          servo1=0;
          wait(PERIOD - MAX_WIDTH);
       }    
    }


La versión completa en  https://os.mbed.com/users/cpantel/code/servo_software/

 

Si ya programaste alguna vez un microcontrolador o si pensás un rato, notarás que no bien quieras hacer algo más, vas a tener tocar los wait()s para compensar, una pesadilla inmanejable. Podrías entonces usar un timer que genere excepciones pero la verdad es que buena parte del los microcontroladores, al menos todos los que me he cruzado, que no son muchos, tiene un dispositivo llamado PWM al que le decís generame pulsos de tales características y lo hacen, aunque hayas logrado colgar al micro.

 

#define PERIOD    0.020
#define MIN_WIDTH 0.001
DigitalIn openButton(D4);
DigitalIn moveUpButton(D5);
DigitalIn moveDownButton(D6);
DigitalIn closeButton(D7);
PwmOut led1(LED1);
PwmOut servo1(PC_8);
PwmOut servo2(PC_9);
float period = PERIOD;
float width = MIN_WIDTH;
int main() {
    servo1.period(PERIOD);
    servo1.pulsewidth(width);
    while (1) {
// hacé lo que quieras acá
    }
}


Listo,un ejemplo más completo que según aprietes los botones mueve el servo en

https://os.mbed.com/users/cpantel/code/servo_PWM/

Volviendo a la cpu risc v en la edu-ciaa-fpga, agregué un PWM restringido, que sólo sirve para manejar un servo. Esto es, con un PWM normal puedo darle por software cualquier frecuencia y duración de pulso, lo cual no necesariamente es correcto. Con el módulo servo, no importa lo que hagas en software, la salida va a ser correcta.


Tuve algunas dificultades, por ejemplo, tengo módulos en los que puedo leer (rtc), en este escribir, pero no pude leer y escribir, ya le dedicaré un rato a ello.

 

La otra, es que tuve que implementar con una condición lógica bastante larga en lugar de unos case que más adelante me hubiesen permitido usar funciones más avanzadas de (system)verilog para generar los casos, ya le dedicaré un rato a ello.

Ya que estaba, aproveché para mejorar los ifdef que habilitan los puertos y los módulos. Antes hubiese sido así:

En top.sv:


`ifdef ARDUINO
    output logic [31:0] arduino,
`endif

y en la instanciación de icicle, en la interfaz:

`ifdef ARDUINO
        .arduino(arduino),
`endif

 

luego en icicle.sv:

 

`ifdef ARDUINO
    //código...

 

El problema es que al hacer el servo, que usa un pin del arduino, tenía que medio hackearle ese pin pues al habilitar ARDUINO, los pines los usaba el módulo arduino. Lo que hice fue separar en _CONN y _DEV, lo cual me permite habilitar los pines del arduino y luego conectarlos al módulo arduino o no instanciar ese módulo y usarlos desde otro.

Esto nos lleva a que cada programa puede usar distintas configuraciones de hardware, asi que además agregué que a los defines.sv que se le copia la base de la placa, se le agreguen lo que el programa pida, entonces en boards/edufpga-defines.sv tengo


// Defines for EDU-FPGA
`define noPMOD0_DEV
`define noPMOD1_DEV
`define noARDUINO_DEV
`define noRTC_DEV
`define noSERVO_DEV
`define noUART_DEV

`define noPMOD0_CONN
`define noPMOD1_CONN
`define noARDUINO_CONN

que cumple la función de documentar que hay disponible, luego en programs/servo_device/edufpga-defines.sv:

// Defines for servo_device running in EDU-FPGA
`define SERVO_DEV
`define UART_DEV

`define ARDUINO_CONN

y esto se arma en el Makefile:

cat boards/$(BOARD)-defines.sv programs/$(PROGRAM)/$(BOARD)-defines.sv > defines.sv

 

En el mismo camino de facilitar cambiar el programa, pasé a un .h el mapa de memoria, actualmente:


#define LEDS        *((volatile uint32_t *) 0x00010000)
#define BUTTONS     *((volatile uint32_t *) 0x00010004)
#define PMOD0       *((volatile uint32_t *) 0x00010008)
#define PMOD1       *((volatile uint32_t *) 0x0001000c)
#define ARDUINO     *((volatile uint32_t *) 0x00010010)
#define RTC         *((volatile uint32_t *) 0x00010014)
#define SERVO       *((volatile uint32_t *) 0x00010018)
#define UART_BAUD   *((volatile uint32_t *) 0x00020000)
#define UART_STATUS *((volatile uint32_t *) 0x00020004)
#define UART_DATA   *((volatile  int32_t *) 0x00020008)
#define MTIME       *((volatile uint64_t *) 0x00030000)
#define MTIMECMP    *((volatile uint64_t *) 0x00030008)

Con todos estos cambios, si algún día alguien quiere portar el programa a otra placa, le va a resultar más fácil.

Esto me llevó, por completitud, a adaptar todo el "legacy", comprobar que todos los programas funcionen, probablemente, con un poco de suerte... https://github.com/cpantel/evilCodeSequence/tree/v0.2.0


Me faltaría documentar un poco en el readme, uno de estos dias...




 

 




H4CK3D 2021: cambios al Makefile

$
0
0

Esquivándole el bulto al problema principal, que es implementar el ataque, sigo introduciendo cambios y mejorías, en este caso al Makefile.

Lo anterior:

 

Relacionado al Makefile, ya había puesto:

BOARD    ?= edufpga
PROGRAM  ?= hello

Lo primero debido a que desde ciaa/icicle para acá, es la placa que vamos a usar, no quiero estar poniendo BOARD=edufpga en cada llamada a make. Lo segundo es que el foco ha pasado de elegir el BOARD a elegir el PROGRAM.

Tambien había modificado la regla para defines.sv para que tome la configuración de puertos y dispositivos de la carpeta del programa. Esto implica que al hacer un nuevo programa, convienen hacer un make clean para que se reescriba con la nueva configuración.

 

Optimización del programa

 

Una mejoría está relacionada a la optimización del código del programa. Por default grahamedgecombe/icicle lo puso en -Os, que es razonable, hacer el programa lo más chico posible, menos RAM, menos ocupación de la FPGA. Pero para un inexperto como yo en assembly en general y de risc-v en particular, se me hace bastante complicado analizar el código para implementar el ataque, así que ahora existe la opción:

OPTLEVEL ?= -Os

si en la invocación agregás

OPTLEVEL=

como en 

make PROGRAM=access_control OPTLEVEL=""

no optimiza.

 

Uso de azar en PNR


Apenas complicado fué el asunto del SEED. ¿Qué es el SEED? Pues que para el place and route funcione bien, el algoritmo utiliza pseudoazar.

¿Qué es place and route? Es el proceso de ubicar y conectar los componentes en un circuito electrónico en este caso en la FPGA.

¿Qué es pseudoazar? Bueno, primero veamos que es azar. Azar es... tirar los dados. El problema con el azar es que no podés repetirlo y si usás azar para algo, si tenés una falla y no lo podés repetir, se complica diagnosticar la cause. Pero necesitás el azar, ¿en qué quedamos?

El pseudoazar viene a ser tirar los dados un montón de veces y guardar lo que te salió en una lista. Luego, cuando lo necesitás tirás una sola vez los dados para elegir donde empezar en esa lista. Entonces tenés azar y repetición a la vez si necesitás, eligiendo la posición en la lista. Es un azar apto para estas cosas, pero no ante un adversario, pues no bien reusás la lista o comparando ejecuciones, se puede obtener la lista.

La gente que realmente sabe criptografía sabrá ser generosa con mi explicación.

Como el archivo generado depende entonces del pseudoazar, si necesito que dado el mismo diseño obtener el mismo placement and routing, necesito el control de SEED, que en nextprn-ice40 se determina con el parámetro --seed.

Me interesa que genere siempre lo mismo debido a que quizás para el ataque pueda, no creo, hacer la modificación no sobre el código HDL sino en una etapa posterior, veremos.

En el Makefile primero hay de definirlo con vacío como default:

SEED     ?=

y luego en arch/ice40.mk, si SEED está vacío, sigue vacío, pero si hay algo, agregarle la key --speed.

ifeq ($(SEED),)
SEEDVAL =
else
SEEDVAL = --seed $(SEED)
endif

SEEDVAL luego es reemplazado en la línea:

nextpnr-ice40 $(QUIET) --$(SPEED)$(DEVICE) --package $(PACKAGE) $(SEEDVAL) --json $< --pcf $(PCF) --freq $(FREQ_PLL) --asc $@

 

¿Por qué no hice la misma lógica con OPTLEVEL? Buena pregunta... debe ser por que es fácil escribir "" y no "--seed 3".

 

Dependencias

 

El diagrama de Pablo, redibujado así, no me alcanza, necesito más detalle. Para ello necesito corregir un poco el Makefile.

 

icicle flow high level
icicle flow high level


Para comenzar, mover todo objeto temporario o construido a una carpeta, BUILD es un buen lugar, luego, mover los .ys a arch.

De tanto revisar como le fuí tomando la mano y generé un nuevo diagrama:

 

ICE40 flow low level
ICE40 flow low level


 

El problema restante que me queda es el tiempo:

  • yosys: 1:10
  • nextpnr-ice40: 1:45
  • iceprog: 2:00

Tengo que armar reglas de Makefile tal que pueda hacer tener estas cuatro:

  • software: genera progmem.hex.
  • hardware: genera top_syn.asc.
  • system: genera top.bin.
  • all: que haga todo sin tanta sutileza, lo que está ahora.
Será otro día...

 

 



H4CK3D 2021: mejor resolución de dependencias en Makefile

$
0
0

Te recuerdo que todo esto tiene el objetivo de armar un ataque al comportamiento de un programa desde el hardware para mostrar en H4CK3D 2021. Como productos colaterales, una mejor adaptación de grahamedgecombe/icicle a ciaa/icicle, en parte aceptada como PR, en parte quedará en cpantel/evilCodeSequence y un montón de aprendizaje de mi parte, que te comparto como siempre, más si venís de leer todo esto:

El problema que vengo arrastrando es el tiempo:

  • yosys: 1:10
  • nextpnr-ice40: 1:45
  • iceprog: 2:00

 

No puedo evitarlos, pero como habías visto en la entrada anterior, hay dos ramas, una para el hardware y otra para el software. Convergen en la que llamo "system", que es al resultado del proceso de hardware pegotearle la imagen obtenida por el proceso de software.

Makefile detallado original
Makefile detallado original


Tal como está el Makefile, como que no está fácil, así que lo desarmé para terminar de entenderlo y lo volví a armar. De paso, veamos el proceso pero un poco menos detallado:


Makefile con reglas sencillas
Makefile con reglas sencillas, diagrama simplificado

Eliminé algunas partes del diagrama para recalcar las cuatro reglas y lo de la memoria. 

Arriba a la derecha, se arma la imagen, lo cual incluye tanto nuestro programa como otro código, de esto aún no sé mucho, parece haber algo de inicialización y el de arranque, ya seá desde la RAM o desde flash, este es un punto interesante a explorar, cargar el programa desde memoria SD, agilizaría aún más el proceso.

Arriba a la izquierda, como que estaba confundida la construcción de la imagen con la inclusión de RAM. De hecho, en el Makefile original si modificás el programa regenera sin necesidad el hardware. El primer icebram lo que hace es sólo reservar el espacio de memoria, luego, en system, nuevamente icebram, pone el programa en esa área reservada.

Las nuevas reglas las construí apuntando al final de reglas existentes, los cambios son en el Makefile:


software: BUILD/progmem.hex

Sencillo, pedís "software", hace lo necesario para construir progmem.hex.


hardware: $(ASC_SYN)

según ASC_SYN  = BUILD/$(TOP)_syn.asc viene a ser top_syn.asc en el diagrama.

 

system: $(BIN)

según BIN      = BUILD/$(TOP).bin es top.bin en el diagrama

 

Para romper el encadenamiento erróneo que había, convertí:

$(BLIF) $(JSON): $(YS) $(SRC) BUILD/progmem_syn.hex BUILD/progmem.hex BUILD/defines.sv

en

$(BLIF) $(JSON): $(YS) $(SRC) BUILD/progmem_syn.hex BUILD/defines.sv

Ahora, tal como es de esperar, puedo reemplazar el programa en sólo dos minutos en lugar de los cinco originales.


Respecto a utilizar la flash como arranque, mmh, estuve mirando con más detalle y dice ser SPI, lo cual implica cuatro señales: SCLK (el clock), SS (Slave Select para elegir el chip), MOSI (datos del master al slave) y MISO (datos del slave al master).

Para comenzar, en top.sv parecen estar los dos primeros, pero los últimos están como inout

Me da olor a Dual SPI. Más que luego se instancian uno tales "flash_io" del tipo SB_IO y TRELLIS_IO que según veo por encima rapidito tienen que ver con tristate.

 Luego, en flash.sv, dice

     assign clk_out = clk;

y no me suena que SPI soporte 36Mhz.

¡Qué lástima! Me había hecho ilusiones, incluso le había soldado los pines al sdcard reader que tengo. Esto está muy encima de mis habilidades y no es bloqueante ni aporta mucho al malware que tengo que hacer funcionar...

Viewing all 264 articles
Browse latest View live