Performing Native system calls for user-level simulators
User-level simulators focus on simulating the user-space part of the target ISA, such as computation instructions, load/stores, branches and so on... Such simulator usually do not implement complex scheduling techniques, running a single thread over a single processor dedicated to this only thread.
Why user-level simulators needs to perform system calls
To simulate at user-level even a simple benchmark, it is required to implement some of the tasks that should be dedicated to the target OS, like the System Calls. To better understand why, let’s consider the following hello_world benchmark.
#include <stdio.h>
int main()
{ printf("hello world\n");
return 0;
}
Simulating this benchmark, the user expect if to write “hello world” on the screen. Let’s detail how this is performed in the real target architecture.
The printf function call will itself performs a call to the following write lower-level method of the glibc.
write(1, "hello world", 12);
With the first parameter (1) being the file descriptor number of the standard output, and the third one (12) the size of the buffer to print.
Once translated to a PowerPC assembly code, this write function correspond to the following assembly code:
li r3, 1 // r3 = target file descriptor mr r4, @hello // r4 = string buffer address li r5, 13 // r5 = buffer length li r0,4 // r0 = 4 (linux write syscall number) sc // Perform the system call
This code is using the system call instruction sc procided by the PowerPC ISA, and this instruction should itself require the operating system to perform the syscall.
Each operating system has its own system call table. The Linux Operating system for instance has the following Linux Syscall Table, where write is the system call number 4.
Such system calls can access dataset files (open, close, read and write), perform some filesystem related operations (rmdir, mkdir), obtain system informations (time,...) etc... Those operation should be performed natively on the hosting machine for the files to be effectively accessed.
We therefore need to translate the system calls of the target architecture to the native architecture running the simulator. This operation is performed by the Syscall Translator capability.
What is needed to perform a Syscall
As long as the Target OS is close to the native OS, it is quite easy to map syscalls: A 32-bit PowerPC 405 linux OS distribution has the same syscall table than a Intel 64-bit linux distribution.
However, the data structure involved in the system calls will not be the same when switching from 32-bit to 64-bit that will require a conversion. And the encoding can also be different when switching from a big-endian target to a little-endian host, as it is the case with the previous example.
The impact of the system calls on the architecture are usually to either read a buffer in the memory and perform something on the data (like the write syscall is doing) or to write a buffer in the memory (like the read syscall is doing). As we are performing the syscall natively, we have to copy those buffers from/to the native memory to/from the simulated memory, eventually performing some endian conversion.
The step usually involved in translating a syscall are:
- Get the target syscall parameters from the register bank
- Converting the input structures for the target syscall to build the native input structures
- Copying memory buffers from the simulated memory to the native memory
- Correcting endianess
- Allocating and filling the native input structures for the native syscall
- Performing the native syscall
- Converting the native output structures to target output structures
- Allocating a buffer corresponding to the target output structure in the native memory
- Filling htis structure with the result of the syscall perfoming some endianess conversion
- Copying back the output structure to the simulated memory.
- Set up the result register correctly
Therefore to perform syscalls, the syscall capability needs to be able to:
- Access (read/write) the register bank
- Access (read/write) the memory hierarchy
Native Syscall for Linux capability in the SVN repository
This capability is available at https://unisim.org/svn/public/components/FullSystem/trunk/src/plugins/os/linux.
To compile it requires to download the whole public branch of the repository which contains all the components composing the simulator. More information can be found on the Public Branch page.
Two different capabilities are provided:
- A Syscall Translation capability for 32-bit PowerPC
- A Syscall Translation capability for 32-bit ARM