SlideShare a Scribd company logo
1 of 41
Download to read offline
スタート低レイヤー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 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();
…
今回やるところ その1(Xsyscall)
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経由で飛んで行っているはず
…
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
…
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って何ぞ?
Xsyscall おさらい


セグメントセレクタとDT

セグメント・セレクタ(今回のケースではFS)で指定するのは
ディスクリプタ・テーブル(GDTやLDT)のインデックス。
これとオフセットからリニアアドレスが算出される。
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構造体には何が入っているのでしょうか?
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
…
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が怪しいですね
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にも値を設定しています。
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
・カーネル権限レベル
であることがわかります。
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();

ここが怪しそうです。両者を追ってみます。
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 を追いかけます
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
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にはカーネルベースアドレスからの何らかのオフセット値が入っているはず!
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 ) // カーネルベースアドレス相対位置
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
カーネルベースアドレス相対位置
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命令を呼び出しているのがわかります
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() を追いかけます
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領域の後ろ)にコピーしているのがわかります
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() が呼ばれているのがわかります
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が設定されました
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が更新されていることがわかります。
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の初期化をみました。
あらためて、初期価値がどうなっているかを見ていきます。
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 でマッピング

// 遅延されてるマッピングを更新
// 新規確保領域にコピー
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()
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を設定している箇所を探します。
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)
が呼ばれると設定されるようです。
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関数設定されることがわかりました
今回やるところ その1(Xsyscall)
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()関数を呼び出しています。
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;
};
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, *, *)を呼び出しています
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 に対応しています
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 を呼び出していることがわかります。
今回やるところ その1(Xsyscall)
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()を呼び出していることがわかります

More Related Content

Recently uploaded

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 

Recently uploaded (8)

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 

Featured

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

Featured (20)

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 

start_printf_5_Xsyscall_syscall_sys_write

  • 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(); …
  • 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. 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. 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って何ぞ?
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 でマッピング // 遅延されてるマッピングを更新 // 新規確保領域にコピー
  • 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. 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. 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関数設定されることがわかりました
  • 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. 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. 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. 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. 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 を呼び出していることがわかります。
  • 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()を呼び出していることがわかります