I still don't have a very firm understanding of the mechanisms of
syscall() and the way it interfaces with things inside the kernel, but I
have managed to solve my problem.
My error (yes, my error again, I apologize) was that I didn't pay
attention to the names of the elements in the struct sysent which I
actually fill in to register my syscall handler in the kernel (look in
/usr/include/sys/systm.h). I should set sy_narg to reflect the number of
arguments I expect from userland. This is not totally true, though. See
below for details.
Even though I don't quite understand all the workings of syscall(), here
is a best-effort explanation of what happens.
1) syscall() in userland packs the parameters into 64-bit registers, so
passing 4 char would still only use one (half) register. When doing a
syscall(), you can't pass parameters on the stack, since you change stack
when switching to kernel mode. See the included reply for details on this
procedure.
2) In the kernel, there is some kind of preamble code which spits the
registers onto the stack, taking sy_narg registers (not in general the
same as the number of formal arguments) onto the stack.
3) My handler (installed in the sy_call element in the struct sysent),
which obviously works well with a prototype such as
int sy_call(struct proc *,void *args,int *return_value);
is called, and the arguments from userland are found at the location of
the second argument (args). You will have to pick the data off of the
stack using the assumption that arguments in userland are packed into
64-bit registers which are later pushed onto the stack.
4) When returning from the syscall, the return value I return with (my
return ESOMETHING; statement) will set errno, and the value I want to
return from the syscall should be written at *return_value. Typically, I
return a positive number and return 0 for success (*return_value =
positive_number; return ESUCCESS;), or if something fails (for example, I
can't MALLOC() memory) , I return the appropriate code and set the
return_value to -1 (*return_value = -1; return ENOMEM;).
I hope this can be of some value in the archives.
Special thanks to --
"J. Dean Brock" <brock_at_cs.unca.edu>
Serguei Patchkovskii <patchkov_at_ucalgary.ca>
Knut Hellebų <Knut.Hellebo_at_nho.hydro.com>
Included is the reply from Serguei wich gives the details on which
registers are used for what. (I think there can be six registers passed,
actually, judging from the header-file (which defines NUMSYSCALLARGS as
6), and for all I know, it may then be possible to pass 48 char arguments,
if you'd be inclined to do that).
----
From the user side, syscall() is very simple: all it does is to
store 0 (SYS_syscall) in R0, with the real system call number in R16. Up
to four (?) parameters are passed in R17-R19. Then it calls PAL with the
opcode 0x83, and that's it. Upon return, R19 is non-zero and R0 is set to
the error code if call failed, or R19 is zero and R0 is set to the return
value.
Unfortunately, I had never had a change to examine how this is
translated to the kernel's point of view...
----
Thanks
//Per
----
Per Boussard, KI/ERA/T/VU Office: +46 8 404 55 11
UNIX System Administrator Fax: +46 8 757 55 50
Ericsson Radio Systems AB Home: +46 8 570 349 67
S-164 80 STOCKHOLM, SWEDEN Email: Per.Boussard_at_era-t.ericsson.se
Received on Wed Dec 09 1998 - 10:24:03 NZDT