スタート低レイヤー5 #start_printf

@takarakasai
今回やるところ
対象はNetBSD 6.0.1 だよ!

←ここやります!
前回のおさらい
@kusabanachiさん http://www.slideshare.net/kusabanachi/net-bsd-i386idt
前回のおさらい
@kusabanachiさん http://www.slideshare.net/kusabanachi/net-bsd-i386idt

IDTを設定すればかつる!
↓
IDTR設定してるとこ探そう!
おさらい


sys/arch/i386/i386/locore.S

255~

start: movw $0x1234,0x472 # warm boot
…
call _C_LABEL(init386) # wire 386 chip ...
今回やるところ その1(Xsyscall)
Xsyscall


sys/arch/i386/i386/locore.S

255~

start: movw $0x1234,0x472 # warm boot
…
call _C_LABEL(init386) # wire 386 c...
Xsyscall


grepしてもXsyscall がヒットしないので
オブジェクトから該当するシンボルを探します.

~/netbsd/src$ find ./ | grep ¥¥.o$ | xargs -n1 -i nm {} -A |...
Xsyscall P_MD_SYSCALL(%edx) ?


sys/arch/i386/include/asm.h

#define CPUVAR(off) %fs:__CONCAT(CPU_INFO_,off)



sys/arch...
Xsyscall おさらい


セグメントセレクタとDT

セグメント・セレクタ(今回のケースではFS)で指定するのは
ディスクリプタ・テーブル(GDTやLDT)のインデックス。
これとオフセットからリニアアドレスが算出される。
Xsyscall


((struct cpuinfo*)%fs)->ci_curlwp->l_proc->p_md.md_syscall()

Any machine-dependent fields
parent process
Curr...
FS register
FSに値を格納しているところを探します


sys/arch/i386/i386/locore.S 1136~

IDTVEC(syscall)
// Xsyscall
pushl $2 # size of instr...
FS register
FSに値を格納しているところを探します


sys/arch/i386/i386/locore.S 1136~

IDTVEC(syscall)
// Xsyscall
pushl $2 # size of instr...
FS register
FSに値を格納しているところを探します
INTRENTRYの中身を追っています


sys/arch/i386/include/frameasm.h 67~

/*
* These are used on interr...
FS register
FSに値を格納しているところを探します
INTRENTRYの中身を追っています
FSに格納される値を確認しています


sys/arch/i386/include/frameasm.h

movl $GSEL(GCPU...
Initialization of GDT
gdtのGCPU_SELが指す先を探します


sys/arch/i386/i386/machdep.c 1063

union descriptor *gdt, *ldt;

ありました, これは...
Initialization of GDT
gdtのGCPU_SELが指す先を探します


sys/arch/i386/i386/machdep.c 1063

union descriptor *gdt, *ldt;

ありました, これは...
Initialization of GDT
call _C_LABEL(initgdt) 周辺を見てみます


sys/arch/i386/i386/locore.S 686~

begin:
…
/* Set up bootstrap st...
Initialization of GDT
call _C_LABEL(initgdt) 周辺を見てみます


sys/arch/i386/i386/locore.S 686~

begin:
…
/* Set up bootstrap st...
Initialization of GDT
initgdt 前に esi が設定されている箇所を見ています


sys/arch/i386/i386/locore.S 523~

2:
/* Find end of kernel image....
Initialization of GDT
initgdt 前に esi が設定されている箇所を見ています


sys/arch/i386/i386/locore.S 523~

2:
/* Find end of kernel image....
Initialization of GDT
今度はinitgdtの中を見ます


sys/arch/i386/i386/locore.S 686~

void initgdt(union descriptor *tgdt) {
struct ...
Initialization of GDT
gdtのGCPU_SELが指す先を探します


sys/arch/i386/i386/machdep.c 1063

union descriptor *gdt, *ldt;

ありました, これは...
Initialization of GDT
まず init386() を見ます


sys/arch/i386/i386/machdep.c 1312~

vaddr_t idt_vaddr;
paddr_t idt_paddr;
…
voi...
Initialization of GDT
gdt_init() を追います


sys/arch/i386/i386/gdt.c 110~

void gdt_init(void){
…
/* Do machine-dependent in...
Initialization of GDT
gdt_init() を追います


sys/arch/i386/i386/gdt.c 109~

void gdt_init(void) {
…
struct cpu_info *ci = &cp...
Initialization of GDT
さらに gdt_init_cpu() を追います


sys/arch/i386/i386/gdt.c 187~

void
gdt_init_cpu(struct cpu_info *ci)
{
...
Initialization of GDT
gdtのGCPU_SELが指す先を探します


sys/arch/i386/i386/machdep.c 1063

union descriptor *gdt, *ldt;

ありました, これは...
Initialization of GDT
gdt_init() を追います


sys/arch/i386/i386/gdt.c 109~

void gdt_init(void) {
…
struct cpu_info *ci = &cp...
cpu_info_primary からmd_syscall()をたどる


ここまででわかったこと

((struct cpuinfo*)%fs)->ci_curlwp->l_proc->p_md.md_syscall()
(struct c...
cpu_info_primary からmd_syscall()をたどる
cpu_info_primaryを探します


sys/arch/x86/x86/cpu.c 151~

struct cpu_info cpu_info_primary...
cpu_info_primary からmd_syscall()をたどる
proc0内のp_md.md_syscallの設定値を探します
netbsd/src > grep -rn p_md¥¥.md_syscall sys
…
sys/arch...
cpu_info_primary からmd_syscall()をたどる
proc0内のp_md.md_syscallの設定値を探します


sys/kern/init_main.c 401 ~

void main(void) {
…
/* ...
今回やるところ その1(Xsyscall)
syscallの呼び出し


sys/arch/i386/include/frameasm.h 67~

/*
* These are used on interrupt or trap entry or exit.
*/
#define I...
syscallの引数(writeの場合)


