2013年4月30日 星期二

x86 process switch implementation (1.5) - save/restore in real mode

遲來的 1.5 篇, 我都已經辛苦的把 x86 protected mode 寫完了說。

我終於想出如何 save/restore in x86/dos real mode, 實在令我開心, 距離第一篇將近有 45 天的時間。這個程式是原創等級的程式, 在我苦思如何在 stm32f4discovery (arm cortex m4) 實作 context switch 時來的靈感。在 100 行左右的組合語言程式碼就可完成, 實在是小而美。

simple_proc.S
  1 # save/restore stack in ms dos (x86 real mode)
  2 .code16
  3 .text
  4 .global begin
  5 begin:
  6   xchg %bx, %bx #bochs magic break point
  7   cli
  8 
  9   xor     %eax, %eax
 10   mov     %cs,%ax
 11   mov     %ax,%ds
 12 
 13 ## reset 0x30 interrupt
 14   movw    $0x0, %bx
 15   movw    %bx, %es
 16   movw $switch_proc, %es:0xc0 # isr offset
 17   movw %ax, %es:0xc2 #isr seg
 18 
 19 
 20   movw    $0xb800, %ax
 21   movw    %ax, %gs
 22 
 23 ## set stack frame eip
 24   movw $proc_a, stack_frame_a+2
 25   movw $proc_b, stack_frame_b+2
 26 
 27 ## set stack frame cs
 28   movw %cs, %ax
 29   movw %ax, stack_frame_a+4
 30   movw %ax, stack_frame_b+4
 31 
 32 ## set stack frame flag
 33   # get flag
 34   pushf
 35   movw (%esp), %ax
 36   popf
 37   movw %ax, stack_frame_a+6
 38   movw %ax, stack_frame_b+6
 39 
 40   movw $stack_frame_a, a_sp
 41   movw $stack_frame_b, b_sp
 42 
 43   movw $stack_frame_a, %sp
 44 
 45   popw %ax
 46   iret
 47 
 48 .global proc_a
 49 proc_a:
 50 1:
 51   mov $0x1, %ax
 52   push %cx
 53   int $0x30
 54   jmp 1b
 55 
 56 .global proc_b
 57 proc_b:
 58 1:
 59   mov $0x2, %al
 60   push %bx
 61   int $0x30
 62   jmp 1b
 63 
 64 .global switch_proc
 65 switch_proc:
 66   pushw %ax
 67   movw cur_proc, %dx
 68 
 69 # 0 run proc_a
 70 # 1 run proc_b
 71   cmp $0, %dx
 72   jne 1f
 73   # save cur_esp to a_sp
 74   movw %esp, a_sp
 75   movw b_sp, %ax
 76   movw $1, cur_proc
 77   jmp 2f
 78 1:
 79   # save cur_esp to b_sp
 80   movw %esp, b_sp
 81   movw a_sp, %ax
 82   movw $0, cur_proc
 83 2:
 84 
 85   movw %ax, %sp
 86   popw %ax
 87   iret
 88 
 89 cur_proc:
 90   .word 0x0
 91 a_sp:
 92   .word 0x0
 93 b_sp:
 94   .word 0x0
 95 
 96   .space  256, 0
 97 proc_stack_top_a:
 98   .space  256, 0
 99 proc_stack_top_b:
100 
101   .space  256, 0
102 stack_frame_a:
103   .word 0x9# ax
104   .word 0x0# eip
105   .word 0x1# cs
106   .word 0x2# flag
107 
109   .space  256, 0
110 stack_frame_b:
111   .word 0x9# ax
112   .word 0x0# eip
113   .word 0x1# cs
114   .word 0x2# flag


當從 proc_a 到 (int $0x30) switch_proc 的 stack 變化, 這次不再是手工圖了, 有了漂亮的表格可看, 數字部份就是 stack 的位址變化。

497
495%cx
493flag
491%cs
48f%eip
48d%ax

所以再次回到 proc_a 時, 只要讓 esp 回到 0x495 即可。說的果然比做的容易多了, 那麼要怎麼實作呢?

這次捨棄固定的 stack_frame_a, stack_frame_b, 在每次進入 switch_proc 把 esp 存起來即可, 再根據 cur_proc 決定要存在 a_sp 或 b_sp, 並把 esp 指向 a_sp 或 b_sp 完成下次的 process switch。

有了這個, 這程式可以說是 context switch, 不只是 process switch 了, 已經可以把 context save/restore。

這是 proc_b 的 stack 變化過程:

59f
59d%bx
flag
%cs
597%eip
595%ax

要體驗她, 一樣需要在 bochs single step 過才能有所體會。

source code:
https://github.com/descent/process/
real_mode_stack branch

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。