Hi all,
I'm currently implementing a IO_MEM session for base-linux. The basic idea is as follows:
- core opens a file in /dev/ and calls an ioctl on the file to set a memory range. - it passes this file descriptor to the child process that requested the session - the child process mmaps the requested memory range from the file descriptor
The required device on Linux is already available (if you're interested, I've written a kernel module to provide it in [1]) so that isn't the problem. I tried to implement the IO_MEM session on base-linux but lack the understanding of how the Linux_dataspace and the Io_mem_dataspace are different. On base the Io_mem_session_component gets the dataspace capability from
_ds_cap = static_cap_cast<Io_mem_dataspace>(_ds_ep->manage(&_ds));
This doesn't work with base-linux since I can't cast the Linux_dataspace to Io_mem_dataspace. Yet I don't understand what I need to do this.
I also didn't find out what parts of the session are executed in which context (speaking what runs in core and what in the component itself), especially where I would put the actual mmap call.
My current implementation of the IO_MEM session is at [2] and [3].
Regards, Johannes
[1]: https://github.com/jklmnn/hwiodev [2]: https://github.com/jklmnn/genode/blob/0cfcec98ad4d4fdfc7bf6cc6e6df3567360b4e... [3]: https://github.com/jklmnn/genode/blob/0cfcec98ad4d4fdfc7bf6cc6e6df3567360b4e...
Hi Johannes,
The required device on Linux is already available (if you're interested, I've written a kernel module to provide it in [1]) so that isn't the problem.
cool!
I tried to implement the IO_MEM session on base-linux but lack the understanding of how the Linux_dataspace and the Io_mem_dataspace are different. On base the Io_mem_session_component gets the dataspace capability from
_ds_cap = static_cap_cast<Io_mem_dataspace>(_ds_ep->manage(&_ds));
This doesn't work with base-linux since I can't cast the Linux_dataspace to Io_mem_dataspace. Yet I don't understand what I need to do this.
On base-linux, there exists a custom 'Dataspace_component' implementation. In contrast to the regular version that maintains the information about the dataspace's physical address, the base-linux version has a file descriptor as member variable and the setter 'fd' to assign it. For RAM dataspaces, the file descriptor is assigned by (the Linux-specific implementation of) 'Ram_dataspace_factory::_export_ram_ds'. For ROM dataspaces, the file descriptor is assigned by the 'Dataspace_components(args)' constructor.
Your approach of introducing a base-linux-specific version of 'io_mem_session_component.h' is good. Like in the regular version, it hosts a 'Dataspace_component' object. But additionally, it would obtain the file descriptor from your kernel driver and assign it to the dataspace object. This could be done in the constructor (which now just prints the warning message). Once assigned, the rest (in particular the passing of the fd to the clients via RPC) should work out of the box.
For a quick test to see if the fd-passing over component boundaries works, you may first open just a regular file with known content and let an IO_MEM client (living outside of core) attach the dataspace and print its content.
Cheers Norman
Hi Norman
thanks for your help.
On base-linux, there exists a custom 'Dataspace_component' implementation. In contrast to the regular version that maintains the information about the dataspace's physical address, the base-linux version has a file descriptor as member variable and the setter 'fd' to assign it. For RAM dataspaces, the file descriptor is assigned by (the Linux-specific implementation of) 'Ram_dataspace_factory::_export_ram_ds'. For ROM dataspaces, the file descriptor is assigned by the 'Dataspace_components(args)' constructor.
Your approach of introducing a base-linux-specific version of 'io_mem_session_component.h' is good. Like in the regular version, it hosts a 'Dataspace_component' object. But additionally, it would obtain the file descriptor from your kernel driver and assign it to the dataspace object. This could be done in the constructor (which now just prints the warning message). Once assigned, the rest (in particular the passing of the fd to the clients via RPC) should work out of the box.
For a quick test to see if the fd-passing over component boundaries works, you may first open just a regular file with known content and let an IO_MEM client (living outside of core) attach the dataspace and print its content.
So if I understand it correctly just opening the file and calling _ds.fd(fd) would be enough (beside having the Dataspace_component _ds)?
I implemented this but when I try to attach the dataspace I get a segfault. I though this might be related to a wrong physical address for the file but the backtrace leaves me completely clueless.
#0 pseudo_end () at /media/sf_kernel/genode/repos/base-linux/src/lib/syscall/spec/x86_64/lx_syscall.S:29 #1 0x000000000109d7c1 in lx_futex (val=0, op=0, uaddr=<optimized out>) at /media/sf_kernel/genode/repos/base-linux/src/lib/syscall/linux_syscalls.h:350 #2 thread_stop_myself () at /media/sf_kernel/genode/repos/base-linux/src/include/base/internal/lock_helper.h:66 #3 Genode::Cancelable_lock::lock (this=this@...551...=0x80ffae0) at /media/sf_kernel/genode/repos/base/src/lib/base/lock.cc:123 #4 0x00000000010b19c8 in Genode::Lock::lock (this=<optimized out>) at /media/sf_kernel/genode/repos/base/include/base/lock.h:33 #5 Genode::sleep_forever () at /media/sf_kernel/genode/repos/base/src/lib/base/sleep.cc:21 #6 0x0000000001065eb8 in Genode::Signal_receiver::block_for_signal (this=this@...551...=0x64b4a70 Genode::bootstrap_component()::startup+3472) at /media/sf_kernel/genode/repos/base/src/core/signal_receiver.cc:49 #7 0x0000000001096a70 in Genode::Entrypoint::_process_incoming_signals (this=this@...551...=0x64b3e40 Genode::bootstrap_component()::startup+352) at /media/sf_kernel/genode/repos/base/src/lib/base/entrypoint.cc:108 #8 0x000000000109772e in Genode::Entrypoint::Entrypoint (this=0x64b3e40 Genode::bootstrap_component()::startup+352, env=...) at /media/sf_kernel/genode/repos/base/src/lib/base/entrypoint.cc:329 #9 0x00000000010915ac in Genode::Startup::Startup (this=0x64b3ce0 Genode::bootstrap_component()::startup) at /media/sf_kernel/genode/repos/base/src/lib/base/component.cc:244 #10 Genode::bootstrap_component () at /media/sf_kernel/genode/repos/base/src/lib/base/component.cc:259 #11 0x00000000010cd016 in _main () at /media/sf_kernel/genode/repos/base/src/lib/startup/_main.cc:248 #12 0x0000000000000000 in ?? ()
Regards, Johannes
Hi Johannes,
I implemented this but when I try to attach the dataspace I get a segfault. I though this might be related to a wrong physical address for the file but the backtrace leaves me completely clueless.
#0 pseudo_end () at /media/sf_kernel/genode/repos/base-linux/src/lib/syscall/spec/x86_64/lx_syscall.S:29 #1 0x000000000109d7c1 in lx_futex (val=0, op=0, uaddr=<optimized out>) at /media/sf_kernel/genode/repos/base-linux/src/lib/syscall/linux_syscalls.h:350 #2 thread_stop_myself () at /media/sf_kernel/genode/repos/base-linux/src/include/base/internal/lock_helper.h:66 #3 Genode::Cancelable_lock::lock (this=this@...551...=0x80ffae0) at /media/sf_kernel/genode/repos/base/src/lib/base/lock.cc:123 #4 0x00000000010b19c8 in Genode::Lock::lock (this=<optimized out>) at /media/sf_kernel/genode/repos/base/include/base/lock.h:33 #5 Genode::sleep_forever () at /media/sf_kernel/genode/repos/base/src/lib/base/sleep.cc:21 #6 0x0000000001065eb8 in Genode::Signal_receiver::block_for_signal (this=this@...551...=0x64b4a70 Genode::bootstrap_component()::startup+3472) at /media/sf_kernel/genode/repos/base/src/core/signal_receiver.cc:49 #7 0x0000000001096a70 in Genode::Entrypoint::_process_incoming_signals (this=this@...551...=0x64b3e40 Genode::bootstrap_component()::startup+352) at /media/sf_kernel/genode/repos/base/src/lib/base/entrypoint.cc:108 #8 0x000000000109772e in Genode::Entrypoint::Entrypoint (this=0x64b3e40 Genode::bootstrap_component()::startup+352, env=...) at /media/sf_kernel/genode/repos/base/src/lib/base/entrypoint.cc:329 #9 0x00000000010915ac in Genode::Startup::Startup (this=0x64b3ce0 Genode::bootstrap_component()::startup) at /media/sf_kernel/genode/repos/base/src/lib/base/component.cc:244 #10 Genode::bootstrap_component () at /media/sf_kernel/genode/repos/base/src/lib/base/component.cc:259 #11 0x00000000010cd016 in _main () at /media/sf_kernel/genode/repos/base/src/lib/startup/_main.cc:248 #12 0x0000000000000000 in ?? ()
are you sure that this is the backtrace of the faulting thread? It doesn't look like it. To double-check, have you had a close look at the instruction pointer within the faulting binary as reported by the kernel's dmesg output?
As another question, did you first try to hand out an FD for a regular file as I suggested in my last email? Just to rule out potential problems with the FD obtained from your kernel module.
The 'rm.attach' - when executed on Linux - is just a process-local operation. It comes down to an invocation of 'mmap' in 'region_map_mmap.cc'. Maybe it helps to instrument the code there? For instrumentation at such a low level, I recommend using the 'raw' function instead of 'log'. The 'raw' function directs the output directly to the kernel - not to core's LOG service. You won't see the component's name as log-message prefix, but rule out any interaction with core by the instrumentation.
Cheers Norman
Hi Norman,
I have tracked down the issue to the Io_mem_dataspace_capability being invalid. The segfault is caused by _dataspace_size() being called on an invalid capability in region_map_mmap.cc. I think a better behaviour would be to check the validity and throw an Invalid_dataspace() exception (I would create a PR for this change).
are you sure that this is the backtrace of the faulting thread? It doesn't look like it. To double-check, have you had a close look at the instruction pointer within the faulting binary as reported by the kernel's dmesg output?
I have taken a look at the IP and this supports the assumption about the invalid capability as the segfault happens in `Capability_space::ipc_cap_data`, especially in the inlined `return local_capability_space().ipc_cap_data(*cap.data());`
Capability_space::Ipc_cap_data Capability_space::ipc_cap_data(Native_capability const &cap) { 70b40: 53 push %rbx 70b41: 48 8d 05 f8 ff ff ff lea -0x8(%rip),%rax # 70b40 <_ZN6Genode16Capability_space12ipc_cap_dataERKNS_17Nativ e_capabilityE> _ZNK6Genode17Native_capability4dataEv(): /media/sf_kernel/genode/repos/base/include/base/native_capability.h:78 Data const *data() const { return _data; } 70b48: 48 8b 1f mov (%rdi),%rbx 70b4b: 49 bb 50 2f 05 00 00 movabs $0x52f50,%r11 70b52: 00 00 00 _ZN6Genode16Capability_space12ipc_cap_dataERKNS_17Native_capabilityE(): /media/sf_kernel/genode/repos/base/src/lib/base/capability_space.cc:81 return local_capability_space().ipc_cap_data(*cap.data()); 70b55: 48 ba 40 cb fa ff ff movabs $0xfffffffffffacb40,%rdx 70b5c: ff ff ff 70b5f: 4c 01 d8 add %r11,%rax 70b62: 48 01 d0 add %rdx,%rax 70b65: ff d0 callq *%rax 70b67: 48 8b 53 08 mov 0x8(%rbx),%rdx 70b6b: 8b 43 10 mov 0x10(%rbx),%eax /media/sf_kernel/genode/repos/base/src/lib/base/capability_space.cc:82 } 70b6e: 5b pop %rbx 70b6f: c3 retq
70b67 is the instruction that fails. I don't know if this helps you in any way.
As another question, did you first try to hand out an FD for a regular file as I suggested in my last email? Just to rule out potential problems with the FD obtained from your kernel module.
This happens no matter which file I have opened.
The 'rm.attach' - when executed on Linux - is just a process-local operation. It comes down to an invocation of 'mmap' in 'region_map_mmap.cc'. Maybe it helps to instrument the code there? For instrumentation at such a low level, I recommend using the 'raw' function instead of 'log'. The 'raw' function directs the output directly to the kernel - not to core's LOG service. You won't see the component's name as log-message prefix, but rule out any interaction with core by the instrumentation.
I looked into other implementations of base-linux that use the Dataspace_capability. They all just instanciate and return it. This is also the case for the Io_mem_dataspace_capability yet it seems to be invalid by default. I tried to return a Dataspace_capability but it can't be casted. As far as I understand it I have to either validate the Io_mem_dataspace_capability or cast to it.
Regards, Johannes
Hi Johannes,
I have tracked down the issue to the Io_mem_dataspace_capability being invalid. The segfault is caused by _dataspace_size() being called on an invalid capability in region_map_mmap.cc.
just a shot in the dark: Have you called 'Rpc_entrypoint::manage(ds)' for your dataspace object? Or to put the question differently: Is the dataspace capability that you return in 'Io_mem_session_component::dataspace()' valid inside core? If not, you have most likely missed to associate your 'Dataspace_component' object with core's entrypoint.
I looked into other implementations of base-linux that use the Dataspace_capability. They all just instanciate and return it. This is also the case for the Io_mem_dataspace_capability yet it seems to be invalid by default. I tried to return a Dataspace_capability but it can't be casted. As far as I understand it I have to either validate the Io_mem_dataspace_capability or cast to it.
Could you point me to a branch where I can have look at the code and reproduce the problem?
Cheers Norman
Hi Norman,
just a shot in the dark: Have you called 'Rpc_entrypoint::manage(ds)' for your dataspace object? Or to put the question differently: Is the dataspace capability that you return in 'Io_mem_session_component::dataspace()' valid inside core? If not, you have most likely missed to associate your 'Dataspace_component' object with core's entrypoint.
I had this issue already in the first email where I tried to do exaclty this. I read through it again and my explanation wasn't clear. When I add
_ds_cap = _ds_ep->manage(&_ds);
It fails to build with
In file included from /.../genode/repos/base/include/dataspace/capability.h:17:0, from /.../genode/repos/base/include/dataspace/client.h:17, from /.../genode/repos/base-linux/src/include/linux_dataspace/client.h:17, from /.../genode/repos/base-linux/src/core/io_mem_session_component.cc:15: /.../genode/repos/base/include/base/capability.h: In instantiation of âGenode::Untyped_capability Genode::Capability< <template-parameter-1-1> >::_check_compatibility(const Genode::Capability<FROM_RPC_INTERFACE>&) const [with FROM_RPC_INTERFACE = Genode::Linux_dataspace; RPC_INTERFACE = Genode::Io_mem_dataspace; Genode::Untyped_capability = Genode::Native_capability]â: /.../genode/repos/base/include/base/capability.h:153:49: required from âGenode::Capability< <template-parameter-1-1> >::Capability(const Genode::Capability<FROM_RPC_INTERFACE>&) [with FROM_RPC_INTERFACE = Genode::Linux_dataspace; RPC_INTERFACE = Genode::Io_mem_dataspace]â /.../genode/repos/base-linux/src/core/io_mem_session_component.cc:72:34: required from here /.../genode/repos/base/include/base/capability.h:134:24: error: cannot convert âGenode::Linux_dataspace*â to âGenode::Io_mem_dataspace*â in initialization RPC_INTERFACE *to = from;
I tried to circumvent this by trying different casts but I could not find an appropriate solution (fooling the compiler with typedefs is not one). When you said that opening the file and adding the file descriptor was enough I misinterpret that I don't need to call manage.
Could you point me to a branch where I can have look at the code and reproduce the problem?
I have a state on [1] that contains a test `run/io_mem` which tests that behaviour. I also have added a check to [2] so `attach` does not segfault but throws an `Invalid_dataspace` exception. In io_mem_session_component.cc I have already added (but commented out) the line to call manage which does not build yet.
Thanks for your help and regards, Johannes
[1]: https://github.com/jklmnn/genode/tree/ac59709d4af406125454180e5453f30184505a... [2]: https://github.com/jklmnn/genode/blob/ac59709d4af406125454180e5453f30184505a... [3]: https://github.com/jklmnn/genode/blob/ac59709d4af406125454180e5453f30184505a...
Hello Johannes,
On Thu, Jan 25, 2018 at 11:25:20AM +0100, Johannes Kliemann wrote:
_ds_cap = _ds_ep->manage(&_ds);
Looking at the generic implementation we learn that it also needs a cast after the manage
base/src/core/io_mem_session_component.cc:87: _ds_cap = static_cap_cast<Io_mem_dataspace>(_ds_ep->manage(&_ds));
For testing I adapted this to the current inheritance state in base-linux, which then compiles
base-linux/src/core/io_mem_session_component.cc:72: _ds_cap = static_cap_cast<Io_mem_dataspace>(static_cap_cast<Dataspace>(_ds_ep->manage(&_ds)));
So, the implementation currently prevents to provide Io_mem_dataspace as easy as Dataspace because there's no hook to tell the generic parts that a Dataspace is in fact a Linux_dataspace because Io_mem_dataspace directly inherits Dataspace. Sounds twisted, but that's the reason in my opinion and we need to do some refactoring to make the Io_mem-related modules tweakable in this regard.
Regards