Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0
menu "Host processor type and features"
source "arch/x86/Kconfig.cpu"
endmenu
config UML_X86
def_bool y
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_USE_QUEUED_SPINLOCKS
select DCACHE_WORD_ACCESS
select HAVE_EFFICIENT_UNALIGNED_ACCESS
select UML_SUBARCH_SUPPORTS_SMP if X86_CX8
config 64BIT
bool "64-bit kernel" if "$(SUBARCH)" = "x86"
default "$(SUBARCH)" != "i386"
config X86_32
def_bool !64BIT
select ARCH_32BIT_OFF_T
select ARCH_WANT_IPC_PARSE_VERSION
select MODULES_USE_ELF_REL
select CLONE_BACKWARDS
select OLD_SIGSUSPEND3
select OLD_SIGACTION
config X86_64
def_bool 64BIT
select MODULES_USE_ELF_RELA
config ARCH_HAS_SC_SIGNALS
def_bool !64BIT
config GENERIC_HWEIGHT
def_bool y
+50
View File
@@ -0,0 +1,50 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
#
ifeq ($(CONFIG_X86_32),y)
BITS := 32
else
BITS := 64
endif
obj-y = bugs_$(BITS).o delay.o fault.o \
ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
stub_segv.o \
sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
subarch.o os-Linux/
ifeq ($(CONFIG_X86_32),y)
obj-y += syscalls_32.o
subarch-y = ../lib/string_32.o ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o
subarch-y += ../lib/cmpxchg8b_emu.o ../lib/atomic64_386_32.o
subarch-y += ../lib/checksum_32.o
subarch-y += ../kernel/sys_ia32.o
else
obj-y += mem_64.o syscalls_64.o vdso/
subarch-y = ../lib/csum-partial_64.o ../lib/memcpy_64.o \
../lib/memmove_64.o ../lib/memset_64.o
endif
subarch-$(CONFIG_MODULES) += ../kernel/module.o
USER_OBJS := bugs_$(BITS).o ptrace_user.o fault.o
$(obj)/user-offsets.s: c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) \
-Iarch/x86/include/generated
targets += user-offsets.s
include/generated/user_constants.h: $(obj)/user-offsets.s FORCE
$(call filechk,offsets,__USER_CONSTANT_H__)
UNPROFILE_OBJS := stub_segv.o
CFLAGS_stub_segv.o := $(CFLAGS_NO_HARDENING)
include $(srctree)/arch/um/scripts/Makefile.rules
+4
View File
@@ -0,0 +1,4 @@
#ifndef __UM_APIC_H
#define __UM_APIC_H
#endif
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_UM_HWEIGHT_H
#define _ASM_UM_HWEIGHT_H
#include <asm-generic/bitops/arch_hweight.h>
#endif
+29
View File
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_UM_BARRIER_H_
#define _ASM_UM_BARRIER_H_
#include <asm/cpufeatures.h>
#include <asm/alternative.h>
/*
* Force strict CPU ordering.
* And yes, this is required on UP too when we're talking
* to devices.
*/
#ifdef CONFIG_X86_32
#define mb() alternative("lock addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
#define rmb() alternative("lock addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)
#define wmb() alternative("lock addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
#else /* CONFIG_X86_32 */
#define mb() asm volatile("mfence" : : : "memory")
#define rmb() asm volatile("lfence" : : : "memory")
#define wmb() asm volatile("sfence" : : : "memory")
#endif /* CONFIG_X86_32 */
#include <asm-generic/barrier.h>
#endif
+122
View File
@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_CHECKSUM_H
#define __UM_CHECKSUM_H
#include <linux/string.h>
#include <linux/in6.h>
#include <linux/uaccess.h>
/*
* computes the checksum of a memory block at buff, length len,
* and adds in "sum" (32-bit)
*
* returns a 32-bit number suitable for feeding into itself
* or csum_tcpudp_magic
*
* this function must be called with even lengths, except
* for the last fragment, which may be odd
*
* it's best to have buff aligned on a 32-bit boundary
*/
extern __wsum csum_partial(const void *buff, int len, __wsum sum);
/* Do not call this directly. Declared for export type visibility. */
extern __visible __wsum csum_partial_copy_generic(const void *src, void *dst, int len);
/**
* csum_fold - Fold and invert a 32bit checksum.
* sum: 32bit unfolded sum
*
* Fold a 32bit running checksum to 16bit and invert it. This is usually
* the last step before putting a checksum into a packet.
* Make sure not to mix with 64bit checksums.
*/
static inline __sum16 csum_fold(__wsum sum)
{
__asm__(
" addl %1,%0\n"
" adcl $0xffff,%0"
: "=r" (sum)
: "r" ((__force u32)sum << 16),
"0" ((__force u32)sum & 0xffff0000)
);
return (__force __sum16)(~(__force u32)sum >> 16);
}
/**
* csum_tcpup_nofold - Compute an IPv4 pseudo header checksum.
* @saddr: source address
* @daddr: destination address
* @len: length of packet
* @proto: ip protocol of packet
* @sum: initial sum to be added in (32bit unfolded)
*
* Returns the pseudo header checksum the input data. Result is
* 32bit unfolded.
*/
static inline __wsum
csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
__u8 proto, __wsum sum)
{
asm(" addl %1, %0\n"
" adcl %2, %0\n"
" adcl %3, %0\n"
" adcl $0, %0\n"
: "=r" (sum)
: "g" (daddr), "g" (saddr), "g" ((len + proto) << 8), "0" (sum));
return sum;
}
/*
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
__u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
/**
* ip_fast_csum - Compute the IPv4 header checksum efficiently.
* iph: ipv4 header
* ihl: length of header / 4
*/
static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
{
unsigned int sum;
asm( " movl (%1), %0\n"
" subl $4, %2\n"
" jbe 2f\n"
" addl 4(%1), %0\n"
" adcl 8(%1), %0\n"
" adcl 12(%1), %0\n"
"1: adcl 16(%1), %0\n"
" lea 4(%1), %1\n"
" decl %2\n"
" jne 1b\n"
" adcl $0, %0\n"
" movl %0, %2\n"
" shrl $16, %0\n"
" addw %w2, %w0\n"
" adcl $0, %0\n"
" notl %0\n"
"2:"
/* Since the input registers which are loaded with iph and ipl
are modified, we must also specify them as outputs, or gcc
will assume they contain their original values. */
: "=r" (sum), "=r" (iph), "=r" (ihl)
: "1" (iph), "2" (ihl)
: "memory");
return (__force __sum16)sum;
}
#ifdef CONFIG_X86_32
# include "checksum_32.h"
#else
# include "checksum_64.h"
#endif
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
* Licensed under the GPL
*/
#ifndef __UM_SYSDEP_CHECKSUM_H
#define __UM_SYSDEP_CHECKSUM_H
static inline __sum16 ip_compute_csum(const void *buff, int len)
{
return csum_fold (csum_partial(buff, len, 0));
}
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
__u32 len, __u8 proto,
__wsum sum)
{
__asm__(
"addl 0(%1), %0 ;\n"
"adcl 4(%1), %0 ;\n"
"adcl 8(%1), %0 ;\n"
"adcl 12(%1), %0 ;\n"
"adcl 0(%2), %0 ;\n"
"adcl 4(%2), %0 ;\n"
"adcl 8(%2), %0 ;\n"
"adcl 12(%2), %0 ;\n"
"adcl %3, %0 ;\n"
"adcl %4, %0 ;\n"
"adcl $0, %0 ;\n"
: "=&r" (sum)
: "r" (saddr), "r" (daddr),
"r"(htonl(len)), "r"(htonl(proto)), "0"(sum));
return csum_fold(sum);
}
#endif
+19
View File
@@ -0,0 +1,19 @@
/*
* Licensed under the GPL
*/
#ifndef __UM_SYSDEP_CHECKSUM_H
#define __UM_SYSDEP_CHECKSUM_H
static inline unsigned add32_with_carry(unsigned a, unsigned b)
{
asm("addl %2,%0\n\t"
"adcl $0,%0"
: "=r" (a)
: "0" (a), "r" (b));
return a;
}
extern __sum16 ip_compute_csum(const void *buff, int len);
#endif
+17
View File
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_DESC_H
#define __UM_DESC_H
/* Taken from asm-i386/desc.h, it's the only thing we need. The rest wouldn't
* compile, and has never been used. */
#define LDT_empty(info) (\
(info)->base_addr == 0 && \
(info)->limit == 0 && \
(info)->contents == 0 && \
(info)->read_exec_only == 1 && \
(info)->seg_32bit == 0 && \
(info)->limit_in_pages == 0 && \
(info)->seg_not_present == 1 && \
(info)->useable == 0 )
#endif
+186
View File
@@ -0,0 +1,186 @@
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#ifndef __UM_ELF_X86_H
#define __UM_ELF_X86_H
#include <asm/user.h>
#include <skas.h>
#define CORE_DUMP_USE_REGSET
#ifdef CONFIG_X86_32
#define R_386_NONE 0
#define R_386_32 1
#define R_386_PC32 2
#define R_386_GOT32 3
#define R_386_PLT32 4
#define R_386_COPY 5
#define R_386_GLOB_DAT 6
#define R_386_JMP_SLOT 7
#define R_386_RELATIVE 8
#define R_386_GOTOFF 9
#define R_386_GOTPC 10
#define R_386_NUM 11
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
#define elf_check_arch(x) \
(((x)->e_machine == EM_386) || ((x)->e_machine == EM_486))
#define ELF_CLASS ELFCLASS32
#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_386
#define ELF_PLAT_INIT(regs, load_addr) do { \
PT_REGS_BX(regs) = 0; \
PT_REGS_CX(regs) = 0; \
PT_REGS_DX(regs) = 0; \
PT_REGS_SI(regs) = 0; \
PT_REGS_DI(regs) = 0; \
PT_REGS_BP(regs) = 0; \
PT_REGS_AX(regs) = 0; \
} while (0)
/* Shamelessly stolen from include/asm-i386/elf.h */
#define ELF_CORE_COPY_REGS(pr_reg, regs) do { \
pr_reg[0] = PT_REGS_BX(regs); \
pr_reg[1] = PT_REGS_CX(regs); \
pr_reg[2] = PT_REGS_DX(regs); \
pr_reg[3] = PT_REGS_SI(regs); \
pr_reg[4] = PT_REGS_DI(regs); \
pr_reg[5] = PT_REGS_BP(regs); \
pr_reg[6] = PT_REGS_AX(regs); \
pr_reg[7] = PT_REGS_DS(regs); \
pr_reg[8] = PT_REGS_ES(regs); \
/* fake once used fs and gs selectors? */ \
pr_reg[9] = PT_REGS_DS(regs); \
pr_reg[10] = PT_REGS_DS(regs); \
pr_reg[11] = PT_REGS_SYSCALL_NR(regs); \
pr_reg[12] = PT_REGS_IP(regs); \
pr_reg[13] = PT_REGS_CS(regs); \
pr_reg[14] = PT_REGS_EFLAGS(regs); \
pr_reg[15] = PT_REGS_SP(regs); \
pr_reg[16] = PT_REGS_SS(regs); \
} while (0);
#define ELF_PLATFORM_FALLBACK "i586"
#else
/* x86-64 relocation types, taken from asm-x86_64/elf.h */
#define R_X86_64_NONE 0 /* No reloc */
#define R_X86_64_64 1 /* Direct 64 bit */
#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
#define R_X86_64_PLT32 4 /* 32 bit PLT address */
#define R_X86_64_COPY 5 /* Copy symbol at runtime */
#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
#define R_X86_64_RELATIVE 8 /* Adjust by program base */
#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative
offset to GOT */
#define R_X86_64_32 10 /* Direct 32 bit zero extended */
#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
#define R_X86_64_16 12 /* Direct 16 bit zero extended */
#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
#define R_X86_64_8 14 /* Direct 8 bit sign extended */
#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
#define R_X86_64_PC64 24 /* Place relative 64-bit signed */
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
#define elf_check_arch(x) \
((x)->e_machine == EM_X86_64)
#define ELF_CLASS ELFCLASS64
#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_X86_64
#define ELF_PLAT_INIT(regs, load_addr) do { \
PT_REGS_BX(regs) = 0; \
PT_REGS_CX(regs) = 0; \
PT_REGS_DX(regs) = 0; \
PT_REGS_SI(regs) = 0; \
PT_REGS_DI(regs) = 0; \
PT_REGS_BP(regs) = 0; \
PT_REGS_AX(regs) = 0; \
PT_REGS_R8(regs) = 0; \
PT_REGS_R9(regs) = 0; \
PT_REGS_R10(regs) = 0; \
PT_REGS_R11(regs) = 0; \
PT_REGS_R12(regs) = 0; \
PT_REGS_R13(regs) = 0; \
PT_REGS_R14(regs) = 0; \
PT_REGS_R15(regs) = 0; \
} while (0)
#define ELF_CORE_COPY_REGS(pr_reg, _regs) \
(pr_reg)[0] = (_regs)->regs.gp[0]; \
(pr_reg)[1] = (_regs)->regs.gp[1]; \
(pr_reg)[2] = (_regs)->regs.gp[2]; \
(pr_reg)[3] = (_regs)->regs.gp[3]; \
(pr_reg)[4] = (_regs)->regs.gp[4]; \
(pr_reg)[5] = (_regs)->regs.gp[5]; \
(pr_reg)[6] = (_regs)->regs.gp[6]; \
(pr_reg)[7] = (_regs)->regs.gp[7]; \
(pr_reg)[8] = (_regs)->regs.gp[8]; \
(pr_reg)[9] = (_regs)->regs.gp[9]; \
(pr_reg)[10] = (_regs)->regs.gp[10]; \
(pr_reg)[11] = (_regs)->regs.gp[11]; \
(pr_reg)[12] = (_regs)->regs.gp[12]; \
(pr_reg)[13] = (_regs)->regs.gp[13]; \
(pr_reg)[14] = (_regs)->regs.gp[14]; \
(pr_reg)[15] = (_regs)->regs.gp[15]; \
(pr_reg)[16] = (_regs)->regs.gp[16]; \
(pr_reg)[17] = (_regs)->regs.gp[17]; \
(pr_reg)[18] = (_regs)->regs.gp[18]; \
(pr_reg)[19] = (_regs)->regs.gp[19]; \
(pr_reg)[20] = (_regs)->regs.gp[20]; \
(pr_reg)[21] = (_regs)->regs.gp[21]; \
(pr_reg)[22] = (_regs)->regs.gp[22]; \
(pr_reg)[23] = 0; \
(pr_reg)[24] = 0; \
(pr_reg)[25] = 0; \
(pr_reg)[26] = 0;
#define ELF_PLATFORM_FALLBACK "x86_64"
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
extern unsigned long um_vdso_addr;
#define AT_SYSINFO_EHDR 33
#define ARCH_DLINFO NEW_AUX_ENT(AT_SYSINFO_EHDR, um_vdso_addr)
#endif
typedef unsigned long elf_greg_t;
#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t))
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
typedef struct user_i387_struct elf_fpregset_t;
struct task_struct;
#define ELF_EXEC_PAGESIZE 4096
#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
extern long elf_aux_hwcap;
#define ELF_HWCAP (elf_aux_hwcap)
extern char *elf_aux_platform;
#define ELF_PLATFORM (elf_aux_platform ?: ELF_PLATFORM_FALLBACK)
#define SET_PERSONALITY(ex) do {} while(0)
#endif
+10
View File
@@ -0,0 +1,10 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __UM_IRQ_VECTORS_H
#define __UM_IRQ_VECTORS_H
#endif
+43
View File
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_PROCESSOR_H
#define __UM_PROCESSOR_H
#include <linux/time-internal.h>
/* include faultinfo structure */
#include <sysdep/faultinfo.h>
#ifdef CONFIG_X86_32
# include "processor_32.h"
#else
# include "processor_64.h"
#endif
#define KSTK_EIP(tsk) KSTK_REG(tsk, HOST_IP)
#define KSTK_ESP(tsk) KSTK_REG(tsk, HOST_SP)
#define KSTK_EBP(tsk) KSTK_REG(tsk, HOST_BP)
#define ARCH_IS_STACKGROW(address) \
(address + 65536 + 32 * sizeof(unsigned long) >= UPT_SP(&current->thread.regs.regs))
#include <asm/user.h>
/* PAUSE is a good thing to insert into busy-wait loops. */
static __always_inline void native_pause(void)
{
__asm__ __volatile__("pause": : :"memory");
}
static __always_inline void cpu_relax(void)
{
if (time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL)
time_travel_ndelay(1);
else
native_pause();
}
#define task_pt_regs(t) (&(t)->thread.regs)
#include <asm/processor-generic.h>
#endif
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __UM_PROCESSOR_I386_H
#define __UM_PROCESSOR_I386_H
#include <linux/string.h>
#include <asm/segment.h>
#include <asm/ldt.h>
extern int host_has_cmov;
struct uml_tls_struct {
struct user_desc tls;
unsigned flushed:1;
unsigned present:1;
};
struct arch_thread {
struct uml_tls_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long debugregs[8];
int debugregs_seq;
struct faultinfo faultinfo;
};
#define INIT_ARCH_THREAD { \
.tls_array = { [ 0 ... GDT_ENTRY_TLS_ENTRIES - 1 ] = \
{ .present = 0, .flushed = 0 } }, \
.debugregs = { [ 0 ... 7 ] = 0 }, \
.debugregs_seq = 0, \
.faultinfo = { 0, 0, 0 } \
}
#define STACKSLOTS_PER_LINE 8
static inline void arch_flush_thread(struct arch_thread *thread)
{
/* Clear any TLS still hanging */
memset(&thread->tls_array, 0, sizeof(thread->tls_array));
}
static inline void arch_copy_thread(struct arch_thread *from,
struct arch_thread *to)
{
memcpy(&to->tls_array, &from->tls_array, sizeof(from->tls_array));
}
#define current_sp() ({ void *sp; __asm__("movl %%esp, %0" : "=r" (sp) : ); sp; })
#define current_bp() ({ unsigned long bp; __asm__("movl %%ebp, %0" : "=r" (bp) : ); bp; })
#endif
@@ -0,0 +1,34 @@
/*
* Copyright 2003 PathScale, Inc.
*
* Licensed under the GPL
*/
#ifndef __UM_PROCESSOR_X86_64_H
#define __UM_PROCESSOR_X86_64_H
struct arch_thread {
unsigned long debugregs[8];
int debugregs_seq;
struct faultinfo faultinfo;
};
#define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \
.debugregs_seq = 0, \
.faultinfo = { 0, 0, 0 } }
#define STACKSLOTS_PER_LINE 4
static inline void arch_flush_thread(struct arch_thread *thread)
{
}
static inline void arch_copy_thread(struct arch_thread *from,
struct arch_thread *to)
{
}
#define current_sp() ({ void *sp; __asm__("movq %%rsp, %0" : "=r" (sp) : ); sp; })
#define current_bp() ({ unsigned long bp; __asm__("movq %%rbp, %0" : "=r" (bp) : ); bp; })
#endif
+103
View File
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_X86_PTRACE_H
#define __UM_X86_PTRACE_H
/* This is here because signal.c needs the REGSET_FP_LEGACY definition */
enum {
REGSET_GENERAL,
#ifdef CONFIG_X86_32
REGSET_FP_LEGACY,
#endif
REGSET_FP,
REGSET_XSTATE,
};
#include <linux/compiler.h>
#ifndef CONFIG_X86_32
#define __FRAME_OFFSETS /* Needed to get the R* macros */
#endif
#include <asm/ptrace-generic.h>
#define user_mode(r) UPT_IS_USER(&(r)->regs)
#define PT_REGS_AX(r) UPT_AX(&(r)->regs)
#define PT_REGS_BX(r) UPT_BX(&(r)->regs)
#define PT_REGS_CX(r) UPT_CX(&(r)->regs)
#define PT_REGS_DX(r) UPT_DX(&(r)->regs)
#define PT_REGS_SI(r) UPT_SI(&(r)->regs)
#define PT_REGS_DI(r) UPT_DI(&(r)->regs)
#define PT_REGS_BP(r) UPT_BP(&(r)->regs)
#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_AX(r)
#define PT_REGS_SYSCALL_RET(r) PT_REGS_AX(r)
#define PT_FIX_EXEC_STACK(sp) do ; while(0)
#define profile_pc(regs) PT_REGS_IP(regs)
#define UPT_RESTART_SYSCALL(r) (UPT_IP(r) -= 2)
#define PT_REGS_SET_SYSCALL_RETURN(r, res) (PT_REGS_AX(r) = (res))
static inline long regs_return_value(struct pt_regs *regs)
{
return PT_REGS_AX(regs);
}
/*
* Forward declaration to avoid including sysdep/tls.h, which causes a
* circular include, and compilation failures.
*/
struct user_desc;
#ifdef CONFIG_X86_32
extern int ptrace_get_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc);
extern int ptrace_set_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc);
extern int arch_switch_tls(struct task_struct *to);
#else
#define PT_REGS_R8(r) UPT_R8(&(r)->regs)
#define PT_REGS_R9(r) UPT_R9(&(r)->regs)
#define PT_REGS_R10(r) UPT_R10(&(r)->regs)
#define PT_REGS_R11(r) UPT_R11(&(r)->regs)
#define PT_REGS_R12(r) UPT_R12(&(r)->regs)
#define PT_REGS_R13(r) UPT_R13(&(r)->regs)
#define PT_REGS_R14(r) UPT_R14(&(r)->regs)
#define PT_REGS_R15(r) UPT_R15(&(r)->regs)
#include <asm/errno.h>
static inline int ptrace_get_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc)
{
return -ENOSYS;
}
static inline int ptrace_set_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc)
{
return -ENOSYS;
}
extern long arch_prctl(struct task_struct *task, int option,
unsigned long __user *addr);
#endif
#define user_stack_pointer(regs) PT_REGS_SP(regs)
extern void arch_switch_to(struct task_struct *to);
#endif /* __UM_X86_PTRACE_H */
@@ -0,0 +1,9 @@
#ifndef __UM_REQUIRED_FEATURES_H
#define __UM_REQUIRED_FEATURES_H
/*
* Nothing to see, just need something for the i386 and x86_64 asm
* headers to include.
*/
#endif
+11
View File
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_SEGMENT_H
#define __UM_SEGMENT_H
extern int host_gdt_entry_tls_min;
#define GDT_ENTRY_TLS_ENTRIES 3
#define GDT_ENTRY_TLS_MIN host_gdt_entry_tls_min
#define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1)
#endif
+8
View File
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_UM_SPINLOCK_H
#define __ASM_UM_SPINLOCK_H
#include <asm/qspinlock.h>
#include <asm/qrwlock.h>
#endif /* __ASM_UM_SPINLOCK_H */
+23
View File
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UM_ASM_SYSCALL_H
#define __UM_ASM_SYSCALL_H
#include <asm/syscall-generic.h>
#include <uapi/linux/audit.h>
typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
extern const sys_call_ptr_t sys_call_table[];
static inline int syscall_get_arch(struct task_struct *task)
{
#ifdef CONFIG_X86_32
return AUDIT_ARCH_I386;
#else
return AUDIT_ARCH_X86_64;
#endif
}
#endif /* __UM_ASM_SYSCALL_H */
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
* Copyright 2003 PathScale, Inc.
* Licensed under the GPL
*/
#ifndef __VM_FLAGS_X86_H
#define __VM_FLAGS_X86_H
#ifdef CONFIG_X86_32
#define VMA_DATA_DEFAULT_FLAGS VMA_DATA_FLAGS_TSK_EXEC
#else
#define VMA_STACK_DEFAULT_FLAGS append_vma_flags(VMA_DATA_FLAGS_EXEC, VMA_GROWSDOWN_BIT)
#endif
#endif
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <arch.h>
#include <signal.h>
#include <kern_util.h>
#include <longjmp.h>
#include <sysdep/ptrace.h>
#include <generated/asm-offsets.h>
/* Set during early boot */
static int host_has_cmov = 1;
static jmp_buf cmov_test_return;
static void cmov_sigill_test_handler(int sig)
{
host_has_cmov = 0;
longjmp(cmov_test_return, 1);
}
void arch_check_bugs(void)
{
struct sigaction old, new;
printk(UM_KERN_INFO "Checking for host processor cmov support...");
new.sa_handler = cmov_sigill_test_handler;
/* Make sure that SIGILL is enabled after the handler longjmps back */
new.sa_flags = SA_NODEFER;
sigemptyset(&new.sa_mask);
sigaction(SIGILL, &new, &old);
if (setjmp(cmov_test_return) == 0) {
unsigned long foo = 0;
__asm__ __volatile__("cmovz %0, %1" : "=r" (foo) : "0" (foo));
printk(UM_KERN_CONT "Yes\n");
} else
printk(UM_KERN_CONT "No\n");
sigaction(SIGILL, &old, &new);
}
void arch_examine_signal(int sig, struct uml_pt_regs *regs)
{
unsigned char tmp[2];
/*
* This is testing for a cmov (0x0f 0x4x) instruction causing a
* SIGILL in init.
*/
if ((sig != SIGILL) || (get_current_pid() != 1))
return;
if (copy_from_user_proc(tmp, (void *) UPT_IP(regs), 2)) {
printk(UM_KERN_ERR "SIGILL in init, could not read "
"instructions!\n");
return;
}
if ((tmp[0] != 0x0f) || ((tmp[1] & 0xf0) != 0x40))
return;
if (host_has_cmov == 0)
printk(UM_KERN_ERR "SIGILL caused by cmov, which this "
"processor doesn't implement. Boot a filesystem "
"compiled for older processors");
else if (host_has_cmov == 1)
printk(UM_KERN_ERR "SIGILL caused by cmov, which this "
"processor claims to implement");
else
printk(UM_KERN_ERR "Bad value for host_has_cmov (%d)",
host_has_cmov);
}
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright 2003 PathScale, Inc.
*
* Licensed under the GPL
*/
#include <arch.h>
#include <sysdep/ptrace.h>
void arch_check_bugs(void)
{
}
void arch_examine_signal(int sig, struct uml_pt_regs *regs)
{
}
+57
View File
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
* Mostly copied from arch/x86/lib/delay.c
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <asm/param.h>
void __delay(unsigned long loops)
{
asm volatile(
"test %0,%0\n"
"jz 3f\n"
"jmp 1f\n"
".align 16\n"
"1: jmp 2f\n"
".align 16\n"
"2: dec %0\n"
" jnz 2b\n"
"3: dec %0\n"
: /* we don't need output */
: "a" (loops)
);
}
EXPORT_SYMBOL(__delay);
inline void __const_udelay(unsigned long xloops)
{
int d0;
xloops *= 4;
asm("mull %%edx"
: "=d" (xloops), "=&a" (d0)
: "1" (xloops), "0"
(loops_per_jiffy * (HZ/4)));
__delay(++xloops);
}
EXPORT_SYMBOL(__const_udelay);
void __udelay(unsigned long usecs)
{
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
}
EXPORT_SYMBOL(__udelay);
void __ndelay(unsigned long nsecs)
{
__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
}
EXPORT_SYMBOL(__ndelay);
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <arch.h>
#include <sysdep/ptrace.h>
/* These two are from asm-um/uaccess.h and linux/module.h, check them. */
struct exception_table_entry
{
unsigned long insn;
unsigned long fixup;
};
const struct exception_table_entry *search_exception_tables(unsigned long add);
/* Compare this to arch/i386/mm/extable.c:fixup_exception() */
int arch_fixup(unsigned long address, struct uml_pt_regs *regs)
{
const struct exception_table_entry *fixup;
fixup = search_exception_tables(address);
if (fixup) {
UPT_IP(regs) = fixup->fixup;
return 1;
}
return 0;
}
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/mm.h>
#include <asm/elf.h>
const char *arch_vma_name(struct vm_area_struct *vma)
{
if (vma->vm_mm && vma->vm_start == um_vdso_addr)
return "[vdso]";
return NULL;
}
+12
View File
@@ -0,0 +1,12 @@
#
# Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
# Licensed under the GPL
#
obj-y = registers.o mcontext.o
obj-$(CONFIG_X86_32) += tls.o
USER_OBJS := $(obj-y)
include $(srctree)/arch/um/scripts/Makefile.rules
+266
View File
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0
#define __FRAME_OFFSETS
#include <linux/errno.h>
#include <linux/string.h>
#include <sys/ucontext.h>
#include <asm/ptrace.h>
/*
* musl defines struct sigcontext in <bits/signal.h>. Rename the kernel's
* copy to avoid redefinition while keeping the FP-state types available.
*/
#define sigcontext __kernel_sigcontext
#include <asm/sigcontext.h>
#undef sigcontext
#include <sysdep/ptrace.h>
#include <sysdep/mcontext.h>
#include <arch.h>
void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
{
#ifdef __i386__
#define COPY2(X,Y) regs->gp[X] = mc->gregs[REG_##Y]
#define COPY(X) regs->gp[X] = mc->gregs[REG_##X]
#define COPY_SEG(X) regs->gp[X] = mc->gregs[REG_##X] & 0xffff;
#define COPY_SEG_CPL3(X) regs->gp[X] = (mc->gregs[REG_##X] & 0xffff) | 3;
COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS);
COPY(EDI); COPY(ESI); COPY(EBP);
COPY2(UESP, ESP); /* sic */
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
#undef COPY2
#undef COPY
#undef COPY_SEG
#undef COPY_SEG_CPL3
#else
#define COPY2(X,Y) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##Y]
#define COPY(X) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##X]
COPY(R8); COPY(R9); COPY(R10); COPY(R11);
COPY(R12); COPY(R13); COPY(R14); COPY(R15);
COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX);
COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP);
COPY(RIP);
COPY2(EFLAGS, EFL);
COPY2(CS, CSGSFS);
regs->gp[SS / sizeof(unsigned long)] = mc->gregs[REG_CSGSFS] >> 48;
#undef COPY2
#undef COPY
#endif
}
void mc_set_rip(void *_mc, void *target)
{
mcontext_t *mc = _mc;
#ifdef __i386__
mc->gregs[REG_EIP] = (unsigned long)target;
#else
mc->gregs[REG_RIP] = (unsigned long)target;
#endif
}
/* Same thing, but the copy macros are turned around. */
void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc, int single_stepping)
{
#ifdef __i386__
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X]
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X]
#define COPY_SEG(X) mc->gregs[REG_##X] = regs->gp[X] & 0xffff;
#define COPY_SEG_CPL3(X) mc->gregs[REG_##X] = (regs->gp[X] & 0xffff) | 3;
COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS);
COPY(EDI); COPY(ESI); COPY(EBP);
COPY2(UESP, ESP); /* sic */
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
#else
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X/sizeof(unsigned long)]
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X/sizeof(unsigned long)]
COPY(R8); COPY(R9); COPY(R10); COPY(R11);
COPY(R12); COPY(R13); COPY(R14); COPY(R15);
COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX);
COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP);
COPY(RIP);
COPY2(EFLAGS, EFL);
mc->gregs[REG_CSGSFS] = mc->gregs[REG_CSGSFS] & 0xffffffffffffl;
mc->gregs[REG_CSGSFS] |= (regs->gp[SS / sizeof(unsigned long)] & 0xffff) << 48;
#endif
if (single_stepping)
mc->gregs[REG_EFL] |= X86_EFLAGS_TF;
else
mc->gregs[REG_EFL] &= ~X86_EFLAGS_TF;
}
#ifdef CONFIG_X86_32
struct _xstate_64 {
struct _fpstate_64 fpstate;
struct _header xstate_hdr;
struct _ymmh_state ymmh;
/* New processor state extensions go here: */
};
/* Not quite the right structures as these contain more information */
int um_i387_from_fxsr(struct _fpstate_32 *i387,
const struct _fpstate_64 *fxsave);
int um_fxsr_from_i387(struct _fpstate_64 *fxsave,
const struct _fpstate_32 *from);
#else
#define _xstate_64 _xstate
#endif
static struct _fpstate *get_fpstate(struct stub_data *data,
mcontext_t *mcontext,
int *fp_size)
{
struct _fpstate *res;
/* Assume floating point registers are on the same page */
res = (void *)(((unsigned long)mcontext->fpregs &
(UM_KERN_PAGE_SIZE - 1)) +
(unsigned long)&data->sigstack[0]);
if ((void *)res + sizeof(struct _fpstate) >
(void *)data->sigstack + sizeof(data->sigstack))
return NULL;
if (res->sw_reserved.magic1 != FP_XSTATE_MAGIC1) {
*fp_size = sizeof(struct _fpstate);
} else {
char *magic2_addr;
magic2_addr = (void *)res;
magic2_addr += res->sw_reserved.extended_size;
magic2_addr -= FP_XSTATE_MAGIC2_SIZE;
/* We still need to be within our stack */
if ((void *)magic2_addr >
(void *)data->sigstack + sizeof(data->sigstack))
return NULL;
/* If we do not read MAGIC2, then we did something wrong */
if (*(__u32 *)magic2_addr != FP_XSTATE_MAGIC2)
return NULL;
/* Remove MAGIC2 from the size, we do not save/restore it */
*fp_size = res->sw_reserved.extended_size -
FP_XSTATE_MAGIC2_SIZE;
}
return res;
}
int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
unsigned long *fp_size_out)
{
mcontext_t *mcontext;
struct _fpstate *fpstate_stub;
struct _xstate_64 *xstate_stub;
int fp_size, xstate_size;
/* mctx_offset is verified by wait_stub_done_seccomp */
mcontext = (void *)&data->sigstack[data->mctx_offset];
get_regs_from_mc(regs, mcontext);
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
if (!fpstate_stub)
return -EINVAL;
#ifdef CONFIG_X86_32
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
#else
xstate_stub = (void *)fpstate_stub;
xstate_size = fp_size;
#endif
if (fp_size_out)
*fp_size_out = xstate_size;
if (xstate_size > host_fp_size)
return -ENOSPC;
memcpy(&regs->fp, xstate_stub, xstate_size);
/* We do not need to read the x86_64 FS_BASE/GS_BASE registers as
* we do not permit userspace to set them directly.
*/
#ifdef CONFIG_X86_32
/* Read the i387 legacy FP registers */
if (um_fxsr_from_i387((void *)&regs->fp, fpstate_stub))
return -EINVAL;
#endif
return 0;
}
/* Copied because we cannot include regset.h here. */
struct task_struct;
struct user_regset;
struct membuf {
void *p;
size_t left;
};
int fpregs_legacy_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to);
int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
int single_stepping)
{
mcontext_t *mcontext;
struct _fpstate *fpstate_stub;
struct _xstate_64 *xstate_stub;
int fp_size, xstate_size;
/* mctx_offset is verified by wait_stub_done_seccomp */
mcontext = (void *)&data->sigstack[data->mctx_offset];
if ((unsigned long)mcontext < (unsigned long)data->sigstack ||
(unsigned long)mcontext >
(unsigned long) data->sigstack +
sizeof(data->sigstack) - sizeof(*mcontext))
return -EINVAL;
get_mc_from_regs(regs, mcontext, single_stepping);
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
if (!fpstate_stub)
return -EINVAL;
#ifdef CONFIG_X86_32
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
#else
xstate_stub = (void *)fpstate_stub;
xstate_size = fp_size;
#endif
memcpy(xstate_stub, &regs->fp, xstate_size);
#ifdef __i386__
/*
* On x86, the GDT entries are updated by arch_set_tls.
*/
/* Store the i387 legacy FP registers which the host will use */
if (um_i387_from_fxsr(fpstate_stub, (void *)&regs->fp))
return -EINVAL;
#else
/*
* On x86_64, we need to sync the FS_BASE/GS_BASE registers using the
* arch specific data.
*/
if (data->arch_data.fs_base != regs->gp[FS_BASE / sizeof(unsigned long)]) {
data->arch_data.fs_base = regs->gp[FS_BASE / sizeof(unsigned long)];
data->arch_data.sync |= STUB_SYNC_FS_BASE;
}
if (data->arch_data.gs_base != regs->gp[GS_BASE / sizeof(unsigned long)]) {
data->arch_data.gs_base = regs->gp[GS_BASE / sizeof(unsigned long)];
data->arch_data.sync |= STUB_SYNC_GS_BASE;
}
#endif
return 0;
}
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2004 PathScale, Inc
* Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <errno.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#ifdef __i386__
#include <sys/user.h>
#endif
#include <longjmp.h>
#include <sysdep/ptrace_user.h>
#include <sys/uio.h>
#include <asm/sigcontext.h>
#include <linux/elf.h>
#include <registers.h>
#include <sys/mman.h>
static unsigned long ptrace_regset;
unsigned long host_fp_size;
int get_fp_registers(int pid, unsigned long *regs)
{
struct iovec iov = {
.iov_base = regs,
.iov_len = host_fp_size,
};
if (ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov) < 0)
return -errno;
return 0;
}
int put_fp_registers(int pid, unsigned long *regs)
{
struct iovec iov = {
.iov_base = regs,
.iov_len = host_fp_size,
};
if (ptrace(PTRACE_SETREGSET, pid, ptrace_regset, &iov) < 0)
return -errno;
return 0;
}
int arch_init_registers(int pid)
{
struct iovec iov = {
/* Just use plenty of space, it does not cost us anything */
.iov_len = 2 * 1024 * 1024,
};
int ret;
iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (iov.iov_base == MAP_FAILED)
return -ENOMEM;
/* GDB has x86_xsave_length, which uses x86_cpuid_count */
ptrace_regset = NT_X86_XSTATE;
ret = ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov);
if (ret)
ret = -errno;
if (ret == -ENODEV) {
#ifdef CONFIG_X86_32
ptrace_regset = NT_PRXFPREG;
#else
ptrace_regset = NT_PRFPREG;
#endif
iov.iov_len = 2 * 1024 * 1024;
ret = ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov);
if (ret)
ret = -errno;
}
munmap(iov.iov_base, 2 * 1024 * 1024);
host_fp_size = iov.iov_len;
return ret;
}
unsigned long get_thread_reg(int reg, jmp_buf *buf)
{
switch (reg) {
#ifdef __i386__
case HOST_IP:
return buf[0]->__eip;
case HOST_SP:
return buf[0]->__esp;
case HOST_BP:
return buf[0]->__ebp;
#else
case HOST_IP:
return buf[0]->__rip;
case HOST_SP:
return buf[0]->__rsp;
case HOST_BP:
return buf[0]->__rbp;
#endif
default:
printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n",
reg);
return 0;
}
}
+69
View File
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <linux/unistd.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <os.h>
#include <sysdep/tls.h>
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
#endif
#ifndef PTRACE_SET_THREAD_AREA
#define PTRACE_SET_THREAD_AREA 26
#endif
/* Checks whether host supports TLS, and sets *tls_min according to the value
* valid on the host.
* i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */
void check_host_supports_tls(int *supports_tls, int *tls_min)
{
/* Values for x86 and x86_64.*/
int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64};
int i;
for (i = 0; i < ARRAY_SIZE(val); i++) {
user_desc_t info;
info.entry_number = val[i];
if (syscall(__NR_get_thread_area, &info) == 0) {
*tls_min = val[i];
*supports_tls = 1;
return;
} else {
if (errno == EINVAL)
continue;
else if (errno == ENOSYS)
*supports_tls = 0;
return;
}
}
*supports_tls = 0;
}
int os_set_thread_area(user_desc_t *info, int pid)
{
int ret;
ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
(unsigned long) info);
if (ret < 0)
ret = -errno;
return ret;
}
int os_get_thread_area(user_desc_t *info, int pid)
{
int ret;
ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
(unsigned long) info);
if (ret < 0)
ret = -errno;
return ret;
}
+305
View File
@@ -0,0 +1,305 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/sched.h>
#include <linux/elf.h>
#include <linux/regset.h>
#include <asm/user32.h>
#include <asm/sigcontext.h>
#ifdef CONFIG_X86_32
/*
* FPU tag word conversions.
*/
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
{
unsigned int tmp; /* to avoid 16 bit prefixes in the code */
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
tmp = ~twd;
tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
/* and move the valid bits to the lower byte. */
tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
return tmp;
}
static inline unsigned long
twd_fxsr_to_i387(const struct user_fxsr_struct *fxsave)
{
struct _fpxreg *st = NULL;
unsigned long twd = (unsigned long) fxsave->twd;
unsigned long tag;
unsigned long ret = 0xffff0000;
int i;
#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16)
for (i = 0; i < 8; i++) {
if (twd & 0x1) {
st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
switch (st->exponent & 0x7fff) {
case 0x7fff:
tag = 2; /* Special */
break;
case 0x0000:
if (!st->significand[0] &&
!st->significand[1] &&
!st->significand[2] &&
!st->significand[3]) {
tag = 1; /* Zero */
} else {
tag = 2; /* Special */
}
break;
default:
if (st->significand[3] & 0x8000)
tag = 0; /* Valid */
else
tag = 2; /* Special */
break;
}
} else {
tag = 3; /* Empty */
}
ret |= (tag << (2 * i));
twd = twd >> 1;
}
return ret;
}
/*
* Get/set the old 32bit i387 registers (pre-FPX)
*
* We provide simple wrappers for mcontext.c, they are only defined locally
* because mcontext.c is userspace facing and needs to a different definition
* of the structures.
*/
static int _um_i387_from_fxsr(struct membuf to,
const struct user_fxsr_struct *fxsave)
{
int i;
membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul);
membuf_store(&to, twd_fxsr_to_i387(fxsave));
membuf_store(&to, fxsave->fip);
membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16));
membuf_store(&to, fxsave->foo);
membuf_store(&to, fxsave->fos);
for (i = 0; i < 8; i++)
membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
return 0;
}
int um_i387_from_fxsr(struct user_i387_struct *i387,
const struct user_fxsr_struct *fxsave);
int um_i387_from_fxsr(struct user_i387_struct *i387,
const struct user_fxsr_struct *fxsave)
{
struct membuf to = {
.p = i387,
.left = sizeof(*i387),
};
return _um_i387_from_fxsr(to, fxsave);
}
static int fpregs_legacy_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
return _um_i387_from_fxsr(to, fxsave);
}
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
const struct user_i387_struct *from);
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
const struct user_i387_struct *from)
{
int i;
fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
fxsave->swd = (unsigned short)(from->swd & 0xffff);
fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff));
fxsave->fip = from->fip;
fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16);
fxsave->fcs = (from->fcs & 0xffff);
fxsave->foo = from->foo;
fxsave->fos = from->fos;
for (i = 0; i < 8; i++) {
memcpy((void *)fxsave->st_space + i * 16,
(void *)from->st_space + i * 10, 10);
}
return 0;
}
static int fpregs_legacy_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
const struct user_i387_struct *from;
struct user_i387_struct buf;
if (ubuf) {
if (copy_from_user(&buf, ubuf, sizeof(buf)))
return -EFAULT;
from = &buf;
} else {
from = kbuf;
}
return um_fxsr_from_i387(fxsave, from);
}
#endif
static int genregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
int reg;
for (reg = 0; to.left; reg++)
membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
return 0;
}
static int genregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
int ret = 0;
if (kbuf) {
const unsigned long *k = kbuf;
while (count >= sizeof(*k) && !ret) {
ret = putreg(target, pos, *k++);
count -= sizeof(*k);
pos += sizeof(*k);
}
} else {
const unsigned long __user *u = ubuf;
while (count >= sizeof(*u) && !ret) {
unsigned long word;
ret = __get_user(word, u++);
if (ret)
break;
ret = putreg(target, pos, word);
count -= sizeof(*u);
pos += sizeof(*u);
}
}
return ret;
}
static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
return regset->n;
}
static int generic_fpregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
void *fpregs = task_pt_regs(target)->regs.fp;
membuf_write(&to, fpregs, regset->size * regset->n);
return 0;
}
static int generic_fpregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
void *fpregs = task_pt_regs(target)->regs.fp;
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
fpregs, 0, regset->size * regset->n);
}
static struct user_regset uml_regsets[] __ro_after_init = {
[REGSET_GENERAL] = {
USER_REGSET_NOTE_TYPE(PRSTATUS),
.n = sizeof(struct user_regs_struct) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.regset_get = genregs_get,
.set = genregs_set
},
#ifdef CONFIG_X86_32
/* Old FP registers, they are needed in signal frames */
[REGSET_FP_LEGACY] = {
USER_REGSET_NOTE_TYPE(PRFPREG),
.n = sizeof(struct user_i387_ia32_struct) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = fpregs_legacy_get,
.set = fpregs_legacy_set,
},
#endif
[REGSET_FP] = {
#ifdef CONFIG_X86_32
USER_REGSET_NOTE_TYPE(PRXFPREG),
.n = sizeof(struct user32_fxsr_struct) / sizeof(long),
#else
USER_REGSET_NOTE_TYPE(PRFPREG),
.n = sizeof(struct user_i387_struct) / sizeof(long),
#endif
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = generic_fpregs_get,
.set = generic_fpregs_set,
},
[REGSET_XSTATE] = {
USER_REGSET_NOTE_TYPE(X86_XSTATE),
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = generic_fpregs_get,
.set = generic_fpregs_set,
},
/* TODO: Add TLS regset for 32bit */
};
static const struct user_regset_view user_uml_view = {
#ifdef CONFIG_X86_32
.name = "i386", .e_machine = EM_386,
#else
.name = "x86_64", .e_machine = EM_X86_64,
#endif
.regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
};
const struct user_regset_view *
task_user_regset_view(struct task_struct *tsk)
{
return &user_uml_view;
}
static int __init init_regset_xstate_info(void)
{
uml_regsets[REGSET_XSTATE].n =
host_fp_size / uml_regsets[REGSET_XSTATE].size;
return 0;
}
arch_initcall(init_regset_xstate_info);
+202
View File
@@ -0,0 +1,202 @@
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/regset.h>
#include <asm/ptrace-abi.h>
#include <registers.h>
#include <skas.h>
void arch_switch_to(struct task_struct *to)
{
int err = arch_switch_tls(to);
if (!err)
return;
if (err != -EINVAL)
printk(KERN_WARNING "arch_switch_tls failed, errno %d, "
"not EINVAL\n", -err);
else
printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n");
}
/* determines which flags the user has access to. */
/* 1 = access 0 = no access */
#define FLAG_MASK 0x00044dd5
static const int reg_offsets[] = {
[EBX] = HOST_BX,
[ECX] = HOST_CX,
[EDX] = HOST_DX,
[ESI] = HOST_SI,
[EDI] = HOST_DI,
[EBP] = HOST_BP,
[EAX] = HOST_AX,
[DS] = HOST_DS,
[ES] = HOST_ES,
[FS] = HOST_FS,
[GS] = HOST_GS,
[EIP] = HOST_IP,
[CS] = HOST_CS,
[EFL] = HOST_EFLAGS,
[UESP] = HOST_SP,
[SS] = HOST_SS,
[ORIG_EAX] = HOST_ORIG_AX,
};
int putreg(struct task_struct *child, int regno, unsigned long value)
{
regno >>= 2;
switch (regno) {
case EBX:
case ECX:
case EDX:
case ESI:
case EDI:
case EBP:
case EAX:
case EIP:
case UESP:
break;
case ORIG_EAX:
/* Update the syscall number. */
UPT_SYSCALL_NR(&child->thread.regs.regs) = value;
break;
case FS:
if (value && (value & 3) != 3)
return -EIO;
break;
case GS:
if (value && (value & 3) != 3)
return -EIO;
break;
case DS:
case ES:
if (value && (value & 3) != 3)
return -EIO;
value &= 0xffff;
break;
case SS:
case CS:
if ((value & 3) != 3)
return -EIO;
value &= 0xffff;
break;
case EFL:
value &= FLAG_MASK;
child->thread.regs.regs.gp[HOST_EFLAGS] |= value;
return 0;
default :
panic("Bad register in putreg() : %d\n", regno);
}
child->thread.regs.regs.gp[reg_offsets[regno]] = value;
return 0;
}
int poke_user(struct task_struct *child, long addr, long data)
{
if ((addr & 3) || addr < 0)
return -EIO;
if (addr < MAX_REG_OFFSET)
return putreg(child, addr, data);
else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))) {
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 2;
if ((addr == 4) || (addr == 5))
return -EIO;
child->thread.arch.debugregs[addr] = data;
return 0;
}
return -EIO;
}
unsigned long getreg(struct task_struct *child, int regno)
{
unsigned long mask = ~0UL;
regno >>= 2;
switch (regno) {
case FS:
case GS:
case DS:
case ES:
case SS:
case CS:
mask = 0xffff;
break;
case EIP:
case UESP:
case EAX:
case EBX:
case ECX:
case EDX:
case ESI:
case EDI:
case EBP:
case EFL:
case ORIG_EAX:
break;
default:
panic("Bad register in getreg() : %d\n", regno);
}
return mask & child->thread.regs.regs.gp[reg_offsets[regno]];
}
/* read the word at location addr in the USER area. */
int peek_user(struct task_struct *child, long addr, long data)
{
unsigned long tmp;
if ((addr & 3) || addr < 0)
return -EIO;
tmp = 0; /* Default return condition */
if (addr < MAX_REG_OFFSET) {
tmp = getreg(child, addr);
}
else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))) {
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 2;
tmp = child->thread.arch.debugregs[addr];
}
return put_user(tmp, (unsigned long __user *) data);
}
long subarch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret = -EIO;
void __user *datap = (void __user *) data;
switch (request) {
case PTRACE_GETFPREGS: /* Get the child FPU state. */
return copy_regset_to_user(child, task_user_regset_view(child),
REGSET_FP_LEGACY,
0, sizeof(struct user_i387_struct),
datap);
case PTRACE_SETFPREGS: /* Set the child FPU state. */
return copy_regset_from_user(child, task_user_regset_view(child),
REGSET_FP_LEGACY,
0, sizeof(struct user_i387_struct),
datap);
case PTRACE_GETFPXREGS: /* Get the child FPU state. */
return copy_regset_to_user(child, task_user_regset_view(child),
REGSET_FP,
0, sizeof(struct user_fxsr_struct),
datap);
case PTRACE_SETFPXREGS: /* Set the child FPU state. */
return copy_regset_from_user(child, task_user_regset_view(child),
REGSET_FP,
0, sizeof(struct user_fxsr_struct),
datap);
default:
ret = -EIO;
}
return ret;
}
+216
View File
@@ -0,0 +1,216 @@
/*
* Copyright 2003 PathScale, Inc.
* Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*
* Licensed under the GPL
*/
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/regset.h>
#define __FRAME_OFFSETS
#include <asm/ptrace.h>
#include <linux/uaccess.h>
#include <registers.h>
#include <asm/ptrace-abi.h>
/*
* determines which flags the user has access to.
* 1 = access 0 = no access
*/
#define FLAG_MASK 0x44dd5UL
static const int reg_offsets[] =
{
[R8 >> 3] = HOST_R8,
[R9 >> 3] = HOST_R9,
[R10 >> 3] = HOST_R10,
[R11 >> 3] = HOST_R11,
[R12 >> 3] = HOST_R12,
[R13 >> 3] = HOST_R13,
[R14 >> 3] = HOST_R14,
[R15 >> 3] = HOST_R15,
[RIP >> 3] = HOST_IP,
[RSP >> 3] = HOST_SP,
[RAX >> 3] = HOST_AX,
[RBX >> 3] = HOST_BX,
[RCX >> 3] = HOST_CX,
[RDX >> 3] = HOST_DX,
[RSI >> 3] = HOST_SI,
[RDI >> 3] = HOST_DI,
[RBP >> 3] = HOST_BP,
[CS >> 3] = HOST_CS,
[SS >> 3] = HOST_SS,
[FS_BASE >> 3] = HOST_FS_BASE,
[GS_BASE >> 3] = HOST_GS_BASE,
[DS >> 3] = HOST_DS,
[ES >> 3] = HOST_ES,
[FS >> 3] = HOST_FS,
[GS >> 3] = HOST_GS,
[EFLAGS >> 3] = HOST_EFLAGS,
[ORIG_RAX >> 3] = HOST_ORIG_AX,
};
int putreg(struct task_struct *child, int regno, unsigned long value)
{
switch (regno) {
case R8:
case R9:
case R10:
case R11:
case R12:
case R13:
case R14:
case R15:
case RIP:
case RSP:
case RAX:
case RBX:
case RCX:
case RDX:
case RSI:
case RDI:
case RBP:
break;
case ORIG_RAX:
/* Update the syscall number. */
UPT_SYSCALL_NR(&child->thread.regs.regs) = value;
break;
case FS:
case GS:
case DS:
case ES:
case SS:
case CS:
if (value && (value & 3) != 3)
return -EIO;
value &= 0xffff;
break;
case FS_BASE:
case GS_BASE:
if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
return -EIO;
break;
case EFLAGS:
value &= FLAG_MASK;
child->thread.regs.regs.gp[HOST_EFLAGS] |= value;
return 0;
default:
panic("Bad register in putreg(): %d\n", regno);
}
child->thread.regs.regs.gp[reg_offsets[regno >> 3]] = value;
return 0;
}
int poke_user(struct task_struct *child, long addr, long data)
{
if ((addr & 3) || addr < 0)
return -EIO;
if (addr < MAX_REG_OFFSET)
return putreg(child, addr, data);
else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))) {
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 3;
if ((addr == 4) || (addr == 5))
return -EIO;
child->thread.arch.debugregs[addr] = data;
return 0;
}
return -EIO;
}
unsigned long getreg(struct task_struct *child, int regno)
{
unsigned long mask = ~0UL;
switch (regno) {
case R8:
case R9:
case R10:
case R11:
case R12:
case R13:
case R14:
case R15:
case RIP:
case RSP:
case RAX:
case RBX:
case RCX:
case RDX:
case RSI:
case RDI:
case RBP:
case ORIG_RAX:
case EFLAGS:
case FS_BASE:
case GS_BASE:
break;
case FS:
case GS:
case DS:
case ES:
case SS:
case CS:
mask = 0xffff;
break;
default:
panic("Bad register in getreg: %d\n", regno);
}
return mask & child->thread.regs.regs.gp[reg_offsets[regno >> 3]];
}
int peek_user(struct task_struct *child, long addr, long data)
{
/* read the word at location addr in the USER area. */
unsigned long tmp;
if ((addr & 3) || addr < 0)
return -EIO;
tmp = 0; /* Default return condition */
if (addr < MAX_REG_OFFSET)
tmp = getreg(child, addr);
else if ((addr >= offsetof(struct user, u_debugreg[0])) &&
(addr <= offsetof(struct user, u_debugreg[7]))) {
addr -= offsetof(struct user, u_debugreg[0]);
addr = addr >> 2;
tmp = child->thread.arch.debugregs[addr];
}
return put_user(tmp, (unsigned long *) data);
}
long subarch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret = -EIO;
void __user *datap = (void __user *) data;
switch (request) {
case PTRACE_GETFPREGS: /* Get the child FPU state. */
return copy_regset_to_user(child, task_user_regset_view(child),
REGSET_FP,
0, sizeof(struct user_i387_struct),
datap);
case PTRACE_SETFPREGS: /* Set the child FPU state. */
return copy_regset_from_user(child, task_user_regset_view(child),
REGSET_FP,
0, sizeof(struct user_i387_struct),
datap);
case PTRACE_ARCH_PRCTL:
/* XXX Calls ptrace on the host - needs some SMP thinking */
ret = arch_prctl(child, data, (void __user *) addr);
break;
}
return ret;
}
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <errno.h>
#include <ptrace_user.h>
int ptrace_getregs(long pid, unsigned long *regs_out)
{
if (ptrace(PTRACE_GETREGS, pid, 0, regs_out) < 0)
return -errno;
return 0;
}
int ptrace_setregs(long pid, unsigned long *regs)
{
if (ptrace(PTRACE_SETREGS, pid, 0, regs) < 0)
return -errno;
return 0;
}
+59
View File
@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0 */
#
# arch/i386/setjmp.S
#
# setjmp/longjmp for the i386 architecture
#
#
# The jmp_buf is assumed to contain the following, in order:
# %ebx
# %esp
# %ebp
# %esi
# %edi
# <return address>
#
.text
.align 4
.globl kernel_setjmp
.type kernel_setjmp, @function
kernel_setjmp:
#ifdef _REGPARM
movl %eax,%edx
#else
movl 4(%esp),%edx
#endif
popl %ecx # Return address, and adjust the stack
xorl %eax,%eax # Return value
movl %ebx,(%edx)
movl %esp,4(%edx) # Post-return %esp!
pushl %ecx # Make the call/return stack happy
movl %ebp,8(%edx)
movl %esi,12(%edx)
movl %edi,16(%edx)
movl %ecx,20(%edx) # Return address
RET
.size kernel_setjmp,.-kernel_setjmp
.text
.align 4
.globl kernel_longjmp
.type kernel_longjmp, @function
kernel_longjmp:
#ifdef _REGPARM
xchgl %eax,%edx
#else
movl 4(%esp),%edx # jmp_ptr address
movl 8(%esp),%eax # Return value
#endif
movl (%edx),%ebx
movl 4(%edx),%esp
movl 8(%edx),%ebp
movl 12(%edx),%esi
movl 16(%edx),%edi
jmp *20(%edx)
.size kernel_longjmp,.-kernel_longjmp
+55
View File
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
#
# arch/x86_64/setjmp.S
#
# setjmp/longjmp for the x86-64 architecture
#
#
# The jmp_buf is assumed to contain the following, in order:
# %rbx
# %rsp (post-return)
# %rbp
# %r12
# %r13
# %r14
# %r15
# <return address>
#
.text
.align 4
.globl kernel_setjmp
.type kernel_setjmp, @function
kernel_setjmp:
pop %rsi # Return address, and adjust the stack
xorl %eax,%eax # Return value
movq %rbx,(%rdi)
movq %rsp,8(%rdi) # Post-return %rsp!
push %rsi # Make the call/return stack happy
movq %rbp,16(%rdi)
movq %r12,24(%rdi)
movq %r13,32(%rdi)
movq %r14,40(%rdi)
movq %r15,48(%rdi)
movq %rsi,56(%rdi) # Return address
RET
.size kernel_setjmp,.-kernel_setjmp
.text
.align 4
.globl kernel_longjmp
.type kernel_longjmp, @function
kernel_longjmp:
movl %esi,%eax # Return value (int)
movq (%rdi),%rbx
movq 8(%rdi),%rsp
movq 16(%rdi),%rbp
movq 24(%rdi),%r12
movq 32(%rdi),%r13
movq 40(%rdi),%r14
movq 48(%rdi),%r15
jmp *56(%rdi)
.size kernel_longjmp,.-kernel_longjmp
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __X86_UM_SYSDEP_ARCHSETJMP_H
#define __X86_UM_SYSDEP_ARCHSETJMP_H
#ifdef __i386__
#include "archsetjmp_32.h"
#else
#include "archsetjmp_64.h"
#endif
unsigned long get_thread_reg(int reg, jmp_buf *buf);
#endif /* __X86_UM_SYSDEP_ARCHSETJMP_H */
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* arch/um/include/sysdep-i386/archsetjmp.h
*/
#ifndef _KLIBC_ARCHSETJMP_H
#define _KLIBC_ARCHSETJMP_H
struct __jmp_buf {
unsigned int __ebx;
unsigned int __esp;
unsigned int __ebp;
unsigned int __esi;
unsigned int __edi;
unsigned int __eip;
};
typedef struct __jmp_buf jmp_buf[1];
#define JB_IP __eip
#define JB_SP __esp
#endif /* _SETJMP_H */
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* arch/um/include/sysdep-x86_64/archsetjmp.h
*/
#ifndef _KLIBC_ARCHSETJMP_H
#define _KLIBC_ARCHSETJMP_H
struct __jmp_buf {
unsigned long __rbx;
unsigned long __rsp;
unsigned long __rbp;
unsigned long __r12;
unsigned long __r13;
unsigned long __r14;
unsigned long __r15;
unsigned long __rip;
};
typedef struct __jmp_buf jmp_buf[1];
#define JB_IP __rip
#define JB_SP __rsp
#endif /* _SETJMP_H */
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifdef __i386__
#include "faultinfo_32.h"
#else
#include "faultinfo_64.h"
#endif
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2004 Fujitsu Siemens Computers GmbH
* Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
* Licensed under the GPL
*/
#ifndef __FAULTINFO_I386_H
#define __FAULTINFO_I386_H
/* this structure contains the full arch-specific faultinfo
* from the traps.
* On i386, ptrace_faultinfo unfortunately doesn't provide
* all the info, since trap_no is missing.
* All common elements are defined at the same position in
* both structures, thus making it easy to copy the
* contents without knowledge about the structure elements.
*/
struct faultinfo {
int error_code; /* in ptrace_faultinfo misleadingly called is_write */
unsigned long cr2; /* in ptrace_faultinfo called addr */
int trap_no; /* missing in ptrace_faultinfo */
};
#define FAULT_WRITE(fi) ((fi).error_code & 2)
#define FAULT_ADDRESS(fi) ((fi).cr2)
/* This is Page Fault */
#define SEGV_IS_FIXABLE(fi) ((fi)->trap_no == 14)
#define PTRACE_FULL_FAULTINFO 0
#define ___backtrack_faulted(_faulted) \
asm volatile ( \
"movl $__get_kernel_nofault_faulted_%=,%1\n" \
"mov $0, %0\n" \
"jmp _end_%=\n" \
"__get_kernel_nofault_faulted_%=:\n" \
"mov $1, %0;" \
"_end_%=:" \
: "=r" (_faulted), \
"=m" (current->thread.segv_continue) :: \
)
#endif
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2004 Fujitsu Siemens Computers GmbH
* Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
* Licensed under the GPL
*/
#ifndef __FAULTINFO_X86_64_H
#define __FAULTINFO_X86_64_H
/* this structure contains the full arch-specific faultinfo
* from the traps.
* On i386, ptrace_faultinfo unfortunately doesn't provide
* all the info, since trap_no is missing.
* All common elements are defined at the same position in
* both structures, thus making it easy to copy the
* contents without knowledge about the structure elements.
*/
struct faultinfo {
int error_code; /* in ptrace_faultinfo misleadingly called is_write */
unsigned long cr2; /* in ptrace_faultinfo called addr */
int trap_no; /* missing in ptrace_faultinfo */
};
#define FAULT_WRITE(fi) ((fi).error_code & 2)
#define FAULT_ADDRESS(fi) ((fi).cr2)
/* This is Page Fault */
#define SEGV_IS_FIXABLE(fi) ((fi)->trap_no == 14)
#define PTRACE_FULL_FAULTINFO 1
#define ___backtrack_faulted(_faulted) \
asm volatile ( \
"movq $__get_kernel_nofault_faulted_%=,%1\n" \
"mov $0, %0\n" \
"jmp _end_%=\n" \
"__get_kernel_nofault_faulted_%=:\n" \
"mov $1, %0;" \
"_end_%=:" \
: "=r" (_faulted), \
"=m" (current->thread.segv_continue) :: \
)
#endif
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#ifndef __SYS_SIGCONTEXT_X86_H
#define __SYS_SIGCONTEXT_X86_H
#include <stub-data.h>
extern void get_regs_from_mc(struct uml_pt_regs *, mcontext_t *);
extern void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc,
int single_stepping);
extern int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
unsigned long *fp_size_out);
extern int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
int single_stepping);
#ifdef __i386__
#define GET_FAULTINFO_FROM_MC(fi, mc) \
{ \
(fi).cr2 = (mc)->cr2; \
(fi).error_code = (mc)->gregs[REG_ERR]; \
(fi).trap_no = (mc)->gregs[REG_TRAPNO]; \
}
#else
#define GET_FAULTINFO_FROM_MC(fi, mc) \
{ \
(fi).cr2 = (mc)->gregs[REG_CR2]; \
(fi).error_code = (mc)->gregs[REG_ERR]; \
(fi).trap_no = (mc)->gregs[REG_TRAPNO]; \
}
#endif
#endif
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SYSDEP_X86_PTRACE_H
#define __SYSDEP_X86_PTRACE_H
#include <generated/user_constants.h>
#include <sysdep/faultinfo.h>
#define MAX_REG_OFFSET (UM_FRAME_SIZE)
#define MAX_REG_NR ((MAX_REG_OFFSET) / sizeof(unsigned long))
#define REGS_IP(r) ((r)[HOST_IP])
#define REGS_SP(r) ((r)[HOST_SP])
#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
#define REGS_AX(r) ((r)[HOST_AX])
#define REGS_BX(r) ((r)[HOST_BX])
#define REGS_CX(r) ((r)[HOST_CX])
#define REGS_DX(r) ((r)[HOST_DX])
#define REGS_SI(r) ((r)[HOST_SI])
#define REGS_DI(r) ((r)[HOST_DI])
#define REGS_BP(r) ((r)[HOST_BP])
#define REGS_CS(r) ((r)[HOST_CS])
#define REGS_SS(r) ((r)[HOST_SS])
#define REGS_DS(r) ((r)[HOST_DS])
#define REGS_ES(r) ((r)[HOST_ES])
#define UPT_IP(r) REGS_IP((r)->gp)
#define UPT_SP(r) REGS_SP((r)->gp)
#define UPT_EFLAGS(r) REGS_EFLAGS((r)->gp)
#define UPT_AX(r) REGS_AX((r)->gp)
#define UPT_BX(r) REGS_BX((r)->gp)
#define UPT_CX(r) REGS_CX((r)->gp)
#define UPT_DX(r) REGS_DX((r)->gp)
#define UPT_SI(r) REGS_SI((r)->gp)
#define UPT_DI(r) REGS_DI((r)->gp)
#define UPT_BP(r) REGS_BP((r)->gp)
#define UPT_CS(r) REGS_CS((r)->gp)
#define UPT_SS(r) REGS_SS((r)->gp)
#define UPT_DS(r) REGS_DS((r)->gp)
#define UPT_ES(r) REGS_ES((r)->gp)
#ifdef __i386__
#include "ptrace_32.h"
#else
#include "ptrace_64.h"
#endif
extern unsigned long host_fp_size;
struct uml_pt_regs {
unsigned long gp[MAX_REG_NR];
struct faultinfo faultinfo;
long syscall;
int is_user;
/* Dynamically sized FP registers (holds an XSTATE) */
unsigned long fp[];
};
#define EMPTY_UML_PT_REGS { }
#define UPT_SYSCALL_NR(r) ((r)->syscall)
#define UPT_FAULTINFO(r) (&(r)->faultinfo)
#define UPT_IS_USER(r) ((r)->is_user)
extern int arch_init_registers(int pid);
#endif /* __SYSDEP_X86_PTRACE_H */
@@ -0,0 +1,16 @@
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#ifndef __SYSDEP_I386_PTRACE_H
#define __SYSDEP_I386_PTRACE_H
#define UPT_SYSCALL_ARG1(r) UPT_BX(r)
#define UPT_SYSCALL_ARG2(r) UPT_CX(r)
#define UPT_SYSCALL_ARG3(r) UPT_DX(r)
#define UPT_SYSCALL_ARG4(r) UPT_SI(r)
#define UPT_SYSCALL_ARG5(r) UPT_DI(r)
#define UPT_SYSCALL_ARG6(r) UPT_BP(r)
#endif
@@ -0,0 +1,58 @@
/*
* Copyright 2003 PathScale, Inc.
* Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*
* Licensed under the GPL
*/
#ifndef __SYSDEP_X86_64_PTRACE_H
#define __SYSDEP_X86_64_PTRACE_H
#define REGS_R8(r) ((r)[HOST_R8])
#define REGS_R9(r) ((r)[HOST_R9])
#define REGS_R10(r) ((r)[HOST_R10])
#define REGS_R11(r) ((r)[HOST_R11])
#define REGS_R12(r) ((r)[HOST_R12])
#define REGS_R13(r) ((r)[HOST_R13])
#define REGS_R14(r) ((r)[HOST_R14])
#define REGS_R15(r) ((r)[HOST_R15])
#define HOST_FS_BASE 21
#define HOST_GS_BASE 22
#define HOST_DS 23
#define HOST_ES 24
#define HOST_FS 25
#define HOST_GS 26
/* Also defined in asm/ptrace-x86_64.h, but not in libc headers. So, these
* are already defined for kernel code, but not for userspace code.
*/
#ifndef FS_BASE
/* These aren't defined in ptrace.h, but exist in struct user_regs_struct,
* which is what x86_64 ptrace actually uses.
*/
#define FS_BASE (HOST_FS_BASE * sizeof(long))
#define GS_BASE (HOST_GS_BASE * sizeof(long))
#define DS (HOST_DS * sizeof(long))
#define ES (HOST_ES * sizeof(long))
#define FS (HOST_FS * sizeof(long))
#define GS (HOST_GS * sizeof(long))
#endif
#define UPT_R8(r) REGS_R8((r)->gp)
#define UPT_R9(r) REGS_R9((r)->gp)
#define UPT_R10(r) REGS_R10((r)->gp)
#define UPT_R11(r) REGS_R11((r)->gp)
#define UPT_R12(r) REGS_R12((r)->gp)
#define UPT_R13(r) REGS_R13((r)->gp)
#define UPT_R14(r) REGS_R14((r)->gp)
#define UPT_R15(r) REGS_R15((r)->gp)
#define UPT_SYSCALL_ARG1(r) UPT_DI(r)
#define UPT_SYSCALL_ARG2(r) UPT_SI(r)
#define UPT_SYSCALL_ARG3(r) UPT_DX(r)
#define UPT_SYSCALL_ARG4(r) UPT_R10(r)
#define UPT_SYSCALL_ARG5(r) UPT_R8(r)
#define UPT_SYSCALL_ARG6(r) UPT_R9(r)
#endif
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <generated/user_constants.h>
#define PT_OFFSET(r) ((r) * sizeof(long))
#define PT_SYSCALL_NR(regs) ((regs)[HOST_ORIG_AX])
#define PT_SYSCALL_NR_OFFSET PT_OFFSET(HOST_ORIG_AX)
#define PT_SYSCALL_RET_OFFSET PT_OFFSET(HOST_AX)
#define REGS_IP_INDEX HOST_IP
#define REGS_SP_INDEX HOST_SP
/*
* glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum,
* ensure we have a definition by (re-)defining it here.
*/
#ifndef PTRACE_SYSEMU_SINGLESTEP
#define PTRACE_SYSEMU_SINGLESTEP 32
#endif
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ARCH_STUB_DATA_H
#define __ARCH_STUB_DATA_H
#ifdef __i386__
#include <generated/asm-offsets.h>
#include <asm/ldt.h>
struct stub_data_arch {
int sync;
struct user_desc tls[UM_KERN_GDT_ENTRY_TLS_ENTRIES];
};
#else
#define STUB_SYNC_FS_BASE (1 << 0)
#define STUB_SYNC_GS_BASE (1 << 1)
struct stub_data_arch {
int sync;
unsigned long fs_base;
unsigned long gs_base;
};
#endif
#endif /* __ARCH_STUB_DATA_H */
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <asm/unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <as-layout.h>
#include <stub-data.h>
#ifdef __i386__
#include "stub_32.h"
#else
#include "stub_64.h"
#endif
extern void stub_segv_handler(int, siginfo_t *, void *);
extern void stub_syscall_handler(void);
extern void stub_signal_interrupt(int, siginfo_t *, void *);
extern void stub_signal_restorer(void);
@@ -0,0 +1,147 @@
/*
* Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#ifndef __SYSDEP_STUB_H
#define __SYSDEP_STUB_H
#include <stddef.h>
#include <asm/ptrace.h>
#include <generated/asm-offsets.h>
#define STUB_MMAP_NR __NR_mmap2
#define MMAP_OFFSET(o) ((o) >> UM_KERN_PAGE_SHIFT)
static __always_inline long stub_syscall0(long syscall)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall)
: "memory");
return ret;
}
static __always_inline long stub_syscall1(long syscall, long arg1)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1)
: "memory");
return ret;
}
static __always_inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2)
: "memory");
return ret;
}
static __always_inline long stub_syscall3(long syscall, long arg1, long arg2,
long arg3)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3)
: "memory");
return ret;
}
static __always_inline long stub_syscall4(long syscall, long arg1, long arg2,
long arg3, long arg4)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3), "S" (arg4)
: "memory");
return ret;
}
static __always_inline long stub_syscall5(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5)
: "memory");
return ret;
}
static __always_inline long stub_syscall6(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5,
long arg6)
{
struct syscall_args {
int ebx, ebp;
} args = { arg1, arg6 };
long ret;
__asm__ volatile ("pushl %%ebp;"
"movl 0x4(%%ebx),%%ebp;"
"movl (%%ebx),%%ebx;"
"int $0x80;"
"popl %%ebp"
: "=a" (ret)
: "0" (syscall), "b" (&args),
"c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5)
: "memory");
return ret;
}
static __always_inline void trap_myself(void)
{
__asm("int3");
}
static __always_inline void *get_stub_data(void)
{
unsigned long ret;
asm volatile (
"call _here_%=;"
"_here_%=:"
"popl %0;"
"andl %1, %0 ;"
"addl %2, %0 ;"
: "=a" (ret)
: "g" (~(UM_KERN_PAGE_SIZE - 1)),
"g" (UM_KERN_PAGE_SIZE));
return (void *)ret;
}
#define stub_start(fn) \
asm volatile ( \
"subl %0,%%esp ;" \
"movl %1, %%eax ; " \
"call *%%eax ;" \
:: "i" (STUB_SIZE), \
"i" (&fn))
static __always_inline void
stub_seccomp_restore_state(struct stub_data_arch *arch)
{
for (int i = 0; i < sizeof(arch->tls) / sizeof(arch->tls[0]); i++) {
if (arch->sync & (1 << i))
stub_syscall1(__NR_set_thread_area,
(unsigned long) &arch->tls[i]);
}
arch->sync = 0;
}
#endif
@@ -0,0 +1,154 @@
/*
* Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#ifndef __SYSDEP_STUB_H
#define __SYSDEP_STUB_H
#include <stddef.h>
#include <sysdep/ptrace_user.h>
#include <generated/asm-offsets.h>
#include <linux/stddef.h>
#include <asm/prctl.h>
#define STUB_MMAP_NR __NR_mmap
#define MMAP_OFFSET(o) (o)
#define __syscall_clobber "r11","rcx","memory"
#define __syscall "syscall"
static __always_inline long stub_syscall0(long syscall)
{
long ret;
__asm__ volatile (__syscall
: "=a" (ret)
: "0" (syscall) : __syscall_clobber );
return ret;
}
static __always_inline long stub_syscall1(long syscall, long arg1)
{
long ret;
__asm__ volatile (__syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1) : __syscall_clobber );
return ret;
}
static __always_inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;
__asm__ volatile (__syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1), "S" (arg2) : __syscall_clobber );
return ret;
}
static __always_inline long stub_syscall3(long syscall, long arg1, long arg2,
long arg3)
{
long ret;
__asm__ volatile (__syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3)
: __syscall_clobber );
return ret;
}
static __always_inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
long arg4)
{
long ret;
__asm__ volatile ("movq %5,%%r10 ; " __syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
"g" (arg4)
: __syscall_clobber, "r10" );
return ret;
}
static __always_inline long stub_syscall5(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5)
{
long ret;
__asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; " __syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
"g" (arg4), "g" (arg5)
: __syscall_clobber, "r10", "r8" );
return ret;
}
static __always_inline long stub_syscall6(long syscall, long arg1, long arg2,
long arg3, long arg4, long arg5,
long arg6)
{
long ret;
__asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; movq %7,%%r9 ; "
__syscall
: "=a" (ret)
: "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
"g" (arg4), "g" (arg5), "g" (arg6)
: __syscall_clobber, "r10", "r8", "r9");
return ret;
}
static __always_inline void trap_myself(void)
{
__asm("int3");
}
static __always_inline void *get_stub_data(void)
{
unsigned long ret;
asm volatile (
"lea 0(%%rip), %0;"
"andq %1, %0 ;"
"addq %2, %0 ;"
: "=a" (ret)
: "g" (~(UM_KERN_PAGE_SIZE - 1)),
"g" (UM_KERN_PAGE_SIZE));
return (void *)ret;
}
#define stub_start(fn) \
asm volatile ( \
"subq %0,%%rsp ;" \
"movq %1,%%rax ;" \
"call *%%rax ;" \
:: "i" (STUB_SIZE), \
"i" (&fn))
static __always_inline void
stub_seccomp_restore_state(struct stub_data_arch *arch)
{
/*
* We could use _writefsbase_u64/_writegsbase_u64 if the host reports
* support in the hwcaps (HWCAP2_FSGSBASE).
*/
if (arch->sync & STUB_SYNC_FS_BASE)
stub_syscall2(__NR_arch_prctl, ARCH_SET_FS, arch->fs_base);
if (arch->sync & STUB_SYNC_GS_BASE)
stub_syscall2(__NR_arch_prctl, ARCH_SET_GS, arch->gs_base);
arch->sync = 0;
}
#endif
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _SYSDEP_TLS_H
#define _SYSDEP_TLS_H
#ifdef __UM_HOST__
/* Change name to avoid conflicts with the original one from <asm/ldt.h>, which
* may be named user_desc (but in 2.4 and in header matching its API was named
* modify_ldt_ldt_s). */
typedef struct um_dup_user_desc {
unsigned int entry_number;
unsigned int base_addr;
unsigned int limit;
unsigned int seg_32bit:1;
unsigned int contents:2;
unsigned int read_exec_only:1;
unsigned int limit_in_pages:1;
unsigned int seg_not_present:1;
unsigned int useable:1;
#ifdef __x86_64__
unsigned int lm:1;
#endif
} user_desc_t;
#else /* __UM_HOST__ */
typedef struct user_desc user_desc_t;
#endif /* __UM_HOST__ */
extern int os_set_thread_area(user_desc_t *info, int pid);
extern int os_get_thread_area(user_desc_t *info, int pid);
#ifdef __i386__
#define GDT_ENTRY_TLS_MIN_I386 6
#define GDT_ENTRY_TLS_MIN_X86_64 12
#endif
#endif /* _SYSDEP_TLS_H */
+470
View File
@@ -0,0 +1,470 @@
/*
* Copyright (C) 2003 PathScale, Inc.
* Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <linux/uaccess.h>
#include <asm/ucontext.h>
#include <frame_kern.h>
#include <registers.h>
#include <skas.h>
#include <linux/regset.h>
#include <asm/sigframe.h>
#ifdef CONFIG_X86_32
struct _xstate_64 {
struct _fpstate_64 fpstate;
struct _header xstate_hdr;
struct _ymmh_state ymmh;
/* New processor state extensions go here: */
};
#else
#define _xstate_64 _xstate
#endif
static int copy_sc_from_user(struct pt_regs *regs,
struct sigcontext __user *from)
{
struct _xstate_64 __user *from_fp64;
struct sigcontext sc;
int err;
/* Always make any pending restarted system calls return -EINTR */
current->restart_block.fn = do_no_restart_syscall;
err = copy_from_user(&sc, from, sizeof(sc));
if (err)
return err;
#define GETREG(regno, regname) regs->regs.gp[HOST_##regno] = sc.regname
#ifdef CONFIG_X86_32
GETREG(GS, gs);
GETREG(FS, fs);
GETREG(ES, es);
GETREG(DS, ds);
#endif
GETREG(DI, di);
GETREG(SI, si);
GETREG(BP, bp);
GETREG(SP, sp);
GETREG(BX, bx);
GETREG(DX, dx);
GETREG(CX, cx);
GETREG(AX, ax);
GETREG(IP, ip);
#ifdef CONFIG_X86_64
GETREG(R8, r8);
GETREG(R9, r9);
GETREG(R10, r10);
GETREG(R11, r11);
GETREG(R12, r12);
GETREG(R13, r13);
GETREG(R14, r14);
GETREG(R15, r15);
#endif
GETREG(CS, cs);
GETREG(EFLAGS, flags);
#ifdef CONFIG_X86_32
GETREG(SS, ss);
#endif
#undef GETREG
#ifdef CONFIG_X86_32
from_fp64 = ((void __user *)sc.fpstate) +
offsetof(struct _fpstate_32, _fxsr_env);
#else
from_fp64 = (void __user *)sc.fpstate;
#endif
err = copy_from_user(regs->regs.fp, from_fp64, host_fp_size);
if (err)
return 1;
#ifdef CONFIG_X86_32
/* Data is duplicated and this copy is the important one */
err = copy_regset_from_user(current,
task_user_regset_view(current),
REGSET_FP_LEGACY, 0,
sizeof(struct user_i387_struct),
(void __user *)sc.fpstate);
if (err < 0)
return err;
#endif
return 0;
}
static int copy_sc_to_user(struct sigcontext __user *to,
struct _xstate __user *to_fp, struct pt_regs *regs,
unsigned long mask)
{
struct _xstate_64 __user *to_fp64;
struct sigcontext sc;
struct faultinfo * fi = &current->thread.arch.faultinfo;
int err;
memset(&sc, 0, sizeof(struct sigcontext));
#define PUTREG(regno, regname) sc.regname = regs->regs.gp[HOST_##regno]
#ifdef CONFIG_X86_32
PUTREG(GS, gs);
PUTREG(FS, fs);
PUTREG(ES, es);
PUTREG(DS, ds);
#endif
PUTREG(DI, di);
PUTREG(SI, si);
PUTREG(BP, bp);
PUTREG(SP, sp);
PUTREG(BX, bx);
PUTREG(DX, dx);
PUTREG(CX, cx);
PUTREG(AX, ax);
#ifdef CONFIG_X86_64
PUTREG(R8, r8);
PUTREG(R9, r9);
PUTREG(R10, r10);
PUTREG(R11, r11);
PUTREG(R12, r12);
PUTREG(R13, r13);
PUTREG(R14, r14);
PUTREG(R15, r15);
#endif
sc.cr2 = fi->cr2;
sc.err = fi->error_code;
sc.trapno = fi->trap_no;
PUTREG(IP, ip);
PUTREG(CS, cs);
PUTREG(EFLAGS, flags);
#ifdef CONFIG_X86_32
PUTREG(SP, sp_at_signal);
PUTREG(SS, ss);
#endif
#undef PUTREG
sc.oldmask = mask;
sc.fpstate = (unsigned long)to_fp;
err = copy_to_user(to, &sc, sizeof(struct sigcontext));
if (err)
return 1;
#ifdef CONFIG_X86_32
err = copy_regset_to_user(current,
task_user_regset_view(current),
REGSET_FP_LEGACY, 0,
sizeof(struct _fpstate_32), to_fp);
if (err < 0)
return err;
__put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic);
BUILD_BUG_ON(offsetof(struct _xstate, xstate_hdr) !=
offsetof(struct _xstate_64, xstate_hdr) +
offsetof(struct _fpstate_32, _fxsr_env));
to_fp64 = (void __user *)to_fp +
offsetof(struct _fpstate_32, _fxsr_env);
#else
to_fp64 = to_fp;
#endif /* CONFIG_X86_32 */
if (copy_to_user(to_fp64, regs->regs.fp, host_fp_size))
return 1;
/*
* Put magic/size values for userspace. We do not bother to verify them
* later on, however, userspace needs them should it try to read the
* XSTATE data. And ptrace does not fill in these parts.
*
* Skip this if we do not have an XSTATE frame.
*/
if (host_fp_size <= sizeof(to_fp64->fpstate))
return 0;
BUILD_BUG_ON(sizeof(int) != FP_XSTATE_MAGIC2_SIZE);
#ifdef CONFIG_X86_32
__put_user(offsetof(struct _fpstate_32, _fxsr_env) +
host_fp_size + FP_XSTATE_MAGIC2_SIZE,
&to_fp64->fpstate.sw_reserved.extended_size);
#else
__put_user(host_fp_size + FP_XSTATE_MAGIC2_SIZE,
&to_fp64->fpstate.sw_reserved.extended_size);
#endif
__put_user(host_fp_size, &to_fp64->fpstate.sw_reserved.xstate_size);
__put_user(FP_XSTATE_MAGIC1, &to_fp64->fpstate.sw_reserved.magic1);
__put_user(FP_XSTATE_MAGIC2,
(int __user *)((void __user *)to_fp64 + host_fp_size));
return 0;
}
#ifdef CONFIG_X86_32
static int copy_ucontext_to_user(struct ucontext __user *uc,
struct _xstate __user *fp, sigset_t *set,
unsigned long sp)
{
int err = 0;
err |= __save_altstack(&uc->uc_stack, sp);
err |= copy_sc_to_user(&uc->uc_mcontext, fp, &current->thread.regs, 0);
err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set));
return err;
}
int setup_signal_stack_sc(unsigned long stack_top, struct ksignal *ksig,
struct pt_regs *regs, sigset_t *mask)
{
size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) +
host_fp_size + FP_XSTATE_MAGIC2_SIZE;
struct sigframe __user *frame;
void __user *restorer;
int err = 0, sig = ksig->sig;
unsigned long fp_to;
/* This is the same calculation as i386 - ((sp + 4) & 15) == 0 */
stack_top = ((stack_top + 4) & -16UL) - 4;
frame = (struct sigframe __user *) stack_top - 1;
if (!access_ok(frame, sizeof(*frame)))
return 1;
/* Add required space for math frame */
frame = (struct sigframe __user *)((unsigned long)frame - math_size);
restorer = frame->retcode;
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
err |= __put_user(restorer, (void __user * __user *)&frame->pretcode);
err |= __put_user(sig, &frame->sig);
fp_to = (unsigned long)frame + sizeof(*frame);
err |= copy_sc_to_user(&frame->sc,
(struct _xstate __user *)fp_to,
regs, mask->sig[0]);
if (_NSIG_WORDS > 1)
err |= __copy_to_user(&frame->extramask, &mask->sig[1],
sizeof(frame->extramask));
/*
* This is popl %eax ; movl $,%eax ; int $0x80
*
* WE DO NOT USE IT ANY MORE! It's only left here for historical
* reasons and because gdb uses it as a signature to notice
* signal handler stack frames.
*/
err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
if (err)
return err;
PT_REGS_SP(regs) = (unsigned long) frame;
PT_REGS_IP(regs) = (unsigned long) ksig->ka.sa.sa_handler;
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) 0;
PT_REGS_CX(regs) = (unsigned long) 0;
return 0;
}
int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig,
struct pt_regs *regs, sigset_t *mask)
{
size_t math_size = offsetof(struct _fpstate_32, _fxsr_env) +
host_fp_size + FP_XSTATE_MAGIC2_SIZE;
struct rt_sigframe __user *frame;
void __user *restorer;
int err = 0, sig = ksig->sig;
unsigned long fp_to;
stack_top &= -8UL;
frame = (struct rt_sigframe __user *) stack_top - 1;
if (!access_ok(frame, sizeof(*frame)))
return 1;
/* Add required space for math frame */
frame = (struct rt_sigframe __user *)((unsigned long)frame - math_size);
restorer = frame->retcode;
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = ksig->ka.sa.sa_restorer;
err |= __put_user(restorer, (void __user * __user *)&frame->pretcode);
err |= __put_user(sig, &frame->sig);
err |= __put_user(&frame->info, (void __user * __user *)&frame->pinfo);
err |= __put_user(&frame->uc, (void __user * __user *)&frame->puc);
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
fp_to = (unsigned long)frame + sizeof(*frame);
err |= copy_ucontext_to_user(&frame->uc, (struct _xstate __user *)fp_to,
mask, PT_REGS_SP(regs));
/*
* This is movl $,%eax ; int $0x80
*
* WE DO NOT USE IT ANY MORE! It's only left here for historical
* reasons and because gdb uses it as a signature to notice
* signal handler stack frames.
*/
err |= __put_user(0xb8, (char __user *)(frame->retcode+0));
err |= __put_user(__NR_rt_sigreturn, (int __user *)(frame->retcode+1));
err |= __put_user(0x80cd, (short __user *)(frame->retcode+5));
if (err)
return err;
PT_REGS_SP(regs) = (unsigned long) frame;
PT_REGS_IP(regs) = (unsigned long) ksig->ka.sa.sa_handler;
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) &frame->info;
PT_REGS_CX(regs) = (unsigned long) &frame->uc;
return 0;
}
SYSCALL_DEFINE0(sigreturn)
{
unsigned long sp = PT_REGS_SP(&current->thread.regs);
struct sigframe __user *frame = (struct sigframe __user *)(sp - 8);
sigset_t set;
struct sigcontext __user *sc = &frame->sc;
int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
if (copy_from_user(&set.sig[0], &sc->oldmask, sizeof(set.sig[0])) ||
copy_from_user(&set.sig[1], frame->extramask, sig_size))
goto segfault;
set_current_blocked(&set);
if (copy_sc_from_user(&current->thread.regs, sc))
goto segfault;
/* Avoid ERESTART handling */
PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
return PT_REGS_SYSCALL_RET(&current->thread.regs);
segfault:
force_sig(SIGSEGV);
return 0;
}
#else
int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig,
struct pt_regs *regs, sigset_t *set)
{
unsigned long math_size = host_fp_size + FP_XSTATE_MAGIC2_SIZE;
struct rt_sigframe __user *frame;
int err = 0, sig = ksig->sig;
unsigned long fp_to;
frame = (void __user *)stack_top - sizeof(struct rt_sigframe);
/* Add required space for math frame */
frame = (void __user *)((unsigned long)frame - math_size);
/* ABI requires 16 byte boundary alignment */
frame = (void __user *)round_down((unsigned long)frame, 16);
/* Subtract 128 for a red zone and 8 for proper alignment */
frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8);
if (!access_ok(frame, sizeof(*frame) + math_size))
goto out;
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
if (err)
goto out;
}
/* Create the ucontext. */
err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(NULL, &frame->uc.uc_link);
err |= __save_altstack(&frame->uc.uc_stack, PT_REGS_SP(regs));
fp_to = (unsigned long)frame + sizeof(*frame);
err |= copy_sc_to_user(&frame->uc.uc_mcontext,
(struct _xstate __user *)fp_to,
regs, set->sig[0]);
err |= __put_user(fp_to, &frame->uc.uc_mcontext.fpstate);
if (sizeof(*set) == 16) {
err |= __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
err |= __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
}
else
err |= __copy_to_user(&frame->uc.uc_sigmask, set,
sizeof(*set));
/*
* Set up to return from userspace. If provided, use a stub
* already in userspace.
*/
/* x86-64 should always use SA_RESTORER. */
if (ksig->ka.sa.sa_flags & SA_RESTORER)
err |= __put_user((void __user *)ksig->ka.sa.sa_restorer,
&frame->pretcode);
else
/* could use a vstub here */
return err;
if (err)
return err;
PT_REGS_SP(regs) = (unsigned long) frame;
PT_REGS_DI(regs) = sig;
/* In case the signal handler was declared without prototypes */
PT_REGS_AX(regs) = 0;
/*
* This also works for non SA_SIGINFO handlers because they expect the
* next argument after the signal number on the stack.
*/
PT_REGS_SI(regs) = (unsigned long) &frame->info;
PT_REGS_DX(regs) = (unsigned long) &frame->uc;
PT_REGS_IP(regs) = (unsigned long) ksig->ka.sa.sa_handler;
out:
return err;
}
#endif
SYSCALL_DEFINE0(rt_sigreturn)
{
unsigned long sp = PT_REGS_SP(&current->thread.regs);
struct rt_sigframe __user *frame =
(struct rt_sigframe __user *)(sp - sizeof(long));
struct ucontext __user *uc = &frame->uc;
sigset_t set;
if (copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
goto segfault;
set_current_blocked(&set);
if (copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext))
goto segfault;
/* Avoid ERESTART handling */
PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
return PT_REGS_SYSCALL_RET(&current->thread.regs);
segfault:
force_sig(SIGSEGV);
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include <sysdep/stub.h>
#include <sysdep/faultinfo.h>
#include <sysdep/mcontext.h>
#include <sys/ucontext.h>
void __attribute__ ((__section__ (".__syscall_stub")))
stub_segv_handler(int sig, siginfo_t *info, void *p)
{
struct faultinfo *f = get_stub_data();
ucontext_t *uc = p;
GET_FAULTINFO_FROM_MC(*f, &uc->uc_mcontext);
trap_myself();
}
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System call table for UML/i386, copied from arch/x86/kernel/syscall_*.c
* with some changes for UML.
*/
#include <linux/linkage.h>
#include <linux/sys.h>
#include <linux/cache.h>
#include <asm/syscall.h>
extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
/*
* Below you can see, in terms of #define's, the differences between the x86-64
* and the UML syscall table.
*/
/* Not going to be implemented by UML, since we have no hardware. */
#define sys_iopl sys_ni_syscall
#define sys_ioperm sys_ni_syscall
#define sys_vm86old sys_ni_syscall
#define sys_vm86 sys_ni_syscall
#define __SYSCALL_WITH_COMPAT(nr, native, compat) __SYSCALL(nr, native)
#define __SYSCALL_NORETURN __SYSCALL
#define __SYSCALL(nr, sym) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#include <asm/syscalls_32.h>
#undef __SYSCALL
#define __SYSCALL(nr, sym) sym,
const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
#include <asm/syscalls_32.h>
};
int syscall_table_size = sizeof(sys_call_table);
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System call table for UML/x86-64, copied from arch/x86/kernel/syscall_*.c
* with some changes for UML.
*/
#include <linux/linkage.h>
#include <linux/sys.h>
#include <linux/cache.h>
#include <asm/syscall.h>
extern asmlinkage long sys_ni_syscall(unsigned long, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
/*
* Below you can see, in terms of #define's, the differences between the x86-64
* and the UML syscall table.
*/
/* Not going to be implemented by UML, since we have no hardware. */
#define sys_iopl sys_ni_syscall
#define sys_ioperm sys_ni_syscall
#define __SYSCALL_NORETURN __SYSCALL
#define __SYSCALL(nr, sym) extern asmlinkage long sym(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
#include <asm/syscalls_64.h>
#undef __SYSCALL
#define __SYSCALL(nr, sym) sym,
const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
#include <asm/syscalls_64.h>
};
int syscall_table_size = sizeof(sys_call_table);
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/syscalls.h>
#include <os.h>
SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2)
{
return -EINVAL;
}
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright 2003 PathScale, Inc.
*
* Licensed under the GPL
*/
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <asm/prctl.h> /* XXX This should get the constants from libc */
#include <registers.h>
#include <os.h>
long arch_prctl(struct task_struct *task, int option,
unsigned long __user *arg2)
{
long ret = -EINVAL;
switch (option) {
case ARCH_SET_FS:
current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] =
(unsigned long) arg2;
ret = 0;
break;
case ARCH_SET_GS:
current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)] =
(unsigned long) arg2;
ret = 0;
break;
case ARCH_GET_FS:
ret = put_user(current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)], arg2);
break;
case ARCH_GET_GS:
ret = put_user(current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)], arg2);
break;
}
return ret;
}
SYSCALL_DEFINE2(arch_prctl, int, option, unsigned long, arg2)
{
return arch_prctl(current, option, (unsigned long __user *) arg2);
}
void arch_switch_to(struct task_struct *to)
{
/*
* Nothing needs to be done on x86_64.
* The FS_BASE/GS_BASE registers are saved in the ptrace register set.
*/
}
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags,
unsigned long, fd, unsigned long, off)
{
if (off & ~PAGE_MASK)
return -EINVAL;
return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/kallsyms.h>
#include <asm/ptrace.h>
/* This is declared by <linux/sched.h> */
void show_regs(struct pt_regs *regs)
{
printk("\n");
printk("EIP: %04lx:[<%08lx>] CPU: %d %s",
0xffff & PT_REGS_CS(regs), PT_REGS_IP(regs),
smp_processor_id(), print_tainted());
if (PT_REGS_CS(regs) & 3)
printk(" ESP: %04lx:%08lx", 0xffff & PT_REGS_SS(regs),
PT_REGS_SP(regs));
printk(" EFLAGS: %08lx\n %s\n", PT_REGS_EFLAGS(regs),
print_tainted());
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
PT_REGS_AX(regs), PT_REGS_BX(regs),
PT_REGS_CX(regs), PT_REGS_DX(regs));
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
PT_REGS_SI(regs), PT_REGS_DI(regs), PT_REGS_BP(regs));
printk(" DS: %04lx ES: %04lx\n",
0xffff & PT_REGS_DS(regs),
0xffff & PT_REGS_ES(regs));
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright 2003 PathScale, Inc.
*
* Licensed under the GPL
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/utsname.h>
#include <asm/current.h>
#include <asm/ptrace.h>
void show_regs(struct pt_regs *regs)
{
printk("\n");
print_modules();
printk(KERN_INFO "Pid: %d, comm: %.20s %s %s\n", task_pid_nr(current),
current->comm, print_tainted(), init_utsname()->release);
printk(KERN_INFO "RIP: %04lx:%pS\n", PT_REGS_CS(regs) & 0xffff,
(void *)PT_REGS_IP(regs));
printk(KERN_INFO "RSP: %016lx EFLAGS: %08lx\n", PT_REGS_SP(regs),
PT_REGS_EFLAGS(regs));
printk(KERN_INFO "RAX: %016lx RBX: %016lx RCX: %016lx\n",
PT_REGS_AX(regs), PT_REGS_BX(regs), PT_REGS_CX(regs));
printk(KERN_INFO "RDX: %016lx RSI: %016lx RDI: %016lx\n",
PT_REGS_DX(regs), PT_REGS_SI(regs), PT_REGS_DI(regs));
printk(KERN_INFO "RBP: %016lx R08: %016lx R09: %016lx\n",
PT_REGS_BP(regs), PT_REGS_R8(regs), PT_REGS_R9(regs));
printk(KERN_INFO "R10: %016lx R11: %016lx R12: %016lx\n",
PT_REGS_R10(regs), PT_REGS_R11(regs), PT_REGS_R12(regs));
printk(KERN_INFO "R13: %016lx R14: %016lx R15: %016lx\n",
PT_REGS_R13(regs), PT_REGS_R14(regs), PT_REGS_R15(regs));
}
+386
View File
@@ -0,0 +1,386 @@
/*
* Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
* Licensed under the GPL
*/
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <asm/ptrace-abi.h>
#include <os.h>
#include <skas.h>
#include <sysdep/tls.h>
#include <asm/desc.h>
#include <stub-data.h>
/*
* If needed we can detect when it's uninitialized.
*
* These are initialized in an initcall and unchanged thereafter.
*/
static int host_supports_tls = -1;
int host_gdt_entry_tls_min;
static int do_set_thread_area(struct task_struct* task, struct user_desc *info)
{
int ret;
if (info->entry_number < host_gdt_entry_tls_min ||
info->entry_number >= host_gdt_entry_tls_min + GDT_ENTRY_TLS_ENTRIES)
return -EINVAL;
if (using_seccomp) {
int idx = info->entry_number - host_gdt_entry_tls_min;
struct stub_data *data = (void *)task->mm->context.id.stack;
data->arch_data.tls[idx] = *info;
data->arch_data.sync |= BIT(idx);
return 0;
}
ret = os_set_thread_area(info, task->mm->context.id.pid);
if (ret)
printk(KERN_ERR "PTRACE_SET_THREAD_AREA failed, err = %d, "
"index = %d\n", ret, info->entry_number);
return ret;
}
/*
* sys_get_thread_area: get a yet unused TLS descriptor index.
* XXX: Consider leaving one free slot for glibc usage at first place. This must
* be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else.
*
* Also, this must be tested when compiling in SKAS mode with dynamic linking
* and running against NPTL.
*/
static int get_free_idx(struct task_struct* task)
{
struct thread_struct *t = &task->thread;
int idx;
for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++)
if (!t->arch.tls_array[idx].present)
return idx + GDT_ENTRY_TLS_MIN;
return -ESRCH;
}
static inline void clear_user_desc(struct user_desc* info)
{
/* Postcondition: LDT_empty(info) returns true. */
memset(info, 0, sizeof(*info));
/*
* Check the LDT_empty or the i386 sys_get_thread_area code - we obtain
* indeed an empty user_desc.
*/
info->read_exec_only = 1;
info->seg_not_present = 1;
}
#define O_FORCE 1
static int load_TLS(int flags, struct task_struct *to)
{
int ret = 0;
int idx;
for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) {
struct uml_tls_struct* curr =
&to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN];
/*
* Actually, now if it wasn't flushed it gets cleared and
* flushed to the host, which will clear it.
*/
if (!curr->present) {
if (!curr->flushed) {
clear_user_desc(&curr->tls);
curr->tls.entry_number = idx;
} else {
WARN_ON(!LDT_empty(&curr->tls));
continue;
}
}
if (!(flags & O_FORCE) && curr->flushed)
continue;
ret = do_set_thread_area(current, &curr->tls);
if (ret)
goto out;
curr->flushed = 1;
}
out:
return ret;
}
/*
* Verify if we need to do a flush for the new process, i.e. if there are any
* present desc's, only if they haven't been flushed.
*/
static inline int needs_TLS_update(struct task_struct *task)
{
int i;
int ret = 0;
for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
struct uml_tls_struct* curr =
&task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
/*
* Can't test curr->present, we may need to clear a descriptor
* which had a value.
*/
if (curr->flushed)
continue;
ret = 1;
break;
}
return ret;
}
/*
* On a newly forked process, the TLS descriptors haven't yet been flushed. So
* we mark them as such and the first switch_to will do the job.
*/
void clear_flushed_tls(struct task_struct *task)
{
int i;
for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
struct uml_tls_struct* curr =
&task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
/*
* Still correct to do this, if it wasn't present on the host it
* will remain as flushed as it was.
*/
if (!curr->present)
continue;
curr->flushed = 0;
}
}
/*
* In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a
* common host process. So this is needed in SKAS0 too.
*
* However, if each thread had a different host process (and this was discussed
* for SMP support) this won't be needed.
*
* And this will not need be used when (and if) we'll add support to the host
* SKAS patch.
*/
int arch_switch_tls(struct task_struct *to)
{
if (!host_supports_tls)
return 0;
/*
* We have no need whatsoever to switch TLS for kernel threads; beyond
* that, that would also result in us calling os_set_thread_area with
* task->mm == NULL, which would cause a crash.
*/
if (likely(to->mm))
return load_TLS(O_FORCE, to);
return 0;
}
static int set_tls_entry(struct task_struct* task, struct user_desc *info,
int idx, int flushed)
{
struct thread_struct *t = &task->thread;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
return -EINVAL;
t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info;
t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1;
t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed;
return 0;
}
int arch_set_tls(struct task_struct *new, unsigned long tls)
{
struct user_desc info;
int idx, ret = -EFAULT;
if (copy_from_user(&info, (void __user *) tls, sizeof(info)))
goto out;
ret = -EINVAL;
if (LDT_empty(&info))
goto out;
idx = info.entry_number;
ret = set_tls_entry(new, &info, idx, 0);
out:
return ret;
}
static int get_tls_entry(struct task_struct *task, struct user_desc *info,
int idx)
{
struct thread_struct *t = &task->thread;
if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
return -EINVAL;
if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present)
goto clear;
*info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls;
out:
/*
* Temporary debugging check, to make sure that things have been
* flushed. This could be triggered if load_TLS() failed.
*/
if (unlikely(task == current &&
!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) {
printk(KERN_ERR "get_tls_entry: task with pid %d got here "
"without flushed TLS.", current->pid);
}
return 0;
clear:
/*
* When the TLS entry has not been set, the values read to user in the
* tls_array are 0 (because it's cleared at boot, see
* arch/i386/kernel/head.S:cpu_gdt_table). Emulate that.
*/
clear_user_desc(info);
info->entry_number = idx;
goto out;
}
SYSCALL_DEFINE1(set_thread_area, struct user_desc __user *, user_desc)
{
struct user_desc info;
int idx, ret;
if (!host_supports_tls)
return -ENOSYS;
if (copy_from_user(&info, user_desc, sizeof(info)))
return -EFAULT;
idx = info.entry_number;
if (idx == -1) {
idx = get_free_idx(current);
if (idx < 0)
return idx;
info.entry_number = idx;
/* Tell the user which slot we chose for him.*/
if (put_user(idx, &user_desc->entry_number))
return -EFAULT;
}
ret = do_set_thread_area(current, &info);
if (ret)
return ret;
return set_tls_entry(current, &info, idx, 1);
}
/*
* Perform set_thread_area on behalf of the traced child.
* Note: error handling is not done on the deferred load, and this differ from
* i386. However the only possible error are caused by bugs.
*/
int ptrace_set_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc)
{
struct user_desc info;
if (!host_supports_tls)
return -EIO;
if (copy_from_user(&info, user_desc, sizeof(info)))
return -EFAULT;
return set_tls_entry(child, &info, idx, 0);
}
SYSCALL_DEFINE1(get_thread_area, struct user_desc __user *, user_desc)
{
struct user_desc info;
int idx, ret;
if (!host_supports_tls)
return -ENOSYS;
if (get_user(idx, &user_desc->entry_number))
return -EFAULT;
ret = get_tls_entry(current, &info, idx);
if (ret < 0)
goto out;
if (copy_to_user(user_desc, &info, sizeof(info)))
ret = -EFAULT;
out:
return ret;
}
/*
* Perform get_thread_area on behalf of the traced child.
*/
int ptrace_get_thread_area(struct task_struct *child, int idx,
struct user_desc __user *user_desc)
{
struct user_desc info;
int ret;
if (!host_supports_tls)
return -EIO;
ret = get_tls_entry(child, &info, idx);
if (ret < 0)
goto out;
if (copy_to_user(user_desc, &info, sizeof(info)))
ret = -EFAULT;
out:
return ret;
}
/*
* This code is really i386-only, but it detects and logs x86_64 GDT indexes
* if a 32-bit UML is running on a 64-bit host.
*/
static int __init __setup_host_supports_tls(void)
{
check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min);
if (host_supports_tls) {
printk(KERN_INFO "Host TLS support detected\n");
printk(KERN_INFO "Detected host type: ");
switch (host_gdt_entry_tls_min) {
case GDT_ENTRY_TLS_MIN_I386:
printk(KERN_CONT "i386");
break;
case GDT_ENTRY_TLS_MIN_X86_64:
printk(KERN_CONT "x86_64");
break;
}
printk(KERN_CONT " (GDT indexes %d to %d)\n",
host_gdt_entry_tls_min,
host_gdt_entry_tls_min + GDT_ENTRY_TLS_ENTRIES);
} else
printk(KERN_ERR " Host TLS support NOT detected! "
"TLS support inside UML will not work\n");
return 0;
}
__initcall(__setup_host_supports_tls);
+18
View File
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/sched.h>
#include <asm/ptrace-abi.h>
void clear_flushed_tls(struct task_struct *task)
{
}
int arch_set_tls(struct task_struct *t, unsigned long tls)
{
/*
* If CLONE_SETTLS is set, we need to save the thread id
* so it can be set during context switches.
*/
t->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] = tls;
return 0;
}
+79
View File
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stddef.h>
#include <signal.h>
#include <poll.h>
#include <sys/mman.h>
#include <sys/user.h>
#define __FRAME_OFFSETS
#include <linux/ptrace.h>
#include <asm/types.h>
#include <linux/kbuild.h>
#define DEFINE_LONGS(sym, val) \
COMMENT(#val " / sizeof(unsigned long)"); \
DEFINE(sym, val / sizeof(unsigned long))
/* workaround for a warning with -Wmissing-prototypes */
void foo(void);
void foo(void)
{
#ifdef __i386__
DEFINE(HOST_IP, EIP);
DEFINE(HOST_SP, UESP);
DEFINE(HOST_EFLAGS, EFL);
DEFINE(HOST_AX, EAX);
DEFINE(HOST_BX, EBX);
DEFINE(HOST_CX, ECX);
DEFINE(HOST_DX, EDX);
DEFINE(HOST_SI, ESI);
DEFINE(HOST_DI, EDI);
DEFINE(HOST_BP, EBP);
DEFINE(HOST_CS, CS);
DEFINE(HOST_SS, SS);
DEFINE(HOST_DS, DS);
DEFINE(HOST_FS, FS);
DEFINE(HOST_ES, ES);
DEFINE(HOST_GS, GS);
DEFINE(HOST_ORIG_AX, ORIG_EAX);
#else
DEFINE_LONGS(HOST_BX, RBX);
DEFINE_LONGS(HOST_CX, RCX);
DEFINE_LONGS(HOST_DI, RDI);
DEFINE_LONGS(HOST_SI, RSI);
DEFINE_LONGS(HOST_DX, RDX);
DEFINE_LONGS(HOST_BP, RBP);
DEFINE_LONGS(HOST_AX, RAX);
DEFINE_LONGS(HOST_R8, R8);
DEFINE_LONGS(HOST_R9, R9);
DEFINE_LONGS(HOST_R10, R10);
DEFINE_LONGS(HOST_R11, R11);
DEFINE_LONGS(HOST_R12, R12);
DEFINE_LONGS(HOST_R13, R13);
DEFINE_LONGS(HOST_R14, R14);
DEFINE_LONGS(HOST_R15, R15);
DEFINE_LONGS(HOST_ORIG_AX, ORIG_RAX);
DEFINE_LONGS(HOST_CS, CS);
DEFINE_LONGS(HOST_SS, SS);
DEFINE_LONGS(HOST_EFLAGS, EFLAGS);
#if 0
DEFINE_LONGS(HOST_FS, FS);
DEFINE_LONGS(HOST_GS, GS);
DEFINE_LONGS(HOST_DS, DS);
DEFINE_LONGS(HOST_ES, ES);
#endif
DEFINE_LONGS(HOST_IP, RIP);
DEFINE_LONGS(HOST_SP, RSP);
#endif
DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct));
DEFINE(UM_POLLIN, POLLIN);
DEFINE(UM_POLLPRI, POLLPRI);
DEFINE(UM_POLLOUT, POLLOUT);
DEFINE(UM_PROT_READ, PROT_READ);
DEFINE(UM_PROT_WRITE, PROT_WRITE);
DEFINE(UM_PROT_EXEC, PROT_EXEC);
}
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
vdso.lds
+56
View File
@@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0
#
# Building vDSO images for x86.
#
# files to link into the vdso
vobjs-y := vdso-note.o um_vdso.o
# files to link into kernel
obj-y += vdso.o vma.o
vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
$(obj)/vdso.o: $(obj)/vdso.so
targets += vdso.so vdso.so.dbg vdso.lds $(vobjs-y)
CPPFLAGS_vdso.lds += -P -C
VDSO_LDFLAGS_vdso.lds = -m64 -Wl,-soname=linux-vdso.so.1 \
-Wl,-z,max-page-size=4096
$(obj)/vdso.o: $(src)/vdso.S $(obj)/vdso.so
$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(vobjs) FORCE
$(call if_changed,vdso)
$(obj)/%.so: OBJCOPYFLAGS := -S
$(obj)/%.so: $(obj)/%.so.dbg FORCE
$(call if_changed,objcopy)
#
# Don't omit frame pointers for ease of userspace debugging, but do
# optimize sibling calls.
#
CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
$(filter -g%,$(KBUILD_CFLAGS)) -fno-stack-protector \
-fno-omit-frame-pointer -foptimize-sibling-calls
$(vobjs): KBUILD_CFLAGS += $(CFL)
#
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
#
CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage
CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage
#
# The DSO images are built using a special linker script.
#
quiet_cmd_vdso = VDSO $@
cmd_vdso = $(CC) -nostdlib -o $@ \
$(CC_FLAGS_LTO) $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \
-Wl,-T,$(filter %.lds,$^) $(filter %.o,$^)
VDSO_LDFLAGS = -fPIC -shared -Wl,--hash-style=sysv -z noexecstack -Wl,--no-undefined
+54
View File
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
*
* This vDSO turns all calls into a syscall so that UML can trap them.
*/
/* Disable profiling for userspace code */
#define DISABLE_BRANCH_PROFILING
#include <vdso/gettime.h>
#include <linux/time.h>
#include <asm/unistd.h>
int __vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts)
{
long ret;
asm("syscall"
: "=a" (ret)
: "0" (__NR_clock_gettime), "D" (clock), "S" (ts)
: "rcx", "r11", "memory");
return ret;
}
int clock_gettime(clockid_t, struct __kernel_timespec *)
__attribute__((weak, alias("__vdso_clock_gettime")));
int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
{
long ret;
asm("syscall"
: "=a" (ret)
: "0" (__NR_gettimeofday), "D" (tv), "S" (tz)
: "rcx", "r11", "memory");
return ret;
}
int gettimeofday(struct __kernel_old_timeval *, struct timezone *)
__attribute__((weak, alias("__vdso_gettimeofday")));
__kernel_old_time_t __vdso_time(__kernel_old_time_t *t)
{
long secs;
asm volatile("syscall"
: "=a" (secs)
: "0" (__NR_time), "D" (t) : "cc", "r11", "cx", "memory");
return secs;
}
__kernel_old_time_t time(__kernel_old_time_t *t) __attribute__((weak, alias("__vdso_time")));
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Linker script for vDSO. This is an ELF shared object prelinked to
* its virtual address, and with only one read-only segment.
* This script controls its layout.
*/
SECTIONS
{
. = VDSO_PRELINK + SIZEOF_HEADERS;
.hash : { *(.hash) } :text
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.note : { *(.note.*) } :text :note
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
.eh_frame : { KEEP (*(.eh_frame)) } :text
.dynamic : { *(.dynamic) } :text :dynamic
.rodata : { *(.rodata*) } :text
.data : {
*(.data*)
*(.sdata*)
*(.got.plt) *(.got)
*(.gnu.linkonce.d.*)
*(.bss*)
*(.dynbss*)
*(.gnu.linkonce.b.*)
}
.altinstructions : { *(.altinstructions) }
.altinstr_replacement : { *(.altinstr_replacement) }
/*
* Align the actual code well away from the non-instruction data.
* This is the best thing for the I-cache.
*/
. = ALIGN(0x100);
.text : { *(.text*) } :text =0x90909090
}
/*
* Very old versions of ld do not recognize this name token; use the constant.
*/
#define PT_GNU_EH_FRAME 0x6474e550
/*
* We must supply the ELF program headers explicitly to get just one
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
*/
PHDRS
{
text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
note PT_NOTE FLAGS(4); /* PF_R */
eh_frame_hdr PT_GNU_EH_FRAME;
}
+12
View File
@@ -0,0 +1,12 @@
/*
* This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
* Here we can supply some information useful to userland.
*/
#include <linux/uts.h>
#include <linux/version.h>
#include <linux/elfnote.h>
ELFNOTE_START(Linux, 0, "a")
.long LINUX_VERSION_CODE
ELFNOTE_END
+11
View File
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/init.h>
#include <linux/linkage.h>
__INITDATA
SYM_DATA_START(vdso_start)
.incbin "arch/x86/um/vdso/vdso.so"
SYM_DATA_END_LABEL(vdso_start, SYM_L_GLOBAL, vdso_end)
__FINIT
+31
View File
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Linker script for 64-bit vDSO.
* We #include the file to define the layout details.
* Here we only choose the prelinked virtual address.
*
* This file defines the version script giving the user-exported symbols in
* the DSO. We can define local symbols here called VDSO* to make their
* values visible using the asm-x86/vdso.h macros from the kernel proper.
*/
#define VDSO_PRELINK 0xffffffffff700000
#include "vdso-layout.lds.S"
/*
* This controls what userland symbols we export from the vDSO.
*/
VERSION {
LINUX_2.6 {
global:
clock_gettime;
__vdso_clock_gettime;
gettimeofday;
__vdso_gettimeofday;
time;
__vdso_time;
local: *;
};
}
VDSO64_PRELINK = VDSO_PRELINK;
+55
View File
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
*/
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/page.h>
#include <asm/elf.h>
#include <linux/init.h>
unsigned long um_vdso_addr;
static struct page *um_vdso;
extern unsigned long task_size;
extern char vdso_start[], vdso_end[];
static int __init init_vdso(void)
{
BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
um_vdso_addr = task_size - PAGE_SIZE;
um_vdso = alloc_page(GFP_KERNEL);
if (!um_vdso)
panic("Cannot allocate vdso\n");
copy_page(page_address(um_vdso), vdso_start);
return 0;
}
subsys_initcall(init_vdso);
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct vm_area_struct *vma;
struct mm_struct *mm = current->mm;
static struct vm_special_mapping vdso_mapping = {
.name = "[vdso]",
.pages = &um_vdso,
};
if (mmap_write_lock_killable(mm))
return -EINTR;
vma = _install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
VM_READ|VM_EXEC|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
&vdso_mapping);
mmap_write_unlock(mm);
return IS_ERR(vma) ? PTR_ERR(vma) : 0;
}