xref: /freebsd/lib/libsys/x86/sysarch.x86.2 (revision bdcfeac3e1cb8ce3f05c32d8a7ad3597deb48bdf)
1.\" SPDX-License-Identifier: BSD-2-Clause
2.\"
3.\" Copyright 2026 The FreeBSD Foundation.
4.\"
5.\" This documentation was written by Konstantin Belousov <kib@FreeBSD.org>
6.\" under sponsorship of The FreeBSD Foundation.
7.\"
8.Dd September 26, 2026
9.Dt SYSARCH.X86 2 i386
10.Os
11.Sh NAME
12.Nm sysarch.x86
13.Nd architecture-dependent system call on x86
14.Sh LIBRARY
15.Lb libc
16.Sh SYNOPSIS
17.In machine/sysarch.h
18.Ft int
19.Fn sysarch "int number" "void *args"
20.Sh DESCRIPTION
21The
22manual page describes the functions provided by the
23.Fn sysarch
24system call on x86 architectures, that is i386 and amd64, see
25.Xr arch 7 .
26.Ss Parameter Types
27The following structures are used as the arguments pointed to by the
28.Fa args
29parameter.
30Refer to the description of each request for the meaning of
31individual fields:
32.Pp
33The
34.Dv I386_GET_IOPERM
35and
36.Dv I386_SET_IOPERM
37requests take a
38.Vt struct i386_ioperm_args ,
39which is defined as follows:
40.Bd -literal
41struct i386_ioperm_args {
42	unsigned int start;
43	unsigned int length;
44	int	enable;
45};
46.Ed
47.Pp
48The
49.Dv I386_GET_LDT
50and
51.Dv I386_SET_LDT
52requests take a
53.Vt struct i386_ldt_args ,
54whose definition differs between i386 and amd64.
55The i386 variant is:
56.Bd -literal
57struct i386_ldt_args {
58	unsigned int start;
59	union descriptor *descs;
60	unsigned int num;
61};
62.Ed
63The amd64 variant is:
64.Bd -literal
65struct i386_ldt_args {
66	unsigned int start;
67	struct user_segment_descriptor *descs __packed;
68	unsigned int num;
69};
70.Ed
71.Pp
72The request
73.Dv I386_VM86
74is only supported by i386 kernels and takes a
75.Bd -literal
76struct i386_vm86_args {
77	int	sub_op;
78	char	*sub_args;
79};
80.Ed
81.Pp
82The
83.Dv I386_GET_XFPUSTATE ,
84takes a
85.Vt struct i386_get_xfpustate
86argument, a
87.Dv AMD64_GET_XFPUSTATE
88.Vt struct amd64_get_xfpustate
89argument.
90The structures have the same members, but their layout differs due to
91pointer size differences.
92Only the amd64 variant is listed for clarity.
93.Bd -literal
94struct amd64_get_xfpustate {
95	void *addr;
96	int len;
97};
98.Ed
99.Pp
100The
101.Dv I386_GET_PKRU ,
102.Dv I386_SET_PKRU ,
103take a
104.Vt struct i386_set_pkru
105argument, and
106.Dv AMD64_GET_PKRU
107and
108.Dv AMD64_SET_PKRU
109take a
110.Vt struct amd64_set_pkru
111argument.
112The structures have the same members, but their layout differs due to
113pointer size differences.
114Only the amd64 variant is listed for clarity.
115.Bd -literal
116struct amd64_set_pkru {
117	void *addr;
118	unsigned long len;
119	unsigned int keyidx;
120	int flags;
121};
122.Ed
123.Ss Requests
124The following requests can be specified by the
125.Fa number
126argument:
127.Bl -tag -width AMD64_DISABLE_TLSBASE
128.It Dv I386_GET_LDT
129Queries the specified range of LDT descriptors for the
130calling process.
131.Pp
132The request takes a pointer to the
133.Vt struct i386_ldt_args
134structure as the
135.Fa args
136parameter.
137Its
138.Va start
139member specifies the starting index, and
140.Va num
141the number of descriptors which are queried.
142The LDT descriptors in the requested range are copied into
143the memory specified by the
144.Fa descs
145member.
146.It Dv I386_SET_LDT
147Set the specified range of LDT descriptors for the calling process.
148.Pp
149LDT is process-global, all process threads share the same table.
150On the other hand, if several processes share an address space,
151they still each have their own LDT.
152.Pp
153The request takes a pointer to the
154.Vt struct i386_ldt_args
155structure as the
156.Fa args
157parameter.
158Its
159.Va start
160member specifies the starting index, and
161.Va num
162the number of descriptors which are set.
163The LDT descriptors in the requested range
164are initialized from the structures pointed to by the
165.Fa descs
166member.
167.Pp
168If the
169.Va descs
170member value is
171.Dv NULL,
172the LDT descriptors are reset to null descriptors.
173If the
174.Va start
175starting descriptor index has the special value
176.Dv LDT_AUTO_ALLOC
177and the number of descriptors to allocate is 1, then the system looks for
178a free descriptor index and allocates it.
179The resulting index is returned as the system call return value.
180.Pp
181Only memory read or read/write normal or expand down, and execute only
182or execute read segment types are allowed.
183Any system segments descriptors or memory conforming execute segments
184are rejected with the
185.Ev EACCESS
186error.
187.Pp
188X86 hardware supports LDTs with up to 8192 descriptors.
189This size is excessive for modern applications.
190The
191.Dv machdep.max_ldt_segment
192loader tunable sets a system-global maximum LDT size
193(in number of descriptors).
194.It Dv I386_GET_IOPERM
195Queries the state of userspace access to the specified range of
196I/O ports for the calling thread.
197.Pp
198The function takes a
199.Vt struct i386_ioperm_args
200structure as the
201.Fa args
202argument.
203The
204.Va start
205member is the starting port number to query.
206Upon successful return, the
207.Va enable
208member records the access permissions for the starting port.
209The
210.Va length
211member indicates how many ports after the starting port number
212have the same access permissions.
213.Pp
214For instance, if
215.Va start
216was set to 10,
217and the request set the
218.Va length
219field to 3, with
220.Va enable
221set to 1, then access to ports 10, 11, and 12 is allowed, while
222access to port 13 is disallowed.
223.It Dv I386_SET_IOPERM
224Grants or revokes userspace access to I/O ports by modifying the TSS
225I/O permission bitmap for the calling thread.
226Each thread maintains a separate bitmap.
227.Pp
228The function takes a
229.Vt struct i386_ioperm_args
230structure as the
231.Fa args
232argument.
233Depending on the
234.Va enable
235member value, it either grants (enables) or revokes (disables)
236access to the range of
237I/O ports specified by the
238.Va start
239and
240.Va length
241members.
242.Pp
243There is total of 65536 ports, so the set of ports specified must fit
244into the 0..65535 range.
245.Pp
246The call requires super-user privileges.
247.It Dv I386_VM86
248Execute CPU instructions in virtual 8086 (VM86) mode.
249This function is only supported by i386 kernels as VM86 mode
250is not supported in long mode.
251To use this request, the additional header
252.In machine/vm86.h
253must be included which exists only for i386.
254.Pp
255The request takes a
256.Vt struct i386_vm86_args
257structure as the
258.Fa args
259argument.
260Its
261.Va sub_op
262member specifies one of the following sub-requests:
263.Pp
264.Bl -tag -width VM86_SET_VME
265.It Dv VM86_INIT
266Initialize the VM86 in-kernel extended pcb.
267Without this initialization, VM86 mode cannot be entered.
268.Pp
269The sub-request takes the
270.Vt struct vm86_init_args
271additional argument pointed to by the
272.Fa sub_args
273pointer in the
274.Vt struct i386_vm86_args
275structure.
276.Pp
277Currently only the
278.Va int_map
279member of the structure is implemented.
280Each bit N in the
281.Va int_map
282bit-string set to 1 causes a
283.Dv SIGTRAP
284signal to be delivered to the thread on the execution
285of the
286.Dv INT N
287instruction.
288Otherwise, if the bit is zero, the execution of the
289.Dv INT N
290instruction is reflected to VM86 mode to the interrupt
291handler as designated by the real-mode interrupt table.
292.It Dv VM86_SET_VME
293Reserved but not implemented.
294.It Dv VM86_GET_VME
295Returns the state of the VME extensions.
296The
297.Va sub_args
298must point to a
299.Vt struct vm86_vme_args
300structure.
301.It Dv VM86_INTCALL
302Arrange execution in VM86 mode as if the specified interrupt
303was delivered.
304Takes a
305.Vt struct vm86_intcall_args
306argument as the
307.Va sub_args
308pointer.
309The
310.Va intnum
311member of it provides the interrupt number to emulate delivery of,
312and the
313.Va vmf
314gives the VM86 mode machine context to load for the interrupt
315handler.
316.El
317.Pp
318To switch thread to the VM86 execution mode, several preparations
319must be done:
320.Bl -dash
321.It
322An alternative signal stack must be created, since otherwise signal
323delivery in VM86 mode would try to use VM86 stack.
324See
325.Xr sigaltstack 2 .
326.It
327The VM86 mode kernel state must be initialized with the
328.Dv VM86_INIT
329sub-request.
330.It
331The memory must be mapped in the first megabyte of the user address
332space, which will be accessible to the VM86 mode execution.
333.It
334Signal handlers must be established that can handle VM86 exceptions.
335Most importantly, the
336.Dv SIGSEGV
337handler must be provided to assist VM86 code to handle exits due to
338IOPL-sensitive instructions.
339.It
340Finally, the VM86 mode can be entered by switching to the context
341with %eflags register having the
342.Dv eflags.VM
343bit set.
344This can be done, among other ways, by loading the full
345machine context with the
346.Xr sigreturn 2
347system call.
348.El
349.Pp
350The thread continues to execute in VM86 mode until a signal
351is delivered to it.
352Note that exceptions generate appropriate signals the same as
353for protected execution mode.
354.Pp
355See
356.Pa src:tools/tests/vm86
357for a minimal example for a VM86 monitor and sample code.
358.It Dv I386_GET_FSBASE
359Read the 32-bit fsbase context value for the calling thread into a
360.Vt uint32_t
361variable pointed to by the
362.Fa args
363argument.
364.It Dv I386_SET_FSBASE
365Set the 32-bit fsbase context value for the calling thread.
366The
367.Fa args
368argument must point to a
369.Vt int32_t
370variable that has the new fsbase value stored.
371.Pp
372The fsbase is maintained by writing the fsbase value into the
373base field of the user 32-bit %fs GDT descriptor, which has index 2.
374.It Dv I386_GET_GSBASE
375Read the 32-bit gsbase context value for the calling thread into a
376.Vt int32_t
377variable pointed to by the
378.Fa args
379argument.
380.It Dv I386_SET_GSBASE
381Set the 32-bit gsbase context value for the calling thread.
382The
383.Fa args
384argument must point to a
385.Vt int32_t
386variable that has the new gsbase value stored.
387.Pp
388The gsbase is maintained by writing the gsbase value into the
389base field of the user 32-bit %gs GDT descriptor, which has index 3.
390.It Dv I386_GET_XFPUSTATE
391Same as
392.Dv AMD64_GET_XFPUSTATE ,
393but takes the address of the
394.Vt struct i386_get_xfpustate
395structure as the
396.Fa args
397argument.
398.It Dv I386_SET_PKRU
399Provided only when running i386 binaries on the amd64 host kernel.
400.Pp
401See description of the
402.Dv AMD64_SET_PKRU
403request.
404.It Dv I386_CLEAR_PKRU
405Provided only when running i386 binaries on the amd64 host kernel.
406.Pp
407See description of the
408.Dv AMD64_CLEAR_PKRU
409request.
410.It Dv I386_SET_SPLITLOCK
411Change the requesting thread splitlock detection control.
412For i386 binaries, the function is only available when running
413on the amd64 host kernel.
414.Pp
415The argument must point to the 32bit
416.Vt int
417variable, holding one of the following values:
418.Bl -tag
419.It Dv 0
420Disable detection of split locking.
421.It Dv 1
422Enable detection of split locking.
423.El
424.Pp
425The split lock is the situation where the read-modify-write atomic
426instruction is performed on the memory location that is split over
427more than one CPU cache line.
428Some CPUs can be configured to generate the Alignment Fault exception
429.Ql #AC
430if split locking is detected.
431The request makes it possible for the thread to control the detector.
432.Pp
433The
434.Xr _umtx_op 2
435operations that pass unaligned locks to kernel requests returns
436.Ev EFAULT
437when splitlock detection is enabled.
438.Pp
439Presence of the detector can be queried with the
440.Dv hw.splitlock
441.Xr sysctl 2
442MIB.
443.It Dv I386_GET_SPLITLOCK
444Returns the state of the splitlock detection control.
445For i386 binaries, the function is only available when running
446on the amd64 host kernel.
447.Pp
448The argument must point to the 32bit
449.Vt int
450variable.
451The function writes the value 0 if the control is disabled
452and 1 if enabled.
453.Pp
454.It Dv AMD64_GET_FSBASE
455Returns the value of the
456.Dv fsbase
457register for the calling thread.
458The
459.Fa args
460argument must point to the 64bit
461.Vt unsigned long variable
462where the value is written.
463.Pp
464On CPUs that do not support the
465.Dv RDFSBASE
466instruction, the call is the only
467way to read the current value of fsbase.
468.It Dv AMD64_SET_FSBASE
469Sets the
470.Dv fsbase
471register to the 64bit value pointed to by the
472.Fa args
473argument.
474.Pp
475The value is checked to not point outside of the allowed user address
476space, which is subset of possible canonical addresses.
477If the
478.Dv WRFSBASE
479instruction is supported, the fsbase register can
480be updated either by the call or by the instruction.
481For 64bit processes, the fsbase register is always restored on return
482to user mode from kernel calls or interrupts, regardless of the value
483of the %fs segment register.
484.It Dv AMD64_GET_GSBASE
485Get the
486.Dv gsbase
487register, see description of
488.Dv AMD64_GET_FSBASE .
489.It Dv AMD64_SET_GSBASE
490Set the
491.Dv gsbase
492register, see description of
493.Dv AMD64_GET_GSBASE .
494.It Dv AMD64_GET_XFPUSTATE
495Fills the extended FPU state as defined by the architecture and the
496running CPU hardware into the memory described by the
497.Vt struct amd64_get_xfpustate
498structure.
499The
500.Fa args
501argument must point to the structure, the extended state (that is, full
502XSAVE area excluding the legacy region) is written to the
503.Va addr
504up to the
505.Va len
506bytes.
507If the
508.Va len
509member value is smaller then the XSAVE area size, the result is truncated.
510.It Dv AMD64_SET_PKRU
511Set the 4bit protection key for the page table entries used to map
512the given userspace virtual address range.
513.Pp
514The request takes the
515.Vt struct amd64_set_pkru
516structure pointed to by the
517.Fa args
518parameter.
519The
520.Va addr
521and
522.Va len
523members of the structure specify starting address and the length in bytes
524of the region.
525The
526.Va keyidx
527member value must be between 0 and 15 and provides the protection key
528index applied to the region.
529The
530.Va flags
531member is or-ed with the following flags:
532.Bl -tag -width AMD64_PKRU_PERSIST
533.It Dv AMD64_PKRU_EXCL
534Requires that no protection key was assigned to the specified range,
535or previously assigned key cleared.
536If some key was already applied to any part of the range, the call
537fails with the
538.Ev EBUSY
539error.
540.It Dv AMD64_PKRU_PERSIST
541Makes the assigned key persistent.
542It is automatically re-applied to any new mapping created in the range.
543.El
544.Pp
545Protection keys feature is optional and depends on CPU implementing it.
546Use the
547.Dv CPUID
548instruction to check its availability.
549.Pp
550The request is used to implement the
551.Xr x86_pkru_protect_range 3
552function.
553.It Dv AMD64_CLEAR_PKRU
554Clear any protection keys assigned to the specified region.
555.Pp
556The request takes the
557.Vt struct amd64_set_pkru
558structure pointed to by the
559.Fa args
560parameter.
561The
562.Va addr
563and
564.Va len
565members of the structure specify starting address and the length in bytes
566of the region.
567Other structure members are ignored.
568.Pp
569The request is used to implement the
570.Xr x86_pkru_unprotect_range 3
571function.
572.It Dv AMD64_GET_TLSBASE
573AMD64 only.
574.Pp
575Returns the current TLSBASE value for the calling thread.
576The
577.Fa args
578argument must point to the 64bit
579.Vt unsigned long
580variable where the value is written to.
581.It Dv AMD64_SET_TLSBASE
582Sets the TLSBASE and the fsbase register values for the calling thread.
583The
584.Fa args
585argument must point to the 64bit
586.Vt unsigned long
587variable where the value is taken from.
588.Pp
589The TLSBASE value is written into the
590.Dv fsbase
591register before a signal handler is invoked.
592The current fsbase value is saved, and then restored
593upon return from the signal handler.
594Use of TLSBASE makes it possible for programs that modify
595the fsbase register using the
596.Dv WRFSBASE
597instruction to reliably access
598thread-local variables in signal handlers.
599.Pp
600TLSBASE is set up by the
601.Fx
602C runtime environment on thread startup.
603.It Dv AMD64_DISABLE_TLSBASE
604Disables loading the fsbase register from TLSBASE value on
605a signal handler invocation.
606The argument
607.Fa args
608is ignored.
609.Pp
610The call allows to undo the effect of the
611.Dv AMD64_SET_TLSBASE ,
612making signal handlers retain the value of fsbase from the
613interrupted context.
614By default, tlsbase is disabled after the
615.Xr execve 2
616syscall activated a new image.
617.El
618.Sh RETURN VALUES
619For all requests except
620.Dv I386_GET_LDT
621and
622.Dv I386_SET_LDT ,
623upon successful completion, the value 0 is returned; otherwise the
624value -1 is returned and the global variable
625.Va errno
626is set to indicate the error.
627.Pp
628The
629.Dv I386_SET_LDT
630request returns the starting index of the descriptors range it operated
631on, on success.
632In particular, the index of the automatically allocated descriptor is
633reported this way.
634Otherwise, -1 is returned and
635.Va errno
636is set.
637.Pp
638The
639.Dv I386_GET_LDT
640request returns 0 if the specified descriptors range is completely
641outside the allocated LDT.
642On any other error, -1 is returned and
643.Va errno
644is set.
645Otherwise the number of reported descriptors is returned as the syscall
646return value.
647.Sh SEE ALSO
648.Xr i386_get_ioperm 2 ,
649.Xr i386_get_ldt 2 ,
650.Xr i386_vm86 2 ,
651.Xr sysarch 2 ,
652.Xr pkru 3 ,
653.Xr arch 7
654.Sh HISTORY
655The manual page appeared in
656.Fx 16.0 .
657.Sh AUTHORS
658The manual page was written by
659.An Konstantin Belousov Aq Mt kib@FreeBSD.org .
660