切换栈操作

1. 何时需要切换栈:

  上下文切换,如:

  当需要切换进程时,需要保存正在运行的进程并切换到新的进程。

  当从一个函数切换到另一个函数时,需要保存原有的栈寄存器

2. 切换栈需要有哪些基本操作:

  1. 切换进程时,什么可以表示一个正在运行的程序?

    程序中各个寄存器的值表示程序的运行状态。

    其中ESP和EBP表示栈指针。ESP是当前运行的栈指针,EBP表示进程初始化时的栈指针。

 

 

 

 

 

 

  2. call convention: 

    in X86, 在调用子函数时,IA32 and X64 有不同的参数传递规则。参考x86 calling conventions - Wikipedia

3. 示例演示:

主要流程如下:

  switch:
    save all old process's register
    load new process's register
    load new process's ESP->完成切换栈操作

  3.1 操作系统导论:Chapter 6

  对于xv6中的进程切换代码函数定义如下:

  其中struct context是进程上下文数据,有[context ptr, esp, ebx, ecx, edx, edi, esi, ebp]

void switch (struct context **old, struct context *new);

 具体汇编代码:(使用的是IA32 的cdecl calling convention: "In cdecl, subroutine arguments are passed on the stack."即函数的参数从右往左一次存到ESP中)

xv6:
switch:
   # 4(%esp) save **old, 8(%esp) save *new # #save old context's registers: # #load old context ptr(*old) to eax movl 4(%esp), eax # save all old context's register popl 0(%eax) #eax[0](old[0]) save the old context ptr movl %esp, 4(%eax) movl %ebx, 8(%eax) movl %ecx, 12(%eax) movl %edx, 16(%eax) movl %esi, 20(%eax) movl %edi, 24(%eax) movl %ebp, 28(%eax) # #load new context's registers: # #load new context ptr movl 4(%esp), eax # load all new context's register movl 28(%eax), %ebp movl 24(%eax), %edi movl 20(%eax), %esi movl 16(%eax), %edx movl 12(%eax), %ecx movl 8(%eax), %ebx movl 4(%eax), %esp #new stack point has been set pushl 0(%eax) # return into new context ret

  3.2 UEFI FSP SwitchStack

  这里涉及到从CAR(cach as Ram)即TemporaryMemory搬到PermanentMemory的操作,

  因此这个情况下,把栈空间原封不动的搬到PermanentMemoryBase对应的地方就好,操作如下:

NewRsp = OldRsp - TemporaryMemoryBase + PermanentMemoryBase
NewRbp = OldRbp - TemporaryMemoryBase + PermanentMemoryBase //新的Rbp就是旧的Rbp的一个fixup(从旧的memorybase 往新的memorybase般)
//其它的register复制到新的栈空间

  下面分别是x64和IA32的示例:他们的calling convention 不一样(line38, line39)

  3.2.1 x64 calling convention, rcx(1st parameter), rdx, r8, r9

15 ; VOID
16 ; EFIAPI
17 ; SecSwitchStack (
18 ;   UINT32   TemporaryMemoryBase,
19 ;   UINT32   PermanentMemoryBase
20 ;   );
21 ;------------------------------------------------------------------------------
22 global ASM_PFX(SecSwitchStack)
23 ASM_PFX(SecSwitchStack):
24     ;
25     ; Save four register: rax, rbx, rcx, rdx
26     ;
27     push  rax
28     push  rbx
29     push  rcx
30     push  rdx
31 
32     ;
33     ; !!CAUTION!! this function address's is pushed into stack after
34     ; migration of whole temporary memory, so need save it to permanent
35     ; memory at first!
36     ;
37 
38     mov   rbx, rcx                 ; Save the first parameter rbx = rcx
39     mov   rcx, rdx                 ; Save the second parameter
40 
41     ;
42     ; Save this function's return address into permanent memory at first.
43     ; Then, Fixup the esp point to permanent memory
44     ;
45     mov   rax, rsp
46     sub   rax, rbx
47     add   rax, rcx
48     mov   rdx, qword [rsp]         ; copy pushed register's value to permanent memory
49     mov   qword [rax], rdx
50     mov   rdx, qword [rsp + 8]
51     mov   qword [rax + 8], rdx
52     mov   rdx, qword [rsp + 16]
53     mov   qword [rax + 16], rdx
54     mov   rdx, qword [rsp + 24]
55     mov   qword [rax + 24], rdx
56     mov   rdx, qword [rsp + 32]    ; Update this function's return address into permanent memory
57     mov   qword [rax + 32], rdx
58     mov   rsp, rax                 ; From now, rsp is pointed to permanent memory
59 
60     ;
61     ; Fixup the rbp point to permanent memory
62     ;
63     mov   rax, rbp
64     sub   rax, rbx
65     add   rax, rcx
66     mov   rbp, rax                 ; From now, rbp is pointed to permanent memory
67 
68     pop   rdx
69     pop   rcx
70     pop   rbx
71     pop   rax
72     ret

  3.2.2 IA32 calling convention

 

15 ; VOID
16 ; EFIAPI
17 ; SecSwitchStack (
18 ;   UINT32   TemporaryMemoryBase,
19 ;   UINT32   PermanentMemoryBase
20 ;   );
21 ;------------------------------------------------------------------------------
22 global ASM_PFX(SecSwitchStack)
23 ASM_PFX(SecSwitchStack):
24     ;
25     ; Save four register: eax, ebx, ecx, edx
26     ;
27     push  eax
28     push  ebx
29     push  ecx
30     push  edx
31 
32     ;
33     ; !!CAUTION!! this function address's is pushed into stack after
34     ; migration of whole temporary memory, so need save it to permanent
35     ; memory at first!
36     ;
37 
38     mov   ebx, [esp + 20]          ; Save the first parameter
39     mov   ecx, [esp + 24]          ; Save the second parameter
40 
41     ;
42     ; Save this function's return address into permanent memory at first.
43     ; Then, Fixup the esp point to permanent memory
44     ;
45     mov   eax, esp
46     sub   eax, ebx
47     add   eax, ecx
48     mov   edx, dword [esp]         ; copy pushed register's value to permanent memory
49     mov   dword [eax], edx
50     mov   edx, dword [esp + 4]
51     mov   dword [eax + 4], edx
52     mov   edx, dword [esp + 8]
53     mov   dword [eax + 8], edx
54     mov   edx, dword [esp + 12]
55     mov   dword [eax + 12], edx
56     mov   edx, dword [esp + 16]    ; Update this function's return address into permanent memory
57     mov   dword [eax + 16], edx
58     mov   esp, eax                 ; From now, esp is pointed to permanent memory
59 
60     ;
61     ; Fixup the ebp point to permanent memory
62     ;
63     mov   eax, ebp
64     sub   eax, ebx
65     add   eax, ecx
66     mov   ebp, eax                 ; From now, ebp is pointed to permanent memory
67 
68     pop   edx
69     pop   ecx
70     pop   ebx
71     pop   eax
72     ret
73 

 

posted @ 2024-04-07 11:27  nipper  阅读(18)  评论(0编辑  收藏  举报