Tag Archives: linux

Linux syscall and RISC-V assembly

Sample of RISC-V assembly code

Syscall in Linux kernel, is an interface to access to kernel basic functions. They are described in section 2 of man pages. The introduction is in man 2 syscall (indirect system call), and the list of functions are described in man 2 syscalls.

This article follow previous one about RISC-V overall progress and available tools to play with, I will try to make a short article here about Linux syscall usage and the RISC-V assembly case.

Table of Content

* Description section of the man page
* Getting the list of function and how to access them
* Passing parameters
* Function number and registers of return values
* Return values and error code
* Compiling and executing on virtual environment
* Update: Bronzebeard assembler and its baremetal environment for real hardware

Description section of the man page

* syscall() is a small library function that invokes the system call whose assembly language interface has the specified number with the specified arguments. Employing
* syscall() is useful, for example, when invoking a system call that has no wrapper function in the C library.
* syscall() saves CPU registers before making the system call, restores the registers upon return from the system call, and stores any error returned by the system call in errno(3).
* Symbolic constants for system call numbers can be found in the header file .

You can find here function, like access to files open/close/read/write/flush, access to sockets, ioctl, uid, gid, pid, messages, ptrace, restart system, etc…

Getting the list of function and how to access them

As far I know, now only a part of syscall functions are accessible easily in assembly, they are defined in /usr/include/unistd.h, and function numbers assigned in ABI are defined in /usr/include/asm-generic/unistd.h.

The more practical match I found is using /usr/include/asm-generic/unistd.h to see which function are available and there respective manpage for the function header definition. For example:
* asm-generic: #define __NR_read 63
* man 2 read: ssize_t read(int fd, void *buf, size_t count);

The ABI with RISC_V as defined in man 2 syscall in section Architecture calling conventions use the registers following 2 tables rules.

Passing parameters

The second table in this section of the man page shows the registers used to pass the system call arguments.

Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
──────────────────────────────────────────────────────────────
riscv         a0    a1    a2    a3    a4    a5    -

Here are the arguments in the order of the function definition, for example, in read (63) function:

ssize_t      read  ( int fd, void *buf, size_t count );
a0(result) = a7(63)( a0(fd),  a1(*buf),    a2(count) )

For remember, 3 standard I/O file descriptors are STDIN=0, STDOUT=1, STDERR=2, the other are used when opened a file with open and closed by close.

So we set the arguments as this. x0 is the always 0 register:

        addi  a0, x0, 0       # Set STDIN as sources
        la    a1, buffer_addr # load address of helloworld
        addi  a2, x0, 3       # reaad 3 bytes

Function number and registers of return values

And the first one give the register in which put the function number, that will receive return value and errno (error value)

Arch/ABI    Instruction           System  Ret  Ret  Error    Notes
                                  call #  val  val2
───────────────────────────────────────────────────────────────────
riscv       ecall                 a7      a0   a1   -

