系统调用
来源:互联网 发布:windows徽标键与t 编辑:程序博客网 时间:2024/06/06 02:56
#define real_read(fd, buf, count ) (syscall(SYS_read, (fd), (buf), (count)))
其实真正调用的还是系统函数syscall(SYS_read),也就是sys_read()函数中,在Linux2.6.37中的利用几个宏定义实现。
Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体的系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同的系统调用分别处理(调用不同的内核函数处理)。
引起系统调用的两种途径
(1)int $0×80 , 老式linux内核版本中引起系统调用的唯一方式
(2)sysenter汇编指令
在Linux内核中使用下面的宏进行系统调用SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
其中SYSCALL_DEFINE3的宏定义如下:
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
##的意思就是宏中的字符直接替换,
如果name = read,那么在宏中__NR_##name就替换成了__NR_read了。 __NR_##name是系统调用号,##指的是两次宏展开.即用实际的系统调用名字代替"name",然后再把__NR_...展开.如name == ioctl,则为__NR_ioctl。
#ifdef CONFIG_FTRACE_SYSCALLS
#define SYSCALL_DEFINEx(x, sname, ...) \
static const char *types_##sname[] = { \
__SC_STR_TDECL##x(__VA_ARGS__) \
}; \
static const char *args_##sname[] = { \
__SC_STR_ADECL##x(__VA_ARGS__) \
}; \
SYSCALL_METADATA(sname, x); \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#else
#define SYSCALL_DEFINEx(x, sname, ...) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#endif
不管是否定义CONFIG_FTRACE_SYSCALLS宏,最终都会执行 下面的这个宏定义:
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
#define SYSCALL_DEFINE(name) static inline long SYSC_##name
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \
asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \
{ \
__SC_TEST##x(__VA_ARGS__); \
return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \
} \
SYSCALL_ALIAS(sys##name, SyS##name); \
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))
#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
#define SYSCALL_DEFINE(name) asmlinkage long sys_##name
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
最终会调用下面类型的宏定义:
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
也就是我们前面提到的sys_read()系统函数。
asmlinkage通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词!这和我们上一篇文章quagga中提到的宏定义,有异曲同工之妙。
也就是宏定义中的下面代码:
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count, &pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
代码解析:
- fget_light() :根据 fd 指定的索引,从当前进程描述符中取出相应的 file 对象(见图3)。
- 如果没找到指定的 file 对象,则返回错误
- 如果找到了指定的 file 对象:
- 调用 file_pos_read() 函数取出此次读写文件的当前位置。
- 调用 vfs_read() 执行文件读取操作,而这个函数最终调用 file->f_op.read() 指向的函数,代码如下:
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
- 调用 file_pos_write() 更新文件的当前读写位置。
- 调用 fput_light() 更新文件的引用计数。
- 最后返回读取数据的字节数。
到此,虚拟文件系统层所做的处理就完成了,控制权交给了 ext2 文件系统层。
http://blogold.chinaunix.net/u3/104447/showart_2527011.html
使用 syscall/sysret 指令
在这里,我想介绍一下 syscall 与 sysret 这对指令。关于这对指令请以 AMD 的手册为准,毕竟它们的 AMD 的产物。当然不能说 Intel 的不对,Intel 对 syscall/sysret 的完全兼容的。
可是,在 AMD 与 Intel 的 processor 上还是有区别的:
- 在 AMD 的 processor 上:syscall/sysret 指令在 long mode 和 protected mode(指的是 Legacy x86 和 compatibility mode)上都是有效的(valid)。
- 在 Intel processor 上:syscall/sysret 指令只能在 64-bit 模式上使用,compatibility 模式和 Legacy x86 模式上都是无效的。可是 sysret 指令虽然不能在 compatibility 模式下执行,但 sysret 却可以返回到 compaitibility 模式。这一点只能是认为了兼容 AMD 的 sysret 指令。
怎么办,这会不会出现兼容上的问题?这里有一个折衷的处理办法:
在 64 位环境里统一使用 syscall/sysret 指令,在 32 位环境里统一使用 sysenter/sysexit 指令然而依旧会产生一些令人不愉快的顾虑:
没错,在 compatibility 模式下谁都不兼容谁:Intel 的 syscall/sysret 指令不能在 compatibility 模式下执行;AMD 的 sysenter/sysexit 指令也不能在 compatibility 模式下执行。
因此:在 compatibility 模式下必须切换到 64 位模式,然后使用 syscall/sysret 指令
1. syscall 指令的逻辑
下面是用 C 语言描述 syscall 指令执行的逻辑:
MSR_EFER EFER;
MSR_STAR STAR;
MSR_LSTAR LSTAR;
MSR_CSTAR CSTAR;
MSR_SFMASK SFMASK;
void syscall()
{
if (EFER.SCE == 0) /* system call extensions is disable */
do_exception_UD(); /* #UD exception */
if (EFER.LMA == 1) { /* long mode is active */
rcx = rip; /* save rip for syscall return */
r11 = rflags; /* save rflags to r11 */
/*
* CS.L == 1 for 64-bit mode, rip from MSR_LSTAR
* CS.L == 0 for compatibility, rip from MSR_CSTAR
*/
rip = CS.attribute.L ? LSTAR : CSTAR;
/*
* processor set CS register
*/
CS.selector = STAR.SYSCALL_CS; /* load selector from MSR_STAR.SYSCALL_CS */
CS.selector.RPL = 0; /* RPL = 0 */
CS.attribute.S = 1; /* user segment descriptor */
CS.attribute.C_D = 1; /* code segment */
CS.attribute.L = 1; /* 64-bit */
CS.attribute.D = 0; /* 64-bit */
CS.attribute.DPL = 0; /* CPL = 0 */
CS.attribute.P = 1; /* present = 1 */
CS.base = 0;
CS.limit = 0xFFFFFFFF;
/*
* processor set SS register
*/
SS.selector = STAR.SYSCALL_CS + 8;
SS.attribute.S = 1;
SS.attribute.C_D = 0;
SS.attribute.P = 1;
SS.attribute.DPL = 0;
SS.base = 0;
SS.limit = 0xFFFFFFFF;
/* set rflags */
rflags = rflags & ~ SFMASK;
rflags.RF = 0;
/* goto rip ... */
} else {
/* legacy mode */
rcx = (unsigned long long)eip; /* eip extend to 64 load into rcx */
rip = (unsigned long long)STAR.EIP; /* get eip from MSR_STAR.EIP */
CS.selector = STAR.SYSCALL_CS;
CS.selector.RPL = 0;
CS.attribute.S = 1; /* user descriptor */
CS.attribute.C_D = 1; /* code segment */
CS.attribute.D = 1; /* 32-bit */
CS.attribute.C = 0; /* non-conforming */
CS.attribute.R = 1; /* read/execute */
CS.attribute.DPL = 0; /* CPL = 0 */
CS.attribute.P = 1; /* present = 1 */
CS.attribute.G = 1; /* G = 1 */
CS.base = 0;
CS.limit = 0xFFFFFFFF;
SS.selector = STAR.SYSCALL_CS + 8;
SS.attribute.S = 1; /* user descriptor */
SS.attribute.C_D = 0; /* data segment */
SS.attribute.D = 1; /* 32-bit esp */
SS.attribute.E = 0; /* expand-up */
SS.attribute.W = 1; /* read/write */
SS.attribute.P = 1; /* present */
SS.attribute.DPL = 0; /* DPL = 0 */
SS.attribute.G = 1; /* G = 1 */
SS.base = 0;
SS.limit = 0xFFFFFFFF;
rflags.VM = 0;
rflags.IF = 0;
rflags.RF = 0;
/* goto rip */
}
}
syscall 指令促使 processor 进行一系列的强制行为:
- 目标代码 DPL = 0,强制切换到 CPL = 0
- CS 被强制为 non-conforming,read/execute 属性,当 long mode 下,目标代码为 64 位,当 legacy mode 下目标代码为 32 位。
- SS 被强制为 expand-up,read/write 属性
- base 都被 0
- limit 都为 4G
在执行 syscall 指令前,processor 会检测 EFER 寄存器的 SCE 标志位,以确认是否开启 System Call Extension 功能,如果没有开启会产生 #UD 无效 opcode 码异常。
可以看出 syscall 指令只是加载 selector,但是并不进行实质的 descriptor 加载行为,强制设置 CS 和 SS 寄存器以满足系统服务例程(指 CPL=0 下的服务例程)的工作环境。
2. sysret 指令的逻辑
sysret 指令执行的情形有些稍复杂,我举些例子来说明。
2.1 从 64 位返回到 64 位
bits 64
... ...
pop rcx ; restore rcx for rip
pop r11 ; restore r11 for rflags
db 0x48 ; 64-bit operand size for sysret !!!
dw 0x070f ; sysret
别看上面的汇编代码怪异,实际上它是最正常的代码,使用手工编码的方式是基于:确保编译器能够编译出我们想要的机器指令。
sysret 指令的缺省操作数是 32 位的,因此这里使用 REX prefix 将 sysret 指令的操作数调整到 64 位,现在我们的代码就返回到 64-bit 模式。这是我们想要的结果:从 64-bit Level-0 代码返回到 64-bit Level-3 代码。
2.2 从 64 位返回到 compatibility 模式
bits 64
... ...
pop rcx ; restore rcx for rip
pop r11 ; restore r11 for rflags
sysret ; 32-bit operand size
这里看上去很正常,其实不正常的汇编代码(不正常是指:不是我们想要的),我们让编译器来产生 sysret 指令机器码,那么编译器会产生 32 位操作数的机器指令。在这情形下,将会返回到 compatibility 模式,这样大多数情况下并不是我们想的结果。
有没有可能通过 sysret 返回到 legacy 模式?当然不可能。回到 legacy 模式要经过一系列的切换工作,这方面的工作,详见:http://www.mouseos.com/arch/exit_longmode.html
sysret 的逻辑如下:
void sysret()
{
if (EFER.SCE == 0) /* System Call Extension is disable */
do_exception_UD();
if (CR0.PE == 0 || CS.attribute.DPL != 0) /* protected mode is disable or CPL != 0 */
do_exception_GP();
if (CS.attribute.L == 1) /* 64-bit mode */
{
if (REX.W == 1) /* 64-bit operand size */
{
/*
* return to 64-bit code !
*/
CS.selector = STAR.SYSRET_CS + 16; /* 64-bit code segment selector */
CS.selector.RPL = 3; /* CPL = 3 */
CS.attribute.L = 1;
CS.attribute.D = 0;
CS.attribute.P = 1;
CS.attribute.DPL = 3;
CS.base = 0;
CS.limit = 0xFFFFFFFF;
rip = rcx; /* restore rip for return */
} else {
/*
* return to compatibility !
*/
CS.selector = STAR.SYSRET_CS; /* 32-bit code segment selector */
CS.selector.RPL = 3;
CS.attribute.L = 0; /* compatibility mode */
CS.attribute.D = 1; /* 32-bit code */
CS.attribute.P = 1;
CS.attribute.C = 0;
CS.attribute.R = 1;
CS.attribute.DPL = 3;
CS.base = 0;
CS.limit = 0xFFFFFFFF;
rip = (unsigned long long)ecx;
}
SS.selector = START.SYSRET_CS + 8; /* SS selector for return */
rflags = r11; /* restore rflags */
/* goto rip */
} else { /* compatibility or legacy mode */
CS.selector = STAR.SYSRET_CS; /* 32-bit code segment selector */
CS.selector.RPL = 3
CS.attribute.L = 0; /* compatibility mode */
CS.attribute.D = 1; /* 32-bit code */
CS.attribute.P = 1;
CS.attribute.C = 0;
CS.attribute.R = 1;
CS.attribute.DPL = 3;
CS.base = 0;
CS.limit = 0xFFFFFFFF;
SS.selector = STAR.SYSRET_CS + 8;
rflags.IF = 1;
rip = (unsigned long long)ecx;
}
}
processor 同样要检查是否开启了 System Call Extension 功能,并且检查是否处于保护模式,当前的 CPL 是否为 0,和 syscall 指令一样,sysret 不做 descriptor 的加载工作,同样需要强制设置 CS 以满足用户环境(指的是 CPL=3 下的代码),可是:processor 并不设置 SS 寄存器,那么需要加载 data segment descriptor 进入 SS 寄存器
3. syscall/sysret 使用的寄存器
为了支持 syscall/sysret 指令,AMD 新增了4个 MSR 寄存器:
- STAR
- LSTAR
- CSTAR
- SFMASK
在 Intel 下 STSR 被称作 IA32_STAR,LSTAR 被称作 IA32_LSTAR, SFMASK 被称作 IA32_SFMASK, 虽然是冠以 IA32 体系,但是请相信它们是 64 位的。除前面所说的只能在 64 位环境执行,其它方面完全是兼容 AMD 的。
4. STAR 寄存器
通过上图我们已经明白了 STAR 寄存器的用途:
- 在 legacy x86 下提供 eip 值(仅在 legacy x86 模式下)
- 为 syscall 指令提供目标代码的 CS 和 SS selector
- 为 sysret 指令提供返回代码的 CS 和 SS selector
因此,STAR 寄存器分为三部分:
- [31:00] - SYSCALL_EIP
- [47:32] - SYSCALL_CS
- [63:48] - SYSRET_CS
STAR 寄存器的地址是 C0000081h,我们可以使用 wrmsr 指令来写 STAR 寄存器
下面是来自我的 mouseos 0.01 版中的初始化 syscall 环境的 init_syscall() 代码:
init_syscall:
;-------------------------------------------------------
; Note: the sysret instruction: not change SS.RPL to 3
; So: MSR_STAR.SYSRET_CS.RPL must be to set 3 !!!!
;-------------------------------------------------------
mov edx, SYSCALL_CS | ((SYSRET_CS | 0x3) << 16)
xor eax, eax
mov ecx, MSR_STAR ; MSR_STAR's address
wrmsr ; write edx:eax into MSR_STAR register
mov rax, sys_services_order * 5 + MICKEY_CODE_ENTRY
mov rdx, rax
shr rdx, 32
mov ecx, MSR_LSTAR ; set MSR_LSTAR = sys_services
wrmsr
xor edx, edx
xor eax, eax
mov ecx, MSR_SFMASK ; set MSR_SFMASK = 0
wrmsr
ret
在 ECX 寄存器中提供 MSR_STAR 的地址,64 位的值由 EDX:EAX 提供,高 32 位放在 EDX 寄存器,低 32 位放在 EAX 寄存器。
init_syscall 代码中分别设置了 STAR 和 LSTAR 以及 SFMASK 寄存器,这里 SFMASK 被设为 0
4.1 设置 SYSCALL_CS 以及 SYSCALL_SS
你应该让 SYSCALL_CS 提供索引 DPL=0 的 Code Segment Descriptor 的 selector。
注意:尽管 processor 会忽略你提供的 SYSCALL_CS.RPL 值,而强制 SYSCALL_CS.RPL = 0,但是你必须将 SYSCALL_CS.RPL 设为 0。
那是因为:SYSCALL_SS 由 SYSCALL_CS + 8 而来!
SYSCALL_SS = SYSCALL_CS + 8,这表示:目标代码的 SS descriptor 是 CS descriptor 的下一个 descriptor
下面同样是来自 mouseos 0.01 的代码,为 syscall 设置 descriptor
;--------------------------------------------------------------
; kernel_cs & kernel_ss for syscall into kernel code
;--------------------------------------------------------------
kernel_cs_desc dd 0 ; 0x0b
dd 0x00209800
kernel_ss_desc dd 0 ; 0x0c
dd 0x00009200
在这个代码中 SYSCALL_CS 设为 0x0b,那么 SYSCALL_SS 就是 0x0c,因此我们在设置 SYSCALL_CS 必须考虑到下一个应该是 SYSCALL_SS
4.2 设置 SYSRET_CS 以及 SYSRET_SS
同理,你也应该让 SYSRET_CS 提供索引 DPL=3 的 Code Segment Descriptor 的 selector,同样 SYSRET_SS = SYSRET_CS + 8,因此:你必须设置 SYSRET_CS.RPL = 3,以此来设置返回的 SYSRET_SS.RPL = 3。
注意,SYSRET_CS 提供的是 3 个 selector,分别是:
- 32-bit code 的 selector:用来返回到 compaitibility 模式代码,以及 legacy x86 模式代码
- SS selector:这个 selector 既是 32-bit 下的 selector,也是 64-bit 下的 selector,将会被加载到 SS 寄存器,descriptor 也会被加载。
- 64-bit code 的 selector:这个是用来返回到 64-bit 模式代码
那么:
- SYSRET_CS:32-bit code segment descriptor selector(包括 legacy x86 的 16-bit 代码)
- SYSRET_CS+8:stack segment descriptor selector
- SYSRET_CS+16:64-bit code segment descriptor selector
这里很容易让人产生疑问:为什么 code segment descriptor selector 分 32-bit(包括 legacy 16-bit)和 64-bit 两个,SS selector 只有一个?
实际上,这个问题很简单,很容易明白其中的道理,只要我们明白 data segment descriptor 的结构就知道了。
上图是 legacy mode 下的 data segment descriptor 结构,下图是 long mode 下的 data segment descriptor 结构(实际上不包括 compaitibility 模式),可以看出 data segment descriiptor 在 legacy 模式下与 long mode 模式下都是一样的。只是在 64-bit 模式下,绝大部分是无效的。
同一个 data segment descriptor 由 processor 当前的状态来决定是属于 legacy mode 下的 data segment descriptor 还是 64-bit 下的 data segment desciptor,因此,只提供一个 data segment descriptor selector 来加载到 SS 寄存器。
4.3 SYSRET_EIP
这部分是为 legacy mode 下提供的,当 processor 处于 32-bit protected mode 下,像这样:
bits 32
......
syscall ; 32-bit mode syscall! eip = MSR_STAR.SYSCALL_EIP
在 32 位代码下执行 syscall 指令,目标的 eip 将从 STAR 的 SYSCALL_EIP 部分提取!
当然使用前需设置 SYSCALL_EIP 值:
bits 32
init_syscall32:
... ...
mov eax, syscall32_entry ; 32-bit syscall entry
mov edx, (user_cs << 16) | (kernel_cs) ; SYSRET_CS and SYSCALL_CS selector
mov ecx, 0C0000081h ; MSR_STAR address
wrmsr ; write MSR_STAR register
... ...
5. LSTAR 寄存器
LSTAR 寄存器为 64-bit 代码的提供目标的 rip 值。
5.1 设置 LSTAR 寄存器
下面代码示范了设置 LSTAR 寄存器:
bits 64
init_syscall:
... ...
mov rax, syscall64_entry ; 64-bit syscall entry
mov rdx, rax
shr rdx, 32
mov ecx, 0C0000082h ; MSR_LSTAR address
wrmsr
... ...
64 位的 rip 值需要被分两部分,低 32 位放在 eax 寄存器,高 32 位放在 edx 寄存器,EDX:EAX 形成 64 位的 rip 值。必须注意的是:这个 64 位地址值是 canonical 形式的值,否则 wrmsr 会产生 #GP 异常。这段初始化代码虽然在 64 位执行,当然你可以在 32 位代码下对 MSR_LSTAR 完成初始化工作,这是正确的。
5.2 使用 LSTAR 寄存器
bits 64
;---------------------------------------------
; Now: processor is run on 64-bit mode
;---------------------------------------------
sys_service_call:
... ...
syscall ; fast call system service routine, load rip from LSTAR
ret
当 processor 运行在 64-bit 模式下,执行 syscall 指令时,目标代码的 rip 将从 LSTAR 中加载。
5.3 返回 64-bit
bits 64
;---------------------------------------------
; Now: processor is run on 64-bit mode
;---------------------------------------------
sys_service_return:
... ...
pop rcx ; restore rip
pop r11 ; restore rflags
db 0x48 ; operand size is 64-bit for return to 64-bit code
sysret ; return user code
前面说过,我们需使用 REX prefix 进行调整 sysret 指令的操作数,以它能够正确返回到 64-bit 代码,否则将会返回到 compatibility 模式。如果有需要应该恢复 rcx 和 r11 寄存器值。取决你有没有改变 rcx 和 r11 的值。因为 rip 和 rflags 需要从 rcx 和 r11 提取。
6. CSTAR 寄存器
CSTAR 寄存器为 compatibility 模式下的代码提供 rip 值,当 processor 在 comatibility 模式下运行时,执行了 syscall 指令,此时 rip 值将从 MSR_STAR 寄存器中加载。
请记住:只能在 AMD 的 processor 使用 compaitibility 模式下的调用。正如下面的代码:
bits 32
;---------------------------------------------
; Now: processor is run on compatibility mode
;---------------------------------------------
sys_service_entry:
... ...
syscall ; fast call system service routine, load rip from MSR_CSTAR
ret
执行 syscall 指令时,processor 处于 compaitibility 模式,那么 rip 将从 CSTAR 寄存器取得目标代码的 rip 值。实际上执行的结果是:会从 compatibility 模式切换到 64-bit 模式
那么,在系统的服务例程执行完毕后,使用 sysret 指令会从 64-bit 模式切换回 compatibility 模式继续执行,正如下面代码所示:
bits 64
;---------------------------------------------
; Now: processor is run on 64-bit mode
;---------------------------------------------
sys_service_return_to_compatibility:
... ...
pop rcx ; restore for return eip
pop r11 ; restore for return rflags
sysret ; return to user's compatibility mode
这段代码在 64-bit 模式下执行,但是返回到 compaitibility 模式。
6.1 通用性的考虑
照顾通用性,为了在 Intel 和 AMD 的 processor 上都能够使用 fast call 功能,操作系统的设计者应该要避免在 comaptibility 模式下使用 syscall 指令。前面提到过,建议在 compatibility 模式下先切换到 64-bit 模式后,再执行 syscall 指令。
正如下面的代码,在系统中提供切换的 stub 库功能:
6.1.1 切换到 64-bit
在 compatibility 模式下执行切换到 64-bit 模式,如下示例:
bits 32
;----------------------------------------------
; Now: processor is run on compatibility mode
;----------------------------------------------
switch_to_64_entry:
mov ecx, return_position ; save return position for compatibility mode
jmp far sel64:sys_service_entry_stub ; 64-bit stub function
; Now: processor switch to 64-bit mode from compatibility !
return_position:
ret ; return to calling
这段代码先保存返回值到 ecx 寄存器,然然通过远跳转(jmp far)切换到 64 位代码,此时 processor 的权限是不变的。 切换后 CPL 还是 3
6.1.2 执行 fast call
现在正处于 64-bit 代码下,CPL=3,下面代码进行 fast call:
bits 64
;----------------------------------------------------
; Now: processor is run on 64-bit mode
;----------------------------------------------------
sys_service_entry_stub:
mov eax, ecx ; return position
push sel32 ; 32-bit code selector
push rax ; return position
syscall ; fast call into system service routine
db 0x48 ; operand size is 64-bit for retf
retf ; return to compatibility, switch to 32-bit
为了返回原来的 compatibility 模式代码,我们可以使用 retf 指令切换回原来的 compatibility 模式,我们依次压入 32-bit selector 和返回地址,再执行我们需要的 syscall 指令。这样当使用 sysret 指令返回后,就可以执行 retf 指令切换回 compatibility 模式。
7. SFMASK 寄存器
在 long mode 下,当执行 syscall 指令时,当前的 rflags 寄存器值被保存在 r11 寄存器,processor 在执行 syscall 时,准备的目标执行环境中,rflags 将会根据 SFMASK 寄存器的值进行设置:如果 SFMASK 寄存器的某一位置为 1,那么 rflags 寄存器中相应的位将会被清 0,置为 0 时,rflags 寄存器中相应位不变
它的逻辑 C 描述为:
rflags = rflags & (~sfmask);下面代码显示了如何使用 SFMASK 寄存器:
init_syscall:
... ...
xor eax, eax
xor edx, edx
bts eax, 14 ; for rflags.NT = 0
mov ecx, 0C0000084h ; MSR_SFMASK address
wrmsr
在这段初台化 SFMASK 寄存器代码中,将 SFMASK 的 bit 14 置为 1,这样的结果导致执行 syscall 指令后,rflags.NT 将会被清为 0(bit14 是 NT 标志)。
你应该在系统服务例程先保存 r11 值(原来的 rflags 寄存器值),以便 sysret 执行返回时可以恢复原来的 rflags 值。
版权 mik 所有,转载请注明出处
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- 系统调用
- iOS开发判断身份证号是否合法
- 正则表达式笔记
- 学java心得体会
- django集成已有的数据库
- Android消息机制源码分析--Lopper,Handler,Message
- 系统调用
- 开发自动化运维管理平台
- linux下sh脚本的一个小问题#!/bin/sh^M不是一个file的错误
- Linux下、sudoers的权限被更改后,不能sudo的解决办法
- 关于ViewFlipper的使用
- 使用Spock框架进行单元测试
- leveldb代码阅读(15)——内存中的数据结构Memtable/SkipList
- Redis数据备份与恢复
- 字典转为Json字符串