NAME
sysarch.x86 – architecture-dependent system call on x86
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <machine/sysarch.h>
int
sysarch(int number, void *args);
DESCRIPTION
The manual page describes the functions provided by the sysarch() system call on x86 architectures, that is i386 and amd64, see arch(7).
Parameter Types
The following structures are used as the arguments pointed to by the args parameter. Refer to the description of each request for the meaning of individual fields:
The I386_GET_IOPERM and I386_SET_IOPERM requests take a struct i386_ioperm_args, which is defined as follows:
struct i386_ioperm_args {
unsigned int start;
unsigned int length;
int enable;
};
The I386_GET_LDT and I386_SET_LDT requests take a struct i386_ldt_args, whose definition differs between i386 and amd64. The i386 variant is:
struct i386_ldt_args {
unsigned int start;
union descriptor *descs;
unsigned int num;
};
The amd64 variant is:
struct i386_ldt_args {
unsigned int start;
struct user_segment_descriptor *descs __packed;
unsigned int num;
};
The request I386_VM86 is only supported by i386 kernels and takes a
struct i386_vm86_args {
int sub_op;
char *sub_args;
};
The I386_GET_XFPUSTATE, takes a struct i386_get_xfpustate argument, a AMD64_GET_XFPUSTATE struct amd64_get_xfpustate argument. The structures have the same members, but their layout differs due to pointer size differences. Only the amd64 variant is listed for clarity.
struct amd64_get_xfpustate {
void *addr;
int len;
};
The I386_GET_PKRU, I386_SET_PKRU, take a struct i386_set_pkru argument, and AMD64_GET_PKRU and AMD64_SET_PKRU take a struct amd64_set_pkru argument. The structures have the same members, but their layout differs due to pointer size differences. Only the amd64 variant is listed for clarity.
struct amd64_set_pkru {
void *addr;
unsigned long len;
unsigned int keyidx;
int flags;
};
Requests
The following requests can be specified by the number argument:
- I386_GET_LDT
-
Queries the specified range of LDT descriptors for the calling process.
The request takes a pointer to the struct i386_ldt_args structure as the args parameter. Its start member specifies the starting index, and num the number of descriptors which are queried. The LDT descriptors in the requested range are copied into the memory specified by the descs member.
- I386_SET_LDT
-
Set the specified range of LDT descriptors for the calling process.
LDT is process-global, all process threads share the same table. On the other hand, if several processes share an address space, they still each have their own LDT.
The request takes a pointer to the struct i386_ldt_args structure as the args parameter. Its start member specifies the starting index, and num the number of descriptors which are set. The LDT descriptors in the requested range are initialized from the structures pointed to by the descs member.
If the descs member value is NULL, the LDT descriptors are reset to null descriptors. If the start starting descriptor index has the special value LDT_AUTO_ALLOC and the number of descriptors to allocate is 1, then the system looks for a free descriptor index and allocates it. The resulting index is returned as the system call return value.
Only memory read or read/write normal or expand down, and execute only or execute read segment types are allowed. Any system segments descriptors or memory conforming execute segments are rejected with the EACCESS error.
X86 hardware supports LDTs with up to 8192 descriptors. This size is excessive for modern applications. The machdep.max_ldt_segment loader tunable sets a system-global maximum LDT size (in number of descriptors).
- I386_GET_IOPERM
-
Queries the state of userspace access to the specified range of I/O ports for the calling thread.
The function takes a struct i386_ioperm_args structure as the args argument. The start member is the starting port number to query. Upon successful return, the enable member records the access permissions for the starting port. The length member indicates how many ports after the starting port number have the same access permissions.
For instance, if start was set to 10, and the request set the length field to 3, with enable set to 1, then access to ports 10, 11, and 12 is allowed, while access to port 13 is disallowed.
- I386_SET_IOPERM
-
Grants or revokes userspace access to I/O ports by modifying the TSS I/O permission bitmap for the calling thread. Each thread maintains a separate bitmap.
The function takes a struct i386_ioperm_args structure as the args argument. Depending on the enable member value, it either grants (enables) or revokes (disables) access to the range of I/O ports specified by the start and length members.
There is total of 65536 ports, so the set of ports specified must fit into the 0..65535 range.
The call requires super-user privileges.
- I386_VM86
-
Execute CPU instructions in virtual 8086 (VM86) mode. This function is only supported by i386 kernels as VM86 mode is not supported in long mode. To use this request, the additional header ⟨machine/vm86.h⟩ must be included which exists only for i386.
The request takes a struct i386_vm86_args structure as the args argument. Its sub_op member specifies one of the following sub-requests:
- VM86_INIT
-
Initialize the VM86 in-kernel extended pcb. Without this initialization, VM86 mode cannot be entered.
The sub-request takes the struct vm86_init_args additional argument pointed to by the sub_args pointer in the struct i386_vm86_args structure.
Currently only the int_map member of the structure is implemented. Each bit N in the int_map bit-string set to 1 causes a SIGTRAP signal to be delivered to the thread on the execution of the INT N instruction. Otherwise, if the bit is zero, the execution of the INT N instruction is reflected to VM86 mode to the interrupt handler as designated by the real-mode interrupt table.
- VM86_SET_VME
-
Reserved but not implemented.
- VM86_GET_VME
-
Returns the state of the VME extensions. The sub_args must point to a struct vm86_vme_args structure.
- VM86_INTCALL
-
Arrange execution in VM86 mode as if the specified interrupt was delivered. Takes a struct vm86_intcall_args argument as the sub_args pointer. The intnum member of it provides the interrupt number to emulate delivery of, and the vmf gives the VM86 mode machine context to load for the interrupt handler.
To switch thread to the VM86 execution mode, several preparations must be done:
-
An alternative signal stack must be created, since otherwise signal delivery in VM86 mode would try to use VM86 stack. See sigaltstack(2).
-
The VM86 mode kernel state must be initialized with the VM86_INIT sub-request.
-
The memory must be mapped in the first megabyte of the user address space, which will be accessible to the VM86 mode execution.
-
Signal handlers must be established that can handle VM86 exceptions. Most importantly, the SIGSEGV handler must be provided to assist VM86 code to handle exits due to IOPL-sensitive instructions.
-
Finally, the VM86 mode can be entered by switching to the context with %eflags register having the eflags.VM bit set. This can be done, among other ways, by loading the full machine context with the sigreturn(2) system call.
The thread continues to execute in VM86 mode until a signal is delivered to it. Note that exceptions generate appropriate signals the same as for protected execution mode.
See src:tools/tests/vm86 for a minimal example for a VM86 monitor and sample code.
-
- I386_GET_FSBASE
-
Read the 32-bit fsbase context value for the calling thread into a uint32_t variable pointed to by the args argument.
- I386_SET_FSBASE
-
Set the 32-bit fsbase context value for the calling thread. The args argument must point to a int32_t variable that has the new fsbase value stored.
The fsbase is maintained by writing the fsbase value into the base field of the user 32-bit %fs GDT descriptor, which has index 2.
- I386_GET_GSBASE
-
Read the 32-bit gsbase context value for the calling thread into a int32_t variable pointed to by the args argument.
- I386_SET_GSBASE
-
Set the 32-bit gsbase context value for the calling thread. The args argument must point to a int32_t variable that has the new gsbase value stored.
The gsbase is maintained by writing the gsbase value into the base field of the user 32-bit %gs GDT descriptor, which has index 3.
- I386_GET_XFPUSTATE
-
Same as AMD64_GET_XFPUSTATE, but takes the address of the struct i386_get_xfpustate structure as the args argument.
- I386_SET_PKRU
-
Provided only when running i386 binaries on the amd64 host kernel.
See description of the AMD64_SET_PKRU request.
- I386_CLEAR_PKRU
-
Provided only when running i386 binaries on the amd64 host kernel.
See description of the AMD64_CLEAR_PKRU request.
- I386_SET_SPLITLOCK
-
Change the requesting thread splitlock detection control. For i386 binaries, the function is only available when running on the amd64 host kernel.
The argument must point to the 32bit int variable, holding one of the following values:
- 0
-
Disable detection of split locking.
- 1
-
Enable detection of split locking.
The split lock is the situation where the read-modify-write atomic instruction is performed on the memory location that is split over more than one CPU cache line. Some CPUs can be configured to generate the Alignment Fault exception ‘#AC’ if split locking is detected. The request makes it possible for the thread to control the detector.
The _umtx_op(2) operations that pass unaligned locks to kernel requests returns EFAULT when splitlock detection is enabled.
Presence of the detector can be queried with the hw.splitlock sysctl(2) MIB.
- I386_GET_SPLITLOCK
-
Returns the state of the splitlock detection control. For i386 binaries, the function is only available when running on the amd64 host kernel.
The argument must point to the 32bit int variable. The function writes the value 0 if the control is disabled and 1 if enabled.
- AMD64_GET_FSBASE
-
Returns the value of the fsbase register for the calling thread. The args argument must point to the 64bit unsigned long variable where the value is written.
On CPUs that do not support the RDFSBASE instruction, the call is the only way to read the current value of fsbase.
- AMD64_SET_FSBASE
-
Sets the fsbase register to the 64bit value pointed to by the args argument.
The value is checked to not point outside of the allowed user address space, which is subset of possible canonical addresses. If the WRFSBASE instruction is supported, the fsbase register can be updated either by the call or by the instruction. For 64bit processes, the fsbase register is always restored on return to user mode from kernel calls or interrupts, regardless of the value of the %fs segment register.
- AMD64_GET_GSBASE
-
Get the gsbase register, see description of AMD64_GET_FSBASE.
- AMD64_SET_GSBASE
-
Set the gsbase register, see description of AMD64_GET_GSBASE.
- AMD64_GET_XFPUSTATE
-
Fills the extended FPU state as defined by the architecture and the running CPU hardware into the memory described by the struct amd64_get_xfpustate structure. The args argument must point to the structure, the extended state (that is, full XSAVE area excluding the legacy region) is written to the addr up to the len bytes. If the len member value is smaller then the XSAVE area size, the result is truncated.
- AMD64_SET_PKRU
-
Set the 4bit protection key for the page table entries used to map the given userspace virtual address range.
The request takes the struct amd64_set_pkru structure pointed to by the args parameter. The addr and len members of the structure specify starting address and the length in bytes of the region. The keyidx member value must be between 0 and 15 and provides the protection key index applied to the region. The flags member is or-ed with the following flags:
- AMD64_PKRU_EXCL
-
Requires that no protection key was assigned to the specified range, or previously assigned key cleared. If some key was already applied to any part of the range, the call fails with the EBUSY error.
- AMD64_PKRU_PERSIST
-
Makes the assigned key persistent. It is automatically re-applied to any new mapping created in the range.
Protection keys feature is optional and depends on CPU implementing it. Use the CPUID instruction to check its availability.
The request is used to implement the x86_pkru_protect_range(3) function.
- AMD64_CLEAR_PKRU
-
Clear any protection keys assigned to the specified region.
The request takes the struct amd64_set_pkru structure pointed to by the args parameter. The addr and len members of the structure specify starting address and the length in bytes of the region. Other structure members are ignored.
The request is used to implement the x86_pkru_unprotect_range(3) function.
- AMD64_GET_TLSBASE
-
AMD64 only.
Returns the current TLSBASE value for the calling thread. The args argument must point to the 64bit unsigned long variable where the value is written to.
- AMD64_SET_TLSBASE
-
Sets the TLSBASE and the fsbase register values for the calling thread. The args argument must point to the 64bit unsigned long variable where the value is taken from.
The TLSBASE value is written into the fsbase register before a signal handler is invoked. The current fsbase value is saved, and then restored upon return from the signal handler. Use of TLSBASE makes it possible for programs that modify the fsbase register using the WRFSBASE instruction to reliably access thread-local variables in signal handlers.
TLSBASE is set up by the FreeBSD C runtime environment on thread startup.
- AMD64_DISABLE_TLSBASE
-
Disables loading the fsbase register from TLSBASE value on a signal handler invocation. The argument args is ignored.
The call allows to undo the effect of the AMD64_SET_TLSBASE, making signal handlers retain the value of fsbase from the interrupted context. By default, tlsbase is disabled after the execve(2) syscall activated a new image.
RETURN VALUES
For all requests except I386_GET_LDT and I386_SET_LDT, upon successful completion, the value 0 is returned; otherwise the value -1 is returned and the global variable errno is set to indicate the error.
The I386_SET_LDT request returns the starting index of the descriptors range it operated on, on success. In particular, the index of the automatically allocated descriptor is reported this way. Otherwise, -1 is returned and errno is set.
The I386_GET_LDT request returns 0 if the specified descriptors range is completely outside the allocated LDT. On any other error, -1 is returned and errno is set. Otherwise the number of reported descriptors is returned as the syscall return value.
SEE ALSO
i386_get_ioperm(2), i386_get_ldt(2), i386_vm86(2), sysarch(2), pkru(3), arch(7)
HISTORY
The manual page appeared in FreeBSD 16.0.
AUTHORS
The manual page was written by Konstantin Belousov ⟨[email protected]⟩.