-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
userspace: Support for split 64 bit arguments #18064
Conversation
Whoops, clicked on "open" before I'd pasted the description. Hopefully got it editted before the email goes out. This is working now. It was done in the context of the timer API work to support a variable-sized k_timeout_t, but is submitted here for easier review as it has nothing in particular to do with timing. |
All checks are passing now. Review history of this comment for details about previous failed status. |
I would like to see this happen.
I'd like to see a PR with existing system calls converted to this mechanism.
Is it really "OK for now?" |
Just to make my review clear: I don't want two different syscall generation mechanisms present in Zephyr simultaneously, and the new mechanism should be at functional parity with the old one. If I understand this code correctly, it should be mechanical to replace instances of Z_SYSCALL_HANDLER() with the new scheme, as the body of the syscall handlers shouldn't have to change, just the function prototypes. Doing this will remove the need to maintain two mechanisms and more extensively show that this actually works and is at parity with the old mechanism. I also don't want to see inline syscall implementations dragged down to non-inline status, I think the <syscalls/xxxxx.h> mechanism should be updated to handle this correctly. Finally, the syscall documentation needs to be updated, it currently describes the old mechanism only. |
OK, will work up a patch series to do it universally everywhere. And getting it inlining won't be difficult, just wordy (FWIW: the flaw is just the inlining of impl into the kernel-mode handler. Any other code that can inline it now will continue to work, it's just that during an actual syscall, we need to make an extra function call. This seemed tolerable enough given the overhead we're already taking, but it's not hard to fix). As for ssf, yeah, that's a design flaw that it got forgotten. Have it working now by caching the value in the CPU struct during syscall execution so that you can Z_OOPS() from anywhere down the call stack. |
edd1e74
to
e894c3e
Compare
As promised, here's one that reworks the whole system to use the new mrsh/vrfy scheme, with doc changes and removal of most of the legacy code (like all good features, this removes more code than it adds, heh). Well... almost. It includes every syscall that gets hit during execution of a top-level sanitycheck. There are a few dozen that don't and are unmodified in this patch. I'll have those in within a few hours, in separate patches that carry warnings about blind changes (most of the changes are routine and scriptable). Currently trying to figure out a rig to at least get some build coverage in a pseudoautomatic way. |
@@ -353,6 +357,7 @@ Z_SYSCALL_HANDLER(zsock_connect, sock, addr, addrlen) | |||
return z_impl_zsock_connect(sock, (struct sockaddr *)&dest_addr_copy, | |||
addrlen); | |||
} | |||
#include <syscalls/zsock_connect_mrsh.c> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a quick question, as I'm still reviewing this and haven't wrapped my head completely around how the build in this new scheme works. Instead of having to #include a separate C file for every syscall implemented, would it be possible to just put all the syscall marshalling functions in a single C file which gets built and linked in? or is there something specific which requires each marshalling function to be included in this way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the way the original scheme worked, actually (they were in syscall_dispatch.c). But it has the side effect of being unable to inline the impl function unless it is in a header. This way they can live wherever the user wants them.
Personally I don't think that extra function call per actually-taken-syscall is a big deal, so I'd be happy with either scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I don't think that extra function call per actually-taken-syscall is a big deal, so I'd be happy with either scheme.
If I understand you correctly I am fine with that also and would rather just put them all in a single C dispatch file.
The inlining I was worried about was supervisor mode calls to syscall APIs, which are currently inlined if the z_impl_xxxx() function for it is declared inline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to putting the marshalling functions to separate C file.
Also, why not use the macro to create the function name so instead of this
static inline int z_vrfy_zsock_listen(int sock, int backlog)
we could have this
static int Z_SYSCALL_HANDLER(zsock_listen, int sock, int backlog)
so user does not need to know / remember how the function should be named.
Fix milestone. This is late, and scope grew at the last minute, but we need it in for the k_timeout_t stuff. |
kernel/sem.c
Outdated
|
||
static inline unsigned int z_vrfy_k_sem_count_get(struct k_sem *sem) | ||
{ | ||
printk("%s @ %p\n", __func__, sem); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, debug code left in by mistake. Will fix in next version.
that'sFrustratingly, eliminating the "include mrsh file" scheme is not going to work everywhere. There are two problems: the minor one is a code size increase, because if we have a custom-generated handler for even disabled syscalls, that's a few hundred bytes extra vs. just putting handler_no_syscall in that slot. The second one is harder. Syscalls have typed arguments, and to generate code for them in the general case means that we need to include the headers that define those types. I made this work. But then the problem is that these headers often don't compile in the wrong kconfig environment (i.e. if networking isn't defined, the network headers blow up). These are bugs, arguably, but they're looking to be difficult to fix in the immediate term. I think we need to use the "include the mrsh file" scheme in the general case. There's really no other way to give the user control over the compilation environment of the handler. How about this: we can commit this using the current scheme, and I'll submit an issue for 2.1 for a __syscall_simple() annotation that can generate the vrfy function also for common cases: we can detect the type of kobjects from the signature already, we can provide an annotation for struct device subtypes, we can even auto-generate copies of fixed-size pointer blocks. And using the two-stage link (which I haven't looked at since before cmake landed) we can even trim the list of generated syscalls to just those seen in linked code. I have lots of ideas. :) Basically most stuff would port to that, except complicated handlers, which would still use hand-written vrfy functions and use the mrsh include. But those are the cases where the new code helps the most already, so it would still be a win. Sound OK? |
757aac3
to
d9e3ba9
Compare
doc/reference/usermode/syscalls.rst
Outdated
{ | ||
struct _syscall_9_args *margs = (struct _syscall_9_args *)more_args_ptr; | ||
All system calls are dispatched to a verifier function with a prefixed | ||
z_vrfy_ name based on the system call. They have exactly the same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in reST, a trailing underscore indicates an outgoing link, hence the doc build error saying there's no link target for the name z_vrfy
. The way to fix this is to change this reference to:
... with a prefixed ``z_vrfy_`` name
doc/reference/usermode/syscalls.rst
Outdated
@@ -135,6 +135,8 @@ the project out directory under ``include/generated/``: | |||
API call, but the sensor subsystem is not enabled, the weak handler | |||
will be invoked instead. | |||
|
|||
* An unmarshalling function will be defined in ``include/generated/<name>_mrsh.c`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the other bullets are expressed in the present tense (is created
), this would read better if it were too:
* An unmarshalling function is defined ...
(unless this generated function really will be defined in the future :)
doc/reference/usermode/syscalls.rst
Outdated
untrusted memory in the handler function. This is done by the derived | ||
functions :c:func:`_syscall_invoke7` through :c:func:`_syscall_invoke10`. | ||
Some system calls may have more than six arguments. The number of | ||
arguments passed via registers is fixed at six for all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it fixed at six
(meaning all system calls pass six arguments) or should it say limited to six
?
doc/reference/usermode/syscalls.rst
Outdated
:c:func:`_syscall_ret64_invoke1`. | ||
Some system calls may return a value that will not fit in a 32-bit | ||
register, such as APIs that return a 64-bit value. In this scenario, | ||
the return value is populated in a (untrusted!) memory buffer that is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about, populated in an **untrusted** memory buffer
(use bold for emphasis instead of the parentheses and exclamation point.
d9e3ba9
to
6117c6b
Compare
@dbkinder docs updated, please recheck? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for doc changes, thanks!
I noted that 64-bit return value always goes through memory. |
@pizi-nordic: That's just the way we've defined the ABI. The architecture syscall hooks are C functions that take 0-6 word arguments (actually it's hard-coded to u32_t right now, though that's easy enough to change once we start supporting syscalls on a 64 bit target) and return one output word. Adding a new scheme with more than one output word would require every supporting architecture to write new hooks, so it's a heavy-weight change. But maybe worth it long term. |
@andyross: Ok. IMHO returning data in registers will be faster (because most of ABIs the already use registers to pass the 64-bit return value) and safer (no write from kernel to userspace). |
Agreed. But that's not what this PR is about, it's taking the existing syscall ABI (i.e. not changing any arch code) and changing the way we emit handlers for them so that more of the code can be automatically generated. Long term I agree that we should look at improvements to the arch interface as well. |
6117c6b
to
9a3320f
Compare
Routine rebase. Picked up support for the logging subsystem syscalls that were just added. |
9a3320f
to
88e2e56
Compare
Rebase again. No changes, just keeping it fresh for an immediate post-release merge |
System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
Add some simple tests of the new code generation for syscalls with 64 bit arguments. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
These calls are buildable on common sanitycheck platforms, but are not invoked at runtime in any tests accessible to CI. The changes are mostly mechanical, so the risk is low, but this commit is separated from the main API change to allow for more careful review. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
These calls are not accessible in CI test, nor do they get built on common platforms (in at least one case I found a typo which proved the code was truly unused). These changes are blind, so live in a separate commit. But the nature of the port is mechanical, all other syscalls in the system work fine, and any errors should be easily corrected. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
The semi-automated API changes weren't checkpatch aware. Fix up whitespace warnings that snuck into the previous patches. Really this should be squashed, but that's somewhat difficult given the structure of the series. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
88e2e56
to
5b9d9bf
Compare
Rebase again to fix the collision with #18744 |
Ping. Can we get this in now? |
userspace: Support for split 64 bit arguments
System call arguments, at the arch layer, are single words. So
passing wider values requires splitting them into two registers at
call time. This gets even more complicated for values (e.g
k_timeout_t) that may have different sizes depending on configuration.
This patch adds a feature to gen_syscalls.py to detect functions with
wide arguments and automatically generates code to split/unsplit them.
Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't
work with functions like this, because for N arguments (our current
maximum N is 10) there are 2^N possible configurations of argument
widths. So this generates the complete functions for each handler and
wrapper, effectively doing in python what was originally done in the
preprocessor.
Another complexity is that traditional the z_hdlr_() function for a
system call has taken the raw list of word arguments, which does not
work when some of those arguments must be 64 bit types. So instead of
using a single Z_SYSCALL_HANDLER macro, this splits the job of
z_hdlr_() into two steps: An automatically-generated unmarshalling
function, z_mrsh_(), which then calls a user-supplied verification
function z_vrfy_(). The verification function is typesafe, and is a
simple C function with exactly the same argument and return signature
as the syscall impl function. It is also not responsible for
validating the pointers to the extra parameter array or a wide return
value, that code gets automatically generated.
One downside is that the generated dispatch "mrsh" handler currently
needs to live in the syscall_dispatch.c file. So it's only able to
inline the vrfy/impl functions if those appear in the syscall header.
Implementations defined in C source files need to be called as real
functions. Long term, we could fix this by emitting a new header that
can be included at the bottom of C files containing syscalls
(analogous to the way the syscalls/*.h headers work). But this seems
OK for now.
Note that in theory, this new code is capable of generating wrappers
and handlers for any system call, there's nothing specific to 64 bit
arguments per se. In theory any system call could use that, and the
z_vrfy() scheme, with an appropriate way to tag it. Alternatively we
could consider migrating all the existing syscalls to this mechanism,
which would allow us to drop almost all the existing macro code and
generation.