Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 27 Aug 2004 04:16:12 -0700
From:      Terry Lambert <tlambert2@mindspring.com>
To:        Mmaist <mmaist@email.it>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: syscalls implementation
Message-ID:  <412F17FC.E856A234@mindspring.com>
References:  <fb356c20f5bbebc5f8888312d44f11b5@213.203.147.246>

next in thread | previous in thread | raw e-mail | index | archive | help
Mmaist wrote:
> Hi!
> I was wondering were syscalls implementation is in the FreeBSD source tree.
> I would like to know, especially, where
> 
> int kldload(const char*);
> 
> is located. sys/kern/kern_linker.c contains
> 
> int
> kldload(struct thread *, struct kldload_args *)
> 
> and I need to watch at what called between them.

This is probably the wrong list.  You probably want -questions.

Here's the simplest answer you will probably understand, given that the
question you are really asking is about understanding how system calls
work, and where baby system calls come from.  8-).

The system calls are in code that's generated from the file
/usr/src/sys/kern/syscalls.master by /usr/src/sys/kern/maksyscalls.sh,
which is an awk script encapsulated in a shell script.

In user space, the system calls are stubs in a library that traps into
the vector code generated from syscalls.master in the kernel.  This
code is located in /usr/src/lib/libc.  In the case os system calls
that are unwrapped (like kldload(2)), the calls are generated assembly
code that comes from running a script against the same syscalls.master
file describd earlier; see src/lib/libc/sys/Makefile.inc for the exact
place in the source tree that these stubs are being generated from.

The way a system call happens is that the arguments are pushed on the
stack (or put into registers, depending), and then a trap is issued by
attempting to execute a supervisor-only instruction in user code.  This
effectively generates a fault which is then serviced by a fault handler
in the kernel which recognizes the particular trap as "special", and
treats it as a system call.

Now when the special trap code in the kernel itself is activated, it
packages up the arguments in a structure of a known size (known to the
code in init_sysent.c, generated from syscalls.master).

For the Intel version of the trap code, see /usr/src/sys/i386/i386/trap.c
and the related assembly code there.  The current thread and the packaged
argument pointer are passed to the system call.

Techinically, it makes a lot of things difficult that the current thread
is passed into the system call as part of its context, rather than obtained
when needed, and cached locally, if necessary, but since it's handy at the
time, it's passed in.  On non-register-poor architectures, the current
thread is usually made available in a register, so the cost in obtaining it
later is actually lss than a memory dereference of a computed stack offset,
as it is/was (depends on the version, architecture, etc.) in FreeBSD because
it's being passed in.  On of the major things this makes difficult is
accurate proxy credential representation at various points in the kernel (see
the NFS server source code for examples of the contorted logic this makes
necessary).

The packages argument structures are defined in the (also generated from
the syscalls.master) file /usr/src/sys/sys/sysproto.h.  For the kldload
system call, this looks like:

struct kldload_args {
  char file_l_[PADL_(const char *)]; const char * file; char
file_r_[PADR_(const char *)];
};

This argument structure is actually a dscriptor.  A descriptor is used to
ensure packing and alignment, so that the user stack save area can be
coerced directly to the structure type, and dereferenced in the function.

The descriptor contains the information necessary to line the structure
contents up with the user stack area and/or register spill area, where the
arguments were stored in user space before the trap.  Mostly, you can just
look at the middle element to know what the argument is, for each line of
arguments.  In this case, it's a "const char * file".  This matches what
it was in user space when you made the call.

So the function you are seeing in the kernel:

	int kldload(struct thread *, struct kldload_args *);

*is* the system call you saw in user space:

	int kldload(const char*);

with the trap added thread pointer and pointer to the packed up save area
containing the same char * value you passed in user space.

Note that since the user and kernel space are not necessarily in core at
the same time (maybe the pointer you pointed to was in a page that was
swapped out), so you have to use copyin/uiomove in the function in the
kernel to copy the path in before it can be used in the kernel address
space.

Probably you should not be hacking in this area until you understand the
code operation a little better, since unless you know what you are doing,
most changes you could possibly make will leave you with a dead system.

-- Terry



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?412F17FC.E856A234>