So for the read function, we need to put 63 (as found in /usr/include/asm-generic/unistd.h in register a7, and registers and following registers will receive the return values, a0 will receive system call result, and a1 an error message (the errno value).

ssize_t read(int fd, void *buf, size_t count);
a0    =  63 (    a0,       a1,         a2)

So can set them as this:

        addi  a7, x0, 63     # set called function as read()
        ecall                # call the function

Return values and error code

After the man page of read(2):
* On success, the number of bytes read is returned
* On error, -1 is returned, and errno is set to indicate the error.

So we can test first the return value of a0 and if a0 < 0 then we jump to part for display the error message, else we can simply display a OK message. So for the branching part, RISC-V in its super reduced set only have < (lt) and <= (le) comparators, you just need to swap registers to compute > (gt) and >= (ge), but this avoid lots more of transistors.

    addi a3,x0,0           # x3=0
    blt  a1,a3, error_seq  # if x1<0 branch to error_seq

We so use here the syscall write function (64) defined as:
* asm-generic: #define __NR_write 64
* man 2 write: ssize_t write(int fd, const void *buf, size_t count);
* So, registers: a0=1 (STDOUT), a1=*buf, a2=count, a7=64 (function number)

And we will finish with exit() syscall, defined as:
* asm-generic: #define __NR_exit 93
* man 2 exit: noreturn void _exit(int status);
* So, registers: a0=return code, a7=93 (function number)

    la    a1, ok           # load address (pseudo code) of ok string
    addi  a2, x0, 3        # set length of text to 3 (O + K + \n)
    addi  a7, x0, 64       # set ecall to write function
    ecall                  # Call the function

    addi  a0, x0, 0        # set return code to 0 (OK) for exit (93) function
    j     end              # unconditional jump to end before quit

error_seq:
    la    a1, error        # load address (pseudo code) of error string
    addi  a2, a2, 0x30     # add 0x30 (0 ASCII code) to the error code
    sb    a2, 7(a1)        # put the (byte) value at position 7 of Error string (before \n)
    addi  a2, x0, 10       # set now length of our string
    addi  a7, x0, 64       # set ecall to write function
    ecall                  # Call the function

    addi  a0, x0, -1       # set return code to -1 (error) for exit (93) function
end:
    addi    a7, x0, 93     # set ecall to exit (93) funciton
    ecall                  # Call linux to terminate the program

.data:
ok:     .ascii "OK\n"
error:  .ascii "Error:  \n"

RISC-V Longan nano

Compiling and executing on virtual environment

If you don't have a RISC-V hardware (can be found as low as 3€ now), you need to have a cross compiler and qemu for emulating instructions, or a whole system installed.

Packages needed for compiling on ArchLinux x86 or ARM for example.

sudo pacman -S riscv64-linux-gnu-gcc  riscv64-linux-gnu-glibc riscv64-elf-binutils riscv64-elf-binutils riscv64-elf-gcc riscv64-elf-gdb

Newlib is a lightweight RV32 (RISC-V 32bits) lightweight library for bare metal that can be used instead of a whole GNU system on embedded devices with low memory capacity (as Longan nano, less than 8€ with screen, see picture below, or 3€ Sipeed RV): riscv32-elf-newlib.

I made a simple shell script to don't have to remember the commands to assemble the code from an x86 platform (work also on ARM or RISC-V one) that take the .s as argument:

name=$1
riscv64-linux-gnu-as -march=rv64imac -o ${name}.o ${name}.s
riscv64-linux-gnu-ld -o ${name} ${name}.o

You can add a strip but better to avoid it if you need to debug it:

riscv64-elf-strip --strip-all ${name}

And it can be executed on non RISC-V platforms by using qemu-riscv64, if it doesn't depend on libraries or if you have them installed, it allow you to test it without having a full RISC-V system installed, qemu is so fantastic. On ArchLinux it is available in package qemu-arch-extra:

qemu-riscv64 ${name}

And can be disassembled (will probably use different instruction than your assembly code, due to RISC-V assembly pseudo-instructions:

riscv64-linux-gnu-objdump -d ${name}

Bronzebeard assembler and its baremetal environment for real hardware

RISC-V Longan nano

Update: Bronzebeard is an assembler with light baremetal environment builder for RISC-V, GD32VF103 as Longan Nano (about 8€ with a screen, as pictured on this article pictures) and Wio (similar board with an added ESP8266 SoC). I made an AUR package of Bronzebeard, and someone made a Mandelbrot set demo in 918 bytes pure RISC-V assembly. You can find some other example in the source of Bronzebeard. gd32vf103inator is a set of tools for GD32V, to manage from a simple random text editor.

Tablette Wacom et autres périphériques USB sur système Android

J’ai fais quelques petits test sur un Galaxy Note III lite avec des périphériques USB via un cable µUSB <=> USB femelle. Comem Android utilise un noyau Linux, il est normal qu’au moins tous les périphériques reconnus par ce dernier soient reconnus. Qu’ils soient interfacés avec une application est un autre problème.

Dans les exemples que je site, j’ai eu un problème une fois lors d’une connexion, peut être problème d’alimentation USB après avoir branché la wacom, en essayant ensuite avec un câble deux prises (comme pour les disques durs externes), la Wacom ayant le même type de connecteur miniUSB (et non microUSB). le clavier ne fonctionnait pas après. J’ai du rebooter le téléphone, et tout fonctionnait directement ensuite.

Joypad type PSX2

Un joypad type PSX2 en USB à l’avantage d’avoir pleins de boutons, deux manches analogiques dont un peut être passé au choix en analogique ou digital), ce qui offre pas mal de possibilité pour l’interaction avec des tonnes d’applications.

J’ai un Saitek Rumble depuis un tas d’années, le retour de force ne marche pas pour ce modèle sous Linux, je me disais que je ingénierie-inverserais bien le pilote histoire de, ça a pas l’air compliqué, mais ça n’a pas réussi à franchir le cap de la TODO vaporeuse et du regard rapide de l’API bien foutu de Linux sur ce point. Il me sert pour quelques expérimentations de bidouilles et sur des jeux ou abandonwares émulés (bornes Mame/Mess ou vieilles consoles en abandonware comme GameCube/Wii, PSX2, etc…). Pour le simulateur de vol (Flightgear Flight Simulator), je préfère souris+clavier, sans doute à tort. Il est immédiatement. J’ai envie d’expérimenter un peu plus des applications interactives dans un futur proche, mais on en est encore dans le TODO à tendance vaporeuse. Bon, donc, j’ai testé sur Android, il est bien détecté, sous l’émulateur de terminal, terminal IDE, il permet de remonter à la ligne précédente de l’historique ^^). Testé avec l’émulateur PSP (PPSSPP, également disponible sur f-droid), ça marche impeccablement. Il faut penser à reconfigurer les boutons dans le menu commandes.

