Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

syscall Re: 快速系统调用 sysenter和syscall

143 views
Skip to first unread message

3ger

unread,
Apr 21, 2011, 6:10:13 AM4/21/11
to

对于amd的cpu,syscall和sysret在长模式(64位)和保护模式(32位)都有效。
对于intel的cpu,syscall和sysret在64位模式有效,在32位模式无效。

看一下64位下的系统调用是如何使用syscall和sysret的

仍从glibc-2.9看起,系统调用的入口syscall,定义在sysdeps\unix\sysv\linux\x86_64\syscall.s

30:ENTRY (syscall)
31: movq %rdi, %rax /* Syscall number -> rax. */
32: movq %rsi, %rdi /* shift arg1 - arg5. */
33: movq %rdx, %rsi
34: movq %rcx, %rdx
35: movq %r8, %r10
36: movq %r9, %r8
37: movq 8(%rsp),%r9 /* arg6 is on the stack. */
38: syscall /* Do the system call. */
39: cmpq $-4095, %rax /* Check %rax for error. */
40: jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
42: ret /* Return to caller. */

31行将系统调用号放入寄存器rax,32行-37行将参数在寄存器中排好。

38行执行syscall指令,cpu操作如下:

1.将MSR_STAR 47-32位加16的值装载到cs
2.将MSR_LSTAR的值装载到rip
3.将MSR_STAR 47-32位加8的值装载到ss
5.切换到内核空间,执行内核入口的第一条指令

和syscall/sysret相关的寄存器:
MSR_STAR: 63-48位是sysret的32模式cs段选择符,加8是sysret的ss段选择符,加16是sysret的64位模式cs段选择符
47-32位是syscall的cs段选择符,加8是syscall的ss段选择符
31- 0位是32位模式syscall的eip
MSR_LSTAR: 64位模式syscall的rip
MSR_CSTAR: 兼容模式syscall的rip
MSR_SYSCALL_MASK:31-0位是一些标志掩码

函数syscall_init初始化这些寄存器,定义在arch\x86\kernel\cpu\common.c

