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