Clavier USB

bon, ben là presque rien à dire, ça marche directement. Par contre, autant sur les tablette chinoises, on peut choisir la disposition du clavier (AZERTY/QWERTY/Bépo…), autant là, rien pour le clavier physique.

Tablette graphique Wacom

Alors là, ça se gatte, elle est détecté comme une souris, mais ne semble gérée par aucune application, et encore une fois, comme pour le clavier, rien dans les préférences. J’essairais de la brancher sur une tablette/téléphone chinoise quand j’en aurais une sous la main.

Pour vérifier la détection dans Terminal IDE :

terminal++@172.0.0.1:-$ lsusb
Bus 001 Device 002: ID 1519:0443
Bus 002 Device 011: ID 056a:00ba <= Wacom
Bus 001 Device 001: ID 1d6b:0002
Bus 002 Device 001: ID 1d6b:0002
Bus 002 Device 001: ID 1d6b:0003

Au niveau du système, sur un Note III lite qui contient un stylet Wacom avec gestion de pression, on pourrait s'attendre à un éventuel fonctionnement de base (on peut toujours rêver), et bien non. Pourtant, Wacom fourni une API pour les senseurs intégrés et tablettes en USB ou bluetooth. Ça marche pas avec Markers, ni avec les applications de dessin fournis par Samsung. Il n'y a rien dans les préférences du système Samsung qui ne permette de le configurer non plus. Dommage. C'est pareil sur une tablette Galaxy Note.

Maral (Марал), méthode mongol cyrillique pour ibus et mise à jour de paquets archlinuxARM

Méthode de saisie du mongol cyrillique pour ibus

J’ai fais un paquet ibus-table-mongolian ou Maral (Марал), méthode de saisie du mongol cyrillique pour ibus pour archLinux (testé sur ARM avec ArchLinuxARM et x86_64 (avec Antergos), en fait c’est du any), par contre, j’ai quelque chose de bizarre. Il faudrait peut être un rapport d’anomalie.

Le clavier devrait être indépendant de la disposition du clavier, la disposition phonétique ne correspondant pas du tout à celle des claviers en écritures latines. Le clavier fonctionne très bien sur ARM, mais pas aussi bien sur x86, car il s’adapte au clavier local. Le même problème apparaît sur Ubuntu 14.04 pour x86_64 qui utilise une beaucoup plus ancienne version.

Par défaut dans les scripts de l’auteur Ochko, on voit la variable des version 1.3 d’ibus-table :

KEYBOARD_LAYOUT = US_Default

Sur les versions récentes, il faudrait :

LAYOUT = us

J’ai essayé tout les cas, j’ai toujours le même problème. Je suppose que la différence entre ARM et x86_64 est un problème subtile du type de l’Endianess ou quelque chose comme ça, sans vraiment trop voir pourquoi ? Vous pouvez tourours utilisez la disposition système mongol après avoir installé la langue, mais c’est moins souple qu’ibus.

Mise à jour de paquets archlinuxARM

J’ai profité de la sortie de Entangle 0.7.0, l’outil de contrôle à distance d’appareil photo réflexe numérique (et autre contrôlable) et de 0AD a18 (un logiciel ludique libre de stratégie temps réel) pour mettre à jour les paquet ArchlinuxARM.

Récupérer tout ça

Vous pouvez récupérer les paquets et les PKGBUILD dans mon dossier-dépôt dédié.

Noyau Linux 4.0 (4.1 le 25 juin) sur Allwinner A20/Cubieboard2

Grâce aux travaux de développement électronique et logiciel, aux efforts de la communauté Linux-sunxi pour l’amélioration et l’intégration des pilotes dans le tronc principal des sources de Linux et au travail d’intégration des communauté ArchLinux et ArchLinuxARM, la Cubieboard2 (basée sur le SoC AllWinner A20) avec la distribution ArchlinuxARM utilise depuis cette nuit le noyau 4.0 par défaut. Auparavant, elle utilisait une branche 3.x du noyau en retard par rapport aux avancées de la branche principale.

* Linux XXX 4.0.5-2-ARCH #1 SMP Fri Jun 12 20:03:44 MDT 2015 armv7l GNU/Linux

Grâce à cela :
* Une grande partie des flash NAND peuvent être utilisée pour le système.
* La gestion de la fréquence CPU dynamique est bien gérée maintenant.
* La plupart des pilotes sont intégrées et à priori très stable.

Mise à jour du 25 juin, 11 jours après le 4.0 : Mise à jour vers noyau linux 4.1 effectuée ce soir
* Linux XXX 4.1.0-1-ARCH #1 SMP Tue Jun 23 23:24:14 MDT 2015 armv7l GNU/Linux