1023:void syscall_init(void)
1024:{
1030: wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
1031: wrmsrl(MSR_LSTAR, system_call);
1032: wrmsrl(MSR_CSTAR, ignore_sysret);

1034:#ifdef CONFIG_IA32_EMULATION
1035: syscall32_cpu_init();
1036:#endif

1039: wrmsrl(MSR_SYSCALL_MASK,
1040: X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
1041:}

MSR_LSTAR被设成system_call,函数system_call就是64位下系统调用syscall的内核入口。

【 在 four3ger (3ger) 的大作中提到: 】
: ia32_sysenter_target定义在entry_32.s
: 399:ENTRY(ia32_sysenter_target)
: 404: movl TSS_sysenter_sp0(%esp),%esp
: ...................

--
蝎子,蝎子,我爱你

[m [37m※ 来源:·水木社区 http://newsmth.net·[FROM: 59.64.161.*] [m

我的饭盒我的饭

unread,
Apr 21, 2011, 7:38:34 AM4/21/11
to
intel 也是用的,Windows XP 就开始用了,Linux 怎么回落后呢:
32 位 intel cpu 是用 sysenter/sysleave
Dump of assembler code for function __kernel_vsyscall:
0xffffe400 <+0>: push %ecx
=> 0xffffe401 <+1>: push %edx
0xffffe402 <+2>: push %ebp
0xffffe403 <+3>: mov %esp,%ebp
0xffffe405 <+5>: sysenter
0xffffe407 <+7>: nop
0xffffe408 <+8>: nop
0xffffe409 <+9>: nop
0xffffe40a <+10>: nop
0xffffe40b <+11>: nop
0xffffe40c <+12>: nop
0xffffe40d <+13>: nop
0xffffe40e <+14>: jmp 0xffffe403 <__kernel_vsyscall+3>
0xffffe410 <+16>: pop %ebp
0xffffe411 <+17>: pop %edx
0xffffe412 <+18>: pop %ecx
0xffffe413 <+19>: ret
End of assembler dump.

【 在 four3ger (3ger) 的大作中提到: 】
: 对于amd的cpu,syscall和sysret在长模式(64位)和保护模式(32位)都有效。
: 对于intel的cpu,syscall和sysret在64位模式有效,在32位模式无效。
: 看一下64位下的系统调用是如何使用syscall和sysret的
: ...................

--
C++ 追求高效,C++标准委员会却很低效。


[m [1;31m※ 来源:·水木社区 newsmth.net·[FROM: 183.16.109.*] [m

3ger

unread,
Apr 21, 2011, 7:47:16 AM4/21/11
to

32位intel cpu是用sysenter/sysexit
但syscall/sysret只在64位用

【 在 RoachCock (我的饭盒我的饭) 的大作中提到: 】
: intel 也是用的,Windows XP 就开始用了,Linux 怎么回落后呢:


: 32 位 intel cpu 是用 sysenter/sysleave
: Dump of assembler code for function __kernel_vsyscall:

: ...................

--
蝎子,蝎子,我爱你

[m [31m※ 来源:·水木社区 http://newsmth.net·[FROM: 59.64.161.*] [m

我的饭盒我的饭

unread,
Apr 21, 2011, 8:05:30 AM4/21/11
to
sysenter 就 intel 家在 32 位模式下的 syscall 的等价物啊。

【 在 four3ger (3ger) 的大作中提到: 】
: 32位intel cpu是用sysenter/sysexit
: 但syscall/sysret只在64位用

unread,
Apr 21, 2011, 7:52:42 AM4/21/11
to
这两个公司也真是的,连系统调用指令也能设计出两套不同的东西来

intel也真是的,抄amd64的时候把64位的syscall抄过来了也不顺便在32位模式也实现了

【 在 four3ger (3ger) 的大作中提到: 】
: 32位intel cpu是用sysenter/sysexit
: 但syscall/sysret只在64位用


--

[m [1;36m※ 来源:·水木社区 newsmth.net·[FROM: 211.99.222.*] [m

3ger

unread,
Apr 25, 2011, 5:41:51 AM4/25/11
to

system_call定义在entry_64.s

455:ENTRY(system_call)

469: movq %rsp,PER_CPU_VAR(old_rsp)
470: movq PER_CPU_VAR(kernel_stack),%rsp

475: ENABLE_INTERRUPTS(CLBR_NONE)
476: SAVE_ARGS 8,1
477: movq %rax,ORIG_RAX-ARGOFFSET(%rsp)
478: movq %rcx,RIP-ARGOFFSET(%rsp)

480: GET_THREAD_INFO(%rcx)
481: testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%rcx)
482: jnz tracesys
483:system_call_fastpath:
484: cmpq $__NR_syscall_max,%rax
485: ja badsys
486: movq %r10,%rcx
487: call *sys_call_table(,%rax,8)
488: movq %rax,RAX-ARGOFFSET(%rsp)

493:ret_from_sys_call:
494: movl $_TIF_ALLWORK_MASK,%edi

496:sysret_check:
497: LOCKDEP_SYS_EXIT
498: GET_THREAD_INFO(%rcx)
499: DISABLE_INTERRUPTS(CLBR_NONE)
500: TRACE_IRQS_OFF
501: movl TI_flags(%rcx),%edx
502: andl %edi,%edx
503: jnz sysret_careful

508: TRACE_IRQS_ON
509: movq RIP-ARGOFFSET(%rsp),%rcx

511: RESTORE_ARGS 0,-ARG_SKIP,1
513: movq PER_CPU_VAR(old_rsp), %rsp
514: USERGS_SYSRET64

469行-470行:将用户堆栈指针保存到old_rsp,将内核堆栈指针(kernel_stack)放到rsp,切换到内核堆栈

476行-478行:在内核堆栈中保存现场

rcx(返回地址)
rax(系统调用号)
rdi
rsi
rdx
xxx
rax
r8
r9
r10
r11 <-rsp

和32位模式的比较一下

old ss
old esp
eflags
cs
eip
系统调用号
gs
fs
es
ds
eax
ebp
edi
esi
edx
ecx
ebx <--sp

64位模式的syscall没有将用户代码段和堆栈段、返回地址和flags放入堆栈。
用户代码段和堆栈段使用寄存器MSR_STAR的值,返回地址和flags保存在寄存器rcx和r11。

484行判断系统调用号是否超出范围,487行调用系统调用表中的函数。
488行将返回值(寄存器rax)放入内核堆栈,返回用户空间会被恢复到寄存器rax,作为返回值。
503行判断是否有额外的事情要做,像进程调用和信号处理。如果有,跳到sysret_careful。

509行将返回用户空间的执行地址放到寄存器rcx,511行将内核堆栈中保存的值恢复到寄存器。
513行将用户堆栈指针(old_rsp)放到rsp。

返回用户空间时,并没有调整内核堆栈指针到进入内核空间时的值,
这是因为进入内核空间时,内核堆栈指针取自kernel_stack,整个过程没有修改kernel_stack的值。

514行的宏USERGS_SYSRET64定义为
#define USERGS_SYSRET64
swapgs;
sysretq;

执行sysretq指令,CPU操作如下:

1.将MSR_STAR 63-48位加16的值装载到cs
2.将MSR_STAR 63-48位加8的值装载到ss
3.将rcx(返回地址)的值放到rip,将r11(flags)放到rflags
4.切换到用户空间,从返回地址开始执行

前面已经说明和syscall/sysret相关的几个寄存器,这里再列一下


MSR_STAR: 63-48位是sysret的32模式cs段选择符,加8是sysret的ss段选择符,加16是sysret的64位模式cs段选择符
47-32位是syscall的cs段选择符,加8是syscall的ss段选择符
31- 0位是32位模式syscall的eip
MSR_LSTAR: 64位模式syscall的rip
MSR_CSTAR: 兼容模式syscall的rip
MSR_SYSCALL_MASK:31-0位是一些标志掩码

【 在 four3ger (3ger) 的大作中提到: 】


: 对于amd的cpu,syscall和sysret在长模式(64位)和保护模式(32位)都有效。
: 对于intel的cpu,syscall和sysret在64位模式有效,在32位模式无效。
: 看一下64位下的系统调用是如何使用syscall和sysret的

: ...................

--
蝎子,蝎子,我爱你

[36m※ 修改:·four3ger 于 Apr 25 17:41:50 2011 修改本文·[FROM: 59.64.161.*] [m
[m [33m※ 来源:·水木社区 http://newsmth.net·[FROM: 59.64.161.*] [m

0 new messages