sys/arch/i386/include/frame.h 76~

/*
* Exception/Trap Stack Frame
*/
struct trapframe {
uint16_t ...
syscall
※ write システムコールでは frame.eax == 4


sys/arch/x86/x86/syscall.c 113~

static void syscall(struct trapframe *frame) ...
syscall -> sysent


sys/kern/kern_exec.c 186 ~

/* NetBSD emul struct */
struct emul emul_netbsd = {
…
.e_sysent = sysent...
syscall  sy_call


sys/arch/x86/x86/syscall.c 113~

error = sy_call(callp, l, args, rval);



// 該当する関数を呼び出し

sys/sys/s...
今回やるところ その1(Xsyscall)
sys_write


sys/kern/sys_generic.c 301~

int
sys_write(struct lwp *l, const struct sys_write_args *uap, register_t *retva...
Upcoming SlideShare
Loading in …5
×

start_printf_5_Xsyscall_syscall_sys_write

848 views

Published on

This Presentation for start_printf#5 at 2014/02/23.
It describes some kernel walk throw guide of Xsyscall/syscall/sys_write source code of NetBSD6.0.1.
https://github.com/start-printf/wiki/wiki.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
848
On SlideShare
0
From Embeds
0
Number of Embeds
41
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

start_printf_5_Xsyscall_syscall_sys_write

  1. 1. スタート低レイヤー5 #start_printf @takarakasai
  2. 2. 今回やるところ 対象はNetBSD 6.0.1 だよ! ←ここやります!
  3. 3. 前回のおさらい @kusabanachiさん http://www.slideshare.net/kusabanachi/net-bsd-i386idt
  4. 4. 前回のおさらい @kusabanachiさん http://www.slideshare.net/kusabanachi/net-bsd-i386idt IDTを設定すればかつる! ↓ IDTR設定してるとこ探そう!
  5. 5. おさらい  sys/arch/i386/i386/locore.S 255~ start: movw $0x1234,0x472 # warm boot … call _C_LABEL(init386) # wire 386 chip for unix operation … call _C_LABEL(main)  sys/arch/i386/i386/machdep.c 1312~ vaddr_t idt_vaddr; paddr_t idt_paddr; main() に入る前に、idt をメモリに配置&初期化 … void init386(paddr_t first_avail) { … pmap_kenter_pa(idt_vaddr, idt_paddr, VM_PROT_READ|VM_PROT_WRITE, 0); pmap_update(pmap_kernel()); memset((void *)idt_vaddr, 0, PAGE_SIZE); … idt = (struct gate_descriptor *)idt_vaddr; … /* new-style interrupt gate for syscalls */ idt_vec_reserve(128); <<<<<<<<<<< 128 = 0x80 !!!! setgate(&idt[128], &IDTVEC(syscall), 0, SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); … cpu_init_idt(); …
  6. 6. 今回やるところ その1(Xsyscall)
  7. 7. Xsyscall  sys/arch/i386/i386/locore.S 255~ start: movw $0x1234,0x472 # warm boot … call _C_LABEL(init386) # wire 386 chip for unix operation … call _C_LABEL(main)  sys/arch/i386/i386/machdep.c 1312~ vaddr_t idt_vaddr; paddr_t idt_paddr; main() に入る前に、idt をメモリに配置&初期化 … void init386(paddr_t first_avail) { … pmap_kenter_pa(idt_vaddr, idt_paddr, VM_PROT_READ|VM_PROT_WRITE, 0); pmap_update(pmap_kernel()); memset((void *)idt_vaddr, 0, PAGE_SIZE); … idt = (struct gate_descriptor *)idt_vaddr; … /* new-style interrupt gate for syscalls */ idt_vec_reserve(128); <<<<<<<<<<< 128 = 0x80 !!!! setgate(&idt[128], &IDTVEC(syscall), 0, SDT_SYS386IGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); このマクロ展開するとXsyscall … cpu_init_idt(); IDT経由で飛んで行っているはず …
  8. 8. Xsyscall  grepしてもXsyscall がヒットしないので オブジェクトから該当するシンボルを探します. ~/netbsd/src$ find ./ | grep ¥¥.o$ | xargs -n1 -i nm {} -A | grep T¥ Xsyscall ./sys/arch/i386/compile/GENERIC/locore.o:00000500 T Xsyscall  sys/arch/i386/i386/locore.S 1136~ IDTVEC(syscall) // Xsyscall pushl $2 # size of instruction for restart syscall1: pushl $T_ASTFLT # trap # for doing ASTs INTRENTRY STI(%eax) // sti 命令, 割り込み enable ... addl $1,CPUVAR(NSYSCALL) # count it atomically adcl $0,CPUVAR(NSYSCALL)+4 # count it atomically movl CPUVAR(CURLWP),%edi movl L_PROC(%edi),%edx movl %esp,L_MD_REGS(%edi) # save pointer to frame pushl %esp call *P_MD_SYSCALL(%edx) # get pointer to syscall() function <<< syscall?? addl $4,%esp .Lsyscall_checkast: /* Check for ASTs on exit to user mode. */ CLI(%eax) // cli 命令, 割り込み disable …
  9. 9. Xsyscall P_MD_SYSCALL(%edx) ?  sys/arch/i386/include/asm.h #define CPUVAR(off) %fs:__CONCAT(CPU_INFO_,off)  sys/arch/i386/i386/genassym.cf:290 define  offsetof(struct cpu_info, ci_curlwp) sys/arch/i386/i386/genassym.cf:180 define  CPU_INFO_CURLWP L_PROC offsetof(struct lwp, l_proc) sys/arch/i386/i386/genassym.cf:191 define P_MD_SYSCALL offsetof(struct proc, p_md.md_syscall) movl CPUVAR(CURLWP),%edi movl L_PROC(%edi),%edx … call *P_MD_SYSCALL(%edx) # get pointer to syscall() function <<< syscall?? … ((struct cpuinfo*)%fs)->ci_curlwp->l_proc->p_md.md_syscall() FSって何ぞ?
  10. 10. Xsyscall おさらい  セグメントセレクタとDT セグメント・セレクタ(今回のケースではFS)で指定するのは ディスクリプタ・テーブル(GDTやLDT)のインデックス。 これとオフセットからリニアアドレスが算出される。
  11. 11. Xsyscall  ((struct cpuinfo*)%fs)->ci_curlwp->l_proc->p_md.md_syscall() Any machine-dependent fields parent process Current owner of the processor セグメントセレクタ(FS)の指すベースアドレスにcpuinfo構造体が存在し、 そのメンバから、関数ポインタ変数md_syscall()をたどっています。 このcpu_info構造体には何が入っているのでしょうか?
  12. 12. FS register FSに値を格納しているところを探します  sys/arch/i386/i386/locore.S 1136~ IDTVEC(syscall) // Xsyscall pushl $2 # size of instruction for restart syscall1: pushl $T_ASTFLT # trap # for doing ASTs INTRENTRY STI(%eax) // sti 命令, 割り込み enable ... addl $1,CPUVAR(NSYSCALL) # count it atomically adcl $0,CPUVAR(NSYSCALL)+4 # count it atomically movl CPUVAR(CURLWP),%edi movl L_PROC(%edi),%edx movl %esp,L_MD_REGS(%edi) # save pointer to frame pushl %esp call *P_MD_SYSCALL(%edx) # get pointer to syscall() function <<< syscall?? addl $4,%esp .Lsyscall_checkast: /* Check for ASTs on exit to user mode. */ CLI(%eax) // cli 命令, 割り込み disable …
  13. 13. FS register FSに値を格納しているところを探します  sys/arch/i386/i386/locore.S 1136~ IDTVEC(syscall) // Xsyscall pushl $2 # size of instruction for restart syscall1: pushl $T_ASTFLT # trap # for doing ASTs INTRENTRY <<<<<< STI(%eax) // sti 命令, 割り込み enable ... addl $1,CPUVAR(NSYSCALL) # count it atomically adcl $0,CPUVAR(NSYSCALL)+4 # count it atomically movl CPUVAR(CURLWP),%edi movl L_PROC(%edi),%edx movl %esp,L_MD_REGS(%edi) # save pointer to frame pushl %esp call *P_MD_SYSCALL(%edx) # get pointer to syscall() function <<< syscall?? addl $4,%esp .Lsyscall_checkast: /* Check for ASTs on exit to user mode. */ CLI(%eax) // cli 命令, 割り込み disable … INTRENTRYが怪しいですね
  14. 14. FS register FSに値を格納しているところを探します INTRENTRYの中身を追っています  sys/arch/i386/include/frameasm.h 67~ /* * These are used on interrupt or trap entry or exit. */ #define INTRENTRY ¥ subl $TF_PUSHSIZE,%esp ; ¥ offsetof(struct trapframe, tf_trapno) "sys/arch/i386/i386/genassym.cf" movw %gs,TF_GS(%esp) ; ¥ offsetof(struct trapframe, tf_gs) movw %fs,TF_FS(%esp) ; ¥ 以下略 movl %eax,TF_EAX(%esp) ; ¥ movw %es,TF_ES(%esp) ; ¥ movw %ds,TF_DS(%esp) ; ¥ … movl $GSEL(GCPU_SEL, SEL_KPL),%eax; ¥ movl %ecx,TF_ECX(%esp) ; ¥ movl %eax,%fs ; ¥ cld ; ¥ TLOG 各種レジスタの値をスタックに退避するマクロ関数?のようです。 FSにも値を設定しています。
  15. 15. FS register FSに値を格納しているところを探します INTRENTRYの中身を追っています FSに格納される値を確認しています  sys/arch/i386/include/frameasm.h movl $GSEL(GCPU_SEL, SEL_KPL),%eax; ¥  sys/arch/i386/include/segments.h #define #define #define GSEL(s,r) (((s) << 3) | r) /* a global selector */ GCPU_SEL 6 /* per-CPU segment */ SEL_KPL 0 /* kernel privilege level */ これは前回にも出てきましたね FS(セグメント・レジスタ)に設定しているのは ・GDT(グローバル・ディスクリプタ・テーブル) ・インデックス6 ・カーネル権限レベル であることがわかります。
  16. 16. Initialization of GDT gdtのGCPU_SELが指す先を探します  sys/arch/i386/i386/machdep.c 1063 union descriptor *gdt, *ldt; ありました, これはどこで初期化されているのでしょうか?  sys/arch/i386/i386/locore.S 723 From start (※initgdt()はinit386()の前に呼ばれます) call _C_LABEL(initgdt)  sys/arch/i386/i386/machdep.c 489 From cpu_startup() from main() gdt_init(); ここが怪しそうです。両者を追ってみます。
  17. 17. Initialization of GDT gdtのGCPU_SELが指す先を探します  sys/arch/i386/i386/machdep.c 1063 union descriptor *gdt, *ldt; ありました, これはどこで初期化されているのでしょうか?  sys/arch/i386/i386/locore.S 723 From start (※initgdt()はinit386()の前に呼ばれます) call _C_LABEL(initgdt)  sys/arch/i386/i386/machdep.c 489 From cpu_startup() from main() gdt_init(); まずは initgdt を追いかけます
  18. 18. Initialization of GDT call _C_LABEL(initgdt) 周辺を見てみます  sys/arch/i386/i386/locore.S 686~ begin: … /* Set up bootstrap stack. */ leal (PROC0_STK_OFF+KERNBASE)(%esi),%eax movl %eax,_C_LABEL(lwp0uarea) leal (KSTACK_SIZE-FRAMESIZE)(%eax),%esp … subl $NGDT*8, %esp # space for temporary gdt // 0x00001000UL + 0xc0000000 + %esi // Frame Size = 引数 // temporary???? pushl %esp call _C_LABEL(initgdt) … call _C_LABEL(init386) # wire 386 chip for unix operation // 前回でてきたinit386 PROC0_STK_OFF+KERNBASE = (PROC0_PDIR_OFF + PDP_SIZE * PAGE_SIZE) + 0xc0000000UL = 0 + 1* 4096 (!PAE) + 0xc0000000UL = 0xc0001000UL
  19. 19. Initialization of GDT call _C_LABEL(initgdt) 周辺を見てみます  sys/arch/i386/i386/locore.S 686~ begin: … /* Set up bootstrap stack. */ leal (PROC0_STK_OFF+KERNBASE)(%esi),%eax movl %eax,_C_LABEL(lwp0uarea) leal (KSTACK_SIZE-FRAMESIZE)(%eax),%esp … subl $NGDT*8, %esp # space for temporary gdt // 0x00001000UL + 0xc0000000 + %esi // Frame Size = 引数 // temporary???? pushl %esp call _C_LABEL(initgdt) … 0xC0000000 call _C_LABEL(init386) # wire 386 chip for unix operation // 前回でてきたinit386 esi + KERNBASE esi PROC0_STK_OFF (4096) PROC0_STK_OFF+KERNBASE KSTACK_SIZE = (PROC0_PDIR_OFF + PDP_SIZE * PAGE_SIZE) + 0xc0000000UL = 0 + 1* 4096 (!PAE) + 0xc0000000UL esp FRAME_SIZE = 0xc0001000UL 0xFFFFFFFF この時のメモリマップはこんな感じになるはず。 esiにはカーネルベースアドレスからの何らかのオフセット値が入っているはず!
  20. 20. Initialization of GDT initgdt 前に esi が設定されている箇所を見ています  sys/arch/i386/i386/locore.S 523~ 2: /* Find end of kernel image. */ movl $RELOC(end),%edi … /* Skip over any modules/blobs. */ movl RELOC(eblob),%eax testl %eax,%eax jz 1f subl $KERNBASE,%eax movl %eax,%edi 1: /* Compute sizes */ movl %edi,%esi addl $PGOFSET,%esi # page align up andl $~PGOFSET,%esi  // ((eblob) – KERNBASE) eblob にはブートローダから受け // とったパラメータが入っているアドレスの末尾が入る // パラメータ分スキップする // (%esi + 0x00000FFF) & 0xFFFFF000 // 切り上げ sys/arch/i386/i386/locore.S 242~ RELOC(x)  _RELOC(_C_LABEL(x))  (( x )– KERNBASE ) // カーネルベースアドレス相対位置
  21. 21. Initialization of GDT initgdt 前に esi が設定されている箇所を見ています  sys/arch/i386/i386/locore.S 523~ 2: /* Find end of kernel image. */ movl $RELOC(end),%edi … /* Skip over any modules/blobs. */ movl RELOC(eblob),%eax testl %eax,%eax jz 1f subl $KERNBASE,%eax movl %eax,%edi 1: /* Compute sizes */ movl %edi,%esi addl $PGOFSET,%esi # page align up andl $~PGOFSET,%esi // ((eblob) – KERNBASE) eblob にはブートローダから受け // とったパラメータが入っているアドレスの末尾が入る // パラメータ分スキップする 0xC0000000 &eblob esi (アライメント済み) // (%esi + 0x00000FFF) +& 0xFFFFF000 // 切り上げ esi KERNBASE PROC0_STK_OFF (4096) KSTACK_SIZE  sys/arch/i386/i386/locore.S 242~ esp FRAME_SIZE RELOC(x)  _RELOC(_C_LABEL(x))  (( x )– KERNBASE ) //0xFFFFFFFF カーネルベースアドレス相対位置
  22. 22. Initialization of GDT 今度はinitgdtの中を見ます  sys/arch/i386/i386/locore.S 686~ void initgdt(union descriptor *tgdt) { struct region_descriptor region; gdt = tgdt; memset(gdt, 0, NGDT*sizeof(*gdt)); // gdt領域の初期化(前述のtemporary領域) /* make gdt gates and memory segments */ setsegment(&gdt[GCODE_SEL].sd, 0, 0xfffff, SDT_MEMERA, SEL_KPL, 1, 1); setsegment(&gdt[GDATA_SEL].sd, 0, 0xfffff, SDT_MEMRWA, SEL_KPL, 1, 1); setsegment(&gdt[GUCODE_SEL].sd, 0, x86_btop(I386_MAX_EXE_ADDR) - 1, SDT_MEMERA, SEL_UPL, 1, 1); setsegment(&gdt[GUCODEBIG_SEL].sd, 0, 0xfffff, SDT_MEMERA, SEL_UPL, 1, 1); setsegment(&gdt[GUDATA_SEL].sd, 0, 0xfffff, SDT_MEMRWA, SEL_UPL, 1, 1); setsegment(&gdt[GCPU_SEL].sd, &cpu_info_primary, 0xfffff, // gdtのGCPU_SEL部分にcpu_infoを登録 SDT_MEMRWA, SEL_KPL, 1, 1); setregion(&region, gdt, NGDT * sizeof(gdt[0]) - 1); lgdt(&region); // lgdt命令を実行し、GDTRに登録 } GDT用テンポラリ領域を初期化し、 値を設定してlgdt命令を呼び出しているのがわかります
  23. 23. Initialization of GDT gdtのGCPU_SELが指す先を探します  sys/arch/i386/i386/machdep.c 1063 union descriptor *gdt, *ldt; ありました, これはどこで初期化されているのでしょうか?  sys/arch/i386/i386/locore.S 723 From start (※initgdt()はinit386()の前に呼ばれます) call _C_LABEL(initgdt)  sys/arch/i386/i386/machdep.c 489 From cpu_startup() from main() gdt_init(); こんどは gdt_init() を追いかけます
  24. 24. Initialization of GDT まず init386() を見ます  sys/arch/i386/i386/machdep.c 1312~ vaddr_t idt_vaddr; paddr_t idt_paddr; … void init386(paddr_t first_avail) { … pmap_kenter_pa(idt_vaddr, idt_paddr, VM_PROT_READ|VM_PROT_WRITE, 0); pmap_update(pmap_kernel()); memset((void *)idt_vaddr, 0, PAGE_SIZE); idt_init(); idt = (struct gate_descriptor *)idt_vaddr; pmap_kenter_pa(pentium_idt_vaddr, idt_paddr, VM_PROT_READ, 0); pmap_update(pmap_kernel()); pentium_idt = (union descriptor *)pentium_idt_vaddr; tgdt = gdt; gdt = (union descriptor *) ((char *)idt + NIDT * sizeof (struct gate_descriptor)); ldt = gdt + NGDT; memcpy(gdt, tgdt, NGDT*sizeof(*gdt)); … 前回見たコード pmapを利用して IDTを再配置不可で メモリ領域に配置。 配置後に書き込み不可 にしている。 // gdt に idt 領域に続く仮想アドレスを設定 // 前述のテンポラリ領域から設定値をコピー GDT用テンポラリ領域から別の領域(idt領域の後ろ)にコピーしているのがわかります
  25. 25. Initialization of GDT gdt_init() を追います  sys/arch/i386/i386/gdt.c 110~ void gdt_init(void){ … /* Do machine-dependent initialization. */ cpu_startup(); … }  sys/arch/i386/i386/machdep.c 422~ void cpu_startup(void) { … gdt_init(); i386_proc0_tss_ldt_init(); <<< GDT(Global Descriptor Table)の初期化 cpu_init_tss(&cpu_info_primary); ltr(cpu_info_primary.ci_tss_sel); x86_startup(); … } main()から呼ばれるcpu_startup()で gdt_init() が呼ばれているのがわかります
  26. 26. Initialization of GDT gdt_init() を追います  sys/arch/i386/i386/gdt.c 109~ void gdt_init(void) { … struct cpu_info *ci = &cpu_info_primary; … old_gdt = gdt; gdt = (union descriptor *)uvm_km_alloc(kernel_map, max_len, 0, UVM_KMF_VAONLY); for (va = (vaddr_t)gdt; va < (vaddr_t)gdt + min_len; va += PAGE_SIZE) { pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO); if (pg == NULL) { panic("gdt_init: no pages"); } pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), VM_PROT_READ | VM_PROT_WRITE, 0); } pmap_update(pmap_kernel()); memcpy(gdt, old_gdt, NGDT * sizeof(gdt[0])); ci->ci_gdt = gdt; setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, 0xfffff, SDT_MEMRWA, SEL_KPL, 1, 1); // PAGE単位 // read&write でマッピング // 遅延されてるマッピングを更新 // 新規確保領域にコピー // 新たにインデックスを追加 gdt_init_cpu(ci); } ようやくpmapで獲得した(再配置不可な)領域にGDTが設定されました
  27. 27. Initialization of GDT さらに gdt_init_cpu() を追います  sys/arch/i386/i386/gdt.c 187~ void gdt_init_cpu(struct cpu_info *ci) { #ifndef XEN struct region_descriptor region; size_t max_len; max_len = MAXGDTSIZ * sizeof(gdt[0]); setregion(&region, ci->ci_gdt, max_len - 1); lgdt(&region); } lgdt()が呼ばれてGDTRが更新されていることがわかります。
  28. 28. Initialization of GDT gdtのGCPU_SELが指す先を探します  sys/arch/i386/i386/machdep.c 1063 union descriptor *gdt, *ldt; ありました, これはどこで初期化されているのでしょうか?  sys/arch/i386/i386/locore.S 723 From start (※initgdt()はinit386()の前に呼ばれます) call _C_LABEL(initgdt)  sys/arch/i386/i386/machdep.c 489 From cpu_startup() from main() gdt_init(); gdtの初期化をみました。 あらためて、初期価値がどうなっているかを見ていきます。
  29. 29. Initialization of GDT gdt_init() を追います  sys/arch/i386/i386/gdt.c 109~ void gdt_init(void) { … struct cpu_info *ci = &cpu_info_primary; … old_gdt = gdt; gdt = (union descriptor *)uvm_km_alloc(kernel_map, max_len, 0, UVM_KMF_VAONLY); for (va = (vaddr_t)gdt; va < (vaddr_t)gdt + min_len; va += PAGE_SIZE) { pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO); if (pg == NULL) { panic("gdt_init: no pages"); } pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), VM_PROT_READ | VM_PROT_WRITE, 0); } pmap_update(pmap_kernel()); memcpy(gdt, old_gdt, NGDT * sizeof(gdt[0])); ci->ci_gdt = gdt; setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, 0xfffff, SDT_MEMRWA, SEL_KPL, 1, 1); gdt_init_cpu(ci); } cpu_info_primaryが実体のようです。 // PAGE単位 // read&write でマッピング // 遅延されてるマッピングを更新 // 新規確保領域にコピー
  30. 30. cpu_info_primary からmd_syscall()をたどる  ここまででわかったこと ((struct cpuinfo*)%fs)->ci_curlwp->l_proc->p_md.md_syscall() (struct cpuinfo*) %fs == &cpu_info_primary cpu_info_primary.ci_curlwp->l_proc->p_md.md_syscall()
  31. 31. cpu_info_primary からmd_syscall()をたどる cpu_info_primaryを探します  sys/arch/x86/x86/cpu.c 151~ struct cpu_info cpu_info_primary __aligned(CACHE_LINE_SIZE) = { … .ci_curlwp = &lwp0, <<< … };  sys/kern/kern_lwp.c 269~ struct lwp lwp0 __aligned(MIN_LWP_ALIGNMENT) = { … .l_proc = &proc0, … };  sys/kern/kern_proc.c 185~ struct proc proc0 = { … .p_emul = &emul_netbsd, … }; p_md以下は静的には設定されていないようです。 次はp_mdを設定している箇所を探します。
  32. 32. cpu_info_primary からmd_syscall()をたどる proc0内のp_md.md_syscallの設定値を探します netbsd/src > grep -rn p_md¥¥.md_syscall sys … sys/arch/x86/x86/syscall.c:105: p->p_md.md_syscall = syscall; …  sys/arch/x86/x86/syscall.c 101 ~ void syscall_intern(struct proc *p) { p->p_md.md_syscall = syscall; }  sys/kern/kern_exec.c 186 ~ /* NetBSD emul struct */ struct emul emul_netbsd = { … .e_sysent = sysent, … .e_syscall_intern = syscall_intern, … } syscall_intern(つまりemul_netbsdのメンバのe_syscall_intern) が呼ばれると設定されるようです。
  33. 33. cpu_info_primary からmd_syscall()をたどる proc0内のp_md.md_syscallの設定値を探します  sys/kern/init_main.c 401 ~ void main(void) { … /* Create process 0. */ proc0_init(); lwp0_init(); … }  sys/kern/init_main.c 401 ~ void proc0_init(void) { … #ifdef __HAVE_SYSCALL_INTERN (*p->p_emul->e_syscall_intern)(p); #endif … } main()から呼ばれるproco-init()でsyscall関数設定されることがわかりました
  34. 34. 今回やるところ その1(Xsyscall)
  35. 35. syscallの呼び出し  sys/arch/i386/include/frameasm.h 67~ /* * These are used on interrupt or trap entry or exit. */ #define INTRENTRY ¥ subl $TF_PUSHSIZE,%esp ; ¥ offsetof(struct trapframe, tf_trapno) "sys/arch/i386/i386/genassym.cf" …  sys/arch/i386/i386/locore.S 67~ IDTVEC(syscall) … pushl %sp call *P_MD_SYSCALL(%edx) # get pointer to syscall() function … ・INTRENTRYでレジスタをスタックに格納し、引数trapframeを作成して syscall()関数を呼び出しています。
  36. 36. syscallの引数(writeの場合)  sys/arch/i386/include/frame.h 76~ /* * Exception/Trap Stack Frame */ struct trapframe { uint16_t tf_gs; uint16_t tf_gs_pad; uint16_t tf_fs; uint16_t tf_fs_pad; uint16_t tf_es; uint16_t tf_es_pad; uint16_t tf_ds; uint16_t tf_ds_pad; int tf_edi; int tf_esi; int tf_ebp; int tf_ebx; int tf_edx; int tf_ecx; int tf_eax; == 4 int tf_trapno; /* below portion defined in 386 hardware */ int tf_err; int tf_eip; int tf_cs; int tf_eflags; /* below used when transitting rings (e.g. user to kernel) */ int tf_esp; int tf_ss; /* below used when switching out of VM86 mode */ int tf_vm86_es; int tf_vm86_ds; int tf_vm86_fs; int tf_vm86_gs; };
  37. 37. syscall ※ write システムコールでは frame.eax == 4  sys/arch/x86/x86/syscall.c 113~ static void syscall(struct trapframe *frame) { … register_t args[2 + SYS_MAXSYSARGS]; … code = X86_TF_RAX(frame) & (SYS_NSYSENT - 1); callp = p->p_emul->e_sysent + code; … f (callp->sy_argsize) { error = x86_copyargs((char *)frame->tf_esp + sizeof(int), args, callp->sy_argsize); if (__predict_false(error != 0)) goto bad; } if (!__predict_false(p->p_trace_enabled) || __predict_false(callp->sy_flags & SYCALL_INDIRECT) || (error = trace_enter(code, args, callp->sy_narg)) == 0) { rval[0] = 0; rval[1] = 0; error = sy_call(callp, l, args, rval); } … if (__predict_true(error == 0)) { X86_TF_RAX(frame) = rval[0]; X86_TF_RDX(frame) = rval[1]; X86_TF_RFLAGS(frame) &= ~PSL_C; /* carry bit */ } else { /* エラー処理 */ } … } // trapframeに保存されたeaxの値のうち有効ビットを取得 // ∵ sys/sys/syscall.h:1350:#define SYS_NSYSENT 512 // trapframeに格納された引数をargsに指定個数分コピーする // 該当する関数を呼び出し // 返り値処理 e_sysent + 4 の位置の関数をcallp に設定し, sy_call(callp, *, *)を呼び出しています
  38. 38. syscall -> sysent  sys/kern/kern_exec.c 186 ~ /* NetBSD emul struct */ struct emul emul_netbsd = { … .e_sysent = sysent, … .e_syscall_intern = syscall_intern, … }  sys/kern/init_sysent.c 106~ struct sysent sysent[] = { { ns(struct sys_syscall_args), SYCALL_INDIRECT, (sy_call_t *)sys_syscall }, /* 0 = syscall */ { ns(struct sys_exit_args), 0, (sy_call_t *)sys_exit }, /* 1 = exit */ { 0, 0, 0, (sy_call_t *)sys_fork }, /* 2 = fork */ { ns(struct sys_read_args), 0, (sy_call_t *)sys_read }, /* 3 = read */ { ns(struct sys_write_args), 0, (sy_call_t *)sys_write }, /* 4 = write */ … } e_sysent + 4 の位置は sys_write に対応しています
  39. 39. syscall  sy_call  sys/arch/x86/x86/syscall.c 113~ error = sy_call(callp, l, args, rval);  // 該当する関数を呼び出し sys/sys/syscallvar.h 54~ static inline int sy_call(const struct sysent *sy, struct lwp *l, const void *uap, register_t *rval) { int error; l->l_sysent = sy; error = (*sy->sy_call)(l, uap, rval); l->l_sysent = NULL; return error; } 前述のcallp が 第1引数(sy) なので sys_write を呼び出していることがわかります。
  40. 40. 今回やるところ その1(Xsyscall)
  41. 41. sys_write  sys/kern/sys_generic.c 301~ int sys_write(struct lwp *l, const struct sys_write_args *uap, register_t *retval) { /* { syscallarg(int) fd; syscallarg(const void *) buf; syscallarg(size_t) nbyte; } */ file_t *fp; int fd; fd = SCARG(uap, fd); // 第1引数 if ((fp = fd_getfile(fd)) == NULL) // 第1引数からfile構造体へのポインタを返す, fileリファレンスカウンタをカウントアップする. return (EBADF); if ((fp->f_flag & FWRITE) == 0) { fd_putfile(fd); return (EBADF); } // 書き出し可能なファイルかチェック // fileリファレンスカウンタをカウントダウンする. /* dofilewrite() will unuse the descriptor for us */ return (dofilewrite(fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), // fileリファレンスカウンタをカウントダウン &fp->f_offset, FOF_UPDATE_OFFSET, retval)); } ファイルディスクリプタ(第1引数)からファイル構造体を取得し、 dofilewrite()を呼び出していることがわかります

×