あるmmapの話

45
あるmmapの話 2014/3/9 えとみ なるあき カーネル/VM探検隊@関西 6

description

2014年3月9日に行われたカーネル/VM探検隊@関西 6でのスライドです。 楽しんでいってね!

Transcript of あるmmapの話

Page 1: あるmmapの話

あるmmapの話

2014/3/9 えとみ なるあきカーネル/VM探検隊@関西 6

Page 2: あるmmapの話

お約束

※ ビットの話が出てきますが、ビットはゼロからはじまるものとします。※ wikipediaのページング方式の項目をあらかじめ読んでおくと、  よりいっそう楽しめる内容になっております。 http://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E6%96%B9%E5%BC%8F

※ UVMはNetBSDおよびOpenBSDのメモリ管理システムの事です。※ ALPHA_PHYS_TO_K0SEGは重要キーワードです。※ IobaseにP_PCI_MEMをORしているというのも重要キーワードです。

Page 3: あるmmapの話

超カッコ イイワークステーション(死語)

Page 4: あるmmapの話

動かない (´・ω・`)

NetBSD/alpha 6.1.1 Secondary Bootstrap, Revision 1.13

VMS PAL rev: 0x1000700010162OSF PAL rev: 0x100070002015cSwitch to OSF PAL code succeeded.

Boot flags: a10205376+201944=0x9ed480

Entering netbsd at 0xfffffc0000431230...

NetBSD does not yet support system type 38 (???).

panic: platform not supportedStopped in pid 0.1 (system) at fffffc0000496e18: ret zero,(ra)db>

Page 5: あるmmapの話

動かす(OpenBSDからコピー) (`・ω・´)

Page 6: あるmmapの話

Xが動かない… (´・ω・`)

X11 server support is currently broken on alpha(OpenBSD)

/usr/X11R6/bin/XdecNetBSD(あっ...察し)

Page 7: あるmmapの話

mikutterが使えない…

Page 8: あるmmapの話

mlterm-fbも使えない

Page 9: あるmmapの話

mikutter 動いた。完! (`・ω・´)

pr-48148NetBSD/alpha fails to install on AlphaStaion DS15

pr-48431radeonfb fails to initialize on Alpha Station DS15 and Alpha Station XP1000

pr-48492sys/dev/wscons/wsdisplay_glyphcache.c/glyphcache_wipe causes kernel panic.

Page 10: あるmmapの話

パッチにミス(良くわかってない子)========================================================================

I think that need ALPHA_PHYS_TO_K0SEG.Because direct-mapped addresses.

diff -Naru src.orig/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c src/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c--- src.orig/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c 2012-02-06 11:14:15.000000000 +0900+++ src/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c 2013-12-01 21:33:43.000000000 +0900@@ -500,7 +500,7 @@ int flags) {

- return (alpha_btop(CHIP_MEM_SYS_START(v) + addr + off));

+ return (alpha_btop(ALPHA_PHYS_TO_K0SEG(CHIP_MEM_SYS_START(v) + addr + off))); }

static inline void========================================================================

Page 11: あるmmapの話

正解! > > --- pci/tsp_bus_mem.c 6 Feb 2012 02:14:15 -0000 1.12 > > +++ pci/tsp_bus_mem.c 15 Jan 2014 15:18:22 -0000 : > > -#define CHIP_MEM_SYS_START(v) (((struct tsp_config *)(v))->pc_iobase) > > +#define CHIP_MEM_SYS_START(v) \

> > + (((struct tsp_config *)(v))->pc_iobase | P_PCI_MEM) : > I apply this patch. > It work fine!

Thanks. I'll commit this part soon.

> >return (alpha_btop(ALPHA_PHYS_TO_K0SEG(CHIP_MEM_SYS_START(v) + addr + off))); > My patch is work but it is very very rare instances. > I guess it should be MMU's magic!

I guess some higher bits (around P_PCI_MEM) are ignored in pmap_enter() or alpha's MMU (but not sure).

Page 12: あるmmapの話

教授!!これはいったい!?

間違ったパッチで取得できる

フレームバッファの??物理??アドレス 0xffff.fd04.4000.0000

正しいパッチで取得できる

フレームバッファの物理アドレス 0x904.4000.0000

何故動くのか調査開始や!!

Page 13: あるmmapの話

?

( ˘⊖˘)。o(待てよ、mlterm-fbや X with wfsb driver はどうやって      画面に描画しているのか…?)

mmap(2)でした…▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわああああああ

Page 14: あるmmapの話

mmap(2)mmap() は、UNIXのシステムコールのひとつで、

ファイルやデバイスなどのオペレーティングシステム (OS) 上のリソースの一部または全部を連続した仮想アドレス空間にマッピングする関数である。

ファイルシステム上のリソースに対するアクセス方法として、ストリームI/Oを行うシステムコールとの比較で、ユーザ空間とカーネル空間の間で読み書きされるデータのブロック転送が多くのアーキテクチャ上では発生しないことから、好まれる場合がある。

デバイスでは、ioctl()とともにメモリマップドI/OやDMAなどの操作を抽象化するものとしてドライバからファイルI/Oサービスの一部として提供されることがある。

wikipediaより

Page 15: あるmmapの話

Device pager is 何?

ハードウェアに対するmmap(2)

Page 16: あるmmapの話

Device pager is 何?

※wsdisplay + wskbd がセットになった端末が ttyE*

if ((fd = open( "/dev/ttyE0", O_RDWR)) < 0) {fprintf(stderr, "Failed to open /dev/ttyE0\n") ;return 1;

}

mode = WSDISPLAYIO_MODE_MAPPED ;

if (ioctl(STDIN_FILENO, WSDISPLAYIO_SMODE, &mode) == -1) {fprintf(stderr, "WSDISPLAYIO_SMODE failed.\n");return 1;

}

if ((fb = mmap( NULL, line_length * vinfo.height,PROT_WRITE|PROT_READ,     MAP_SHARED, fd , (off_t)0)) == MAP_FAILED) { fprintf(stderr, "mmap failed.\n");      goto error;}

memset(fb, 0xff, line_length * vinfo.height);

Page 17: あるmmapの話

白くなっちゃった

サンプルコードを実行すると画面が白くなりましたどうやって仮想アドレスと物理アドレスが紐づくか調べてみました

Page 18: あるmmapの話

src/sys/uvm/uvm_mmap.c

←匿名マップかどうか判断

uvm_mmap

処理のポイントは赤線の3点です匿名マップは今回扱いません!

Page 19: あるmmapの話

openした/dev/ttyE0のvnode構造体を取得

1139 KASSERT(handle != NULL); 1140 vp = (struct vnode *)handle; 1141 1142 /* 1143 * Don't allow mmap for EXEC if the file system 1144 * is mounted NOEXEC. 1145 */

file構造体、fd_getfile(9)sys_mmap関数内 vp = fp->f_data;/* convert to vnode *の処理も参照してください

Page 20: あるmmapの話

キャラクタデバイスか判断

1146 if ((prot & PROT_EXEC) != 0 && 1147 (vp->v_mount->mnt_flag & MNT_NOEXEC) != 0) 1148 return (EACCES); 1149 1150 if (vp->v_type != VCHR) { 1151 error = VOP_MMAP(vp, prot, curlwp->l_cred);

159 union { 160 struct mount *vu_mountedhere; /* v: ptr to vfs (VDIR) */ 161 struct socket *vu_socket; /* v: unix ipc (VSOCK) */ 162 struct specnode *vu_specnode; /* v: device (VCHR, VBLK) */ 163 struct fifoinfo *vu_fifoinfo; /* v: fifo (VFIFO) */ 164 struct uvm_ractx *vu_ractx; /* i: read-ahead ctx (VREG) */ 165 } v_un;

vnode構造体を参照して下さい

Page 21: あるmmapの話

デバイスだったらudv_attachを呼ぶ

1169 * XXX Some devices don't like to be mapped with 1170 * XXX PROT_EXEC or PROT_WRITE, but we don't really 1171 * XXX have a better way of handling this, right now 1172 */ 1173 do { 1174 uobj = udv_attach((void *) &vp->v_rdev, 1175 (flags & MAP_SHARED) ? i : 1176 (i & ~VM_PROT_WRITE), foff, size); 1177 i--; 1178 } while ((uobj == NULL) && (i > 0));

Page 22: あるmmapの話

/src/sys/uvm/uvm_device.c

← /dev/ttyE0のデバイスナンバー取得

← bus_space_mmap経由で mmapされる範囲の物理アドレスが  有効か判断(フレームバッフアの領域)

sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c

Page 23: あるmmapの話

/dev/ttyE0のデバイスナンバー取得

108 struct uvm_object * 109 udv_attach(void *arg, vm_prot_t accessprot, 110 voff_t off, /* used only for access check */ 111 vsize_t size /* used only for access check */) 112 {

113 dev_t device = *((dev_t *)arg); 114 struct uvm_device *udv, *lcv;

デバイスナンバーやcdev_mmap、 cdevsw構造体についてはBSDカーネルの設計と実装の第6章、入出力システムの概要が詳しいです/src/sys/arch/alpha/conf/majors.alphaも参照して下さい

Page 24: あるmmapの話

/src/sys/uvm/uvm_device.c

← /dev/ttyE0のデバイスナンバー取得

← bus_space_mmap経由で mmapされる範囲の物理アドレスが  有効か判断(フレームバッフアの領域)

sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c

Page 25: あるmmapの話

mmapされる領域が有効なフレームバッファのアドレスか判断

  143 /* 144 * Check that the specified range of the device allows the 145 * desired protection. 146 * 147 * XXX assumes VM_PROT_* == PROT_* 148 * XXX clobbers off and size, but nothing else here needs them. 149 */ 150 151 while (size != 0) {

152 if (cdev_mmap(device, off, accessprot) == -1) { 153 return (NULL); 154 } 155 off += PAGE_SIZE; size -= PAGE_SIZE; 156 }

cdev_mmap関数はデバイスナンバーを引数にcdevsw構造体経由でデバイスドライバ(今回はradeonfb)のmmapを呼び出します

Page 26: あるmmapの話

mmapされる領域が有効なフレームバッファのアドレスか判断

1245 paddr_t 1246 radeonfb_mmap(void *v, void *vs, off_t offset, int prot) 1247 { 1248 struct vcons_data *vd; 1249 struct radeonfb_display *dp; 1250 struct radeonfb_softc *sc; 1251 paddr_t pa; … 1256 1257 /* XXX: note that we don't allow mapping of registers right now */ 1258 /* XXX: this means that the XFree86 radeon driver won't work */ 1259 if ((offset >= 0) && (offset < (dp->rd_virty * dp->rd_stride))) { 1260 pa = bus_space_mmap(sc->sc_memt, 1261 sc->sc_memaddr + dp->rd_offset + offset, 0, 1262 prot, BUS_SPACE_MAP_LINEAR); 1263 return pa; 1264 }

Page 27: あるmmapの話

mmapされる領域が有効なフレームバッファのアドレスか判断

494 paddr_t 495 __C(CHIP,_mem_mmap)( 496 void *v, 497 bus_addr_t addr, 498 off_t off, 499 int prot, 500 int flags) 501 { 502 503 return (alpha_btop(CHIP_MEM_SYS_START(v) + addr + off)); 504 }

src/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c

bus_space_mmap(9)についてはbus space APIhttp://www.ceres.dti.ne.jp/tsutsui/netbsd/doc/bus_space2.htmlが参考になります

ようやくフレームバッファの物理アドレスが出てくる!!

Page 28: あるmmapの話

突然の質問コ~ナ~せんせ~、 CHIP_MEM_SYS_STARTがこの機種のPCIバスのベース値になると言いましたけど、そういう情報はどこから調べるんですか?addrはフレームバッファのアドレスでPCIバスのベース値からのオフセットと言いましたけど、どうやって調べるんですか?

OSの移植にはメモリマップが必要なんだ。(凄い人はいらない)PCIバスのベース値などのハード固有の情報はメモリマップに書いてあるんだよ。今回の場合はTru64 UNIXのヘッダファイルを参考にしたんだ。フレームバッファのアドレスはPCIコンフィギュレーション空間を読めば分かるんだよ。ハードがメモリマップと違った動きをすると、クリスマスに一人PS2キーボードを抜き差しするような事態に陥るんだ。OpenBSD/sgi install battlehttp://togetter.com/li/599343「NetBSD移植 いまむかし」http://www.ceres.dti.ne.jp/tsutsui/osc12eh/img00.htmlも参考にしてね。

<

Page 29: あるmmapの話

/src/sys/uvm/uvm_device.c

uvm_device構造体を初期化しますその際uvm_deviceops構造体を設定する事でuvm_objectからの操作を抽象化します

218 /* Note: both calls may allocate memory and sleep. */ 219 udv = kmem_alloc(sizeof(*udv), KM_SLEEP); 220 uvm_obj_init(&udv->u_obj, &uvm_deviceops, true, 1); 221 222 mutex_enter(&udv_lock); 223 251 udv->u_flags = 0; 252 udv->u_device = device; 253 LIST_INSERT_HEAD(&udv_list, udv, u_list); 254 mutex_exit(&udv_lock); 255 return(&udv->u_obj);

フレームバッファの領域が有効だったら

74 const struct uvm_pagerops uvm_deviceops = { 75 .pgo_init = udv_init, 76 .pgo_reference = udv_reference, 77 .pgo_detach = udv_detach, 78 .pgo_fault = udv_fault, 79 };

Page 30: あるmmapの話

src/sys/uvm/uvm_mmap.c

← udv_attachの後、様々な関数を経てmmapへ マップされる領域へのポインタを返します

sys_mmap関数はmmapのsystem call

ちなみにユーザーランドから引数を受け取るところです

Page 31: あるmmapの話

ここまでのおさらい

※ mmapするフレームバッファの物理アドレスの範囲が有効だったら、 uvm_device構造体を割り当てる

※ mmapするフレームバッファの物理アドレスの範囲が有効だったら、  mmapへマップされる領域へのポインタを返す

※ まだ、 mmapへマップされる領域へのポインタ(仮想アドレス)と フレームバッファの物理アドレスは紐づいていない

その後、memsetすると…

Page 32: あるmmapの話

page faultsrc/sys/arch/alpha/alpha/locore.s

src/sys/arch/alpha/alpha/trap.c

memsetするアドレス(VA)に対応するフレームバッファのアドレス(PA)がTLBに無いため割り込みがかかります

ページングするためuvm_faultを呼びます

428 /* 429 * XentMM: 430 * System memory management fault entry point. 431 */ 432 433 PALVECT(XentMM) /* setup frame, save registers */ 434 435 /* a0, a1, & a2 already set up */ 436 ldiq a3, ALPHA_KENTRY_MM 437 mov sp, a4 ; .loc 1 __LINE__ 438 CALL(trap) 439 440 jmp zero, exception_return 441 END(XentMM)

366 367 case ALPHA_KENTRY_MM: 368 pcb = lwp_getpcb(l);… 462 va = trunc_page((vaddr_t)a0); 463 pcb->pcb_onfault = 0; 464 rv = uvm_fault(map, va, ftype); 465 pcb->pcb_onfault = onfault;

Page 33: あるmmapの話

src/sys/uvm/uvm_fault.c uvm_fault_internal

割り込みハンドラから呼ばれたuvm_falut_internal関数内でudv_fault関数が呼ばれます

891 if (uobj && uobj->pgops->pgo_fault != NULL) { 892 /* 893 * invoke "special" fault routine. 894 */ 895 mutex_enter(uobj->vmobjlock); 896 /* locked: maps(read), amap(if there), uobj */ 897 error = uobj->pgops->pgo_fault(&ufi, 898 flt.startva, pages, flt.npages, 899 flt.centeridx, flt.access_type, 900 PGO_LOCKED|PGO_SYNCIO); 901

Page 34: あるmmapの話

src/sys/uvm/uvm_device.c

フレームバッファの物理アドレス取得 (bus_space_mmap経由で)

← pmap_enterして 仮想アドレスと物理アドレスを紐づける

Page 35: あるmmapの話

仮想アドレスと物理アドレスを紐づける

 418 mdpgno = cdev_mmap(device, curr_offset, access_type); 419 if (mdpgno == -1) { 420 retval = EIO; 421 break; 422 } 423 paddr = pmap_phys_address(mdpgno); 424 mmapflags = pmap_mmap_flags(mdpgno); 425 mapprot = ufi->entry->protection; 426 UVMHIST_LOG(maphist, 427 " MAPPING: device: pm=0x%x, va=0x%x, pa=0x%lx, at=%d", 428 ufi->orig_map->pmap, curr_va, paddr, mapprot); 429 if (pmap_enter(ufi->orig_map->pmap, curr_va, paddr, mapprot, 430 PMAP_CANFAIL | mapprot | mmapflags) != 0) {

Page 36: あるmmapの話

src/sys/arch/alpha/alpha/pmap.cpmap_enter

← フレームバッファのアドレス(VA) からPTEを作成

← PTEをセット(VA→PAが変換可能になる)

Page 37: あるmmapの話

フレームバッファのアドレス(PA)からPTEを作成

1793 validate: 1794 /* 1795 * Build the new PTE. 1796 */ 1797 npte = ((pa >> PGSHIFT) << PG_SHIFT) | pte_prot(pmap, prot) | PG_V; アクセス権の設定

211 #define ALPHA_PTE_VALID 0x0001212 213 #define ALPHA_PTE_FAULT_ON_READ 0x0002214 #define ALPHA_PTE_FAULT_ON_WRITE 0x0004215 #define ALPHA_PTE_FAULT_ON_EXECUTE 0x0008216 217 #define ALPHA_PTE_ASM 0x0010 /* addr. space match */218 #define ALPHA_PTE_GRANULARITY 0x0060 /* granularity hint */219 220 #define ALPHA_PTE_PROT 0xff00221 #define ALPHA_PTE_KR 0x0100222 #define ALPHA_PTE_UR 0x0200223 #define ALPHA_PTE_KW 0x1000224 #define ALPHA_PTE_UW 0x2000

src/sys/arch/alpha/include/alpha_cpu.h

Page 38: あるmmapの話

src/sys/arch/alpha/alpha/pmap.cpmap_enter

← フレームバッファのアドレス(VA) からPTEを作成

← PTEをセット(VA→PAが変換可能になる)

Page 39: あるmmapの話

ALPHA_PHYS_TO_K0SEG is 何src/sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c(間違ったパッチ)

src/sys/arch/alpha/include/alpha_cpu.h494 paddr_t495 __C(CHIP,_mem_mmap)(496 void *v,497 bus_addr_t addr,498 off_t off,499 int prot,500 int flags)501 {502 bus_addr_t memaddr;503 504 memaddr = CHIP_MEM_SYS_START(v) + addr + off;505 return (alpha_btop(ALPHA_PHYS_TO_K0SEG(memaddr)));506 }

203 #define ALPHA_K0SEG_BASE 0xfffffc0000000000 /* direct-mapped */204 #define ALPHA_K0SEG_END 0xfffffdffffffffff205 #define ALPHA_K1SEG_BASE 0xfffffe0000000000 /* virtual */206 #define ALPHA_K1SEG_END 0xffffffffffffffff207 208 #define ALPHA_K0SEG_TO_PHYS(x) ((x) & ~ALPHA_K0SEG_BASE)209 #define ALPHA_PHYS_TO_K0SEG(x) ((x) | ALPHA_K0SEG_BASE)

( ˘⊖˘)。o(結局ksegって何なのかな?)

Page 40: あるmmapの話

ALPHA_PHYS_TO_K0SEG is 何

kseg = 0xffff.fc00.0000.0000

物理アドレス

※ ksegのアドレスには 物理アドレスが1:1でマップされている※ alphaにはそういうアドレスがある

Page 41: あるmmapの話

ksegの正しい使い方

#define ALPHA_K0SEG_BASE 0xfffffc0000000000#define ALPHA_PHYS_TO_K0SEG(x) ((x) | ALPHA_K0SEG_BASE)#define S_PAGE(phys) ((void *)ALPHA_PHYS_TO_K0SEG(phys))

pcireg_t *datap;u_long z;

datap = S_PAGE(0x10440000000UL);

for(z = 0; z < 24967295; z++){*(datap + z) = 0xffffff00;

}

例えば、radeonfb.cに以下のようなコードを書けば画面に色を書き込める

kseg経由でマップされたフレームバッファの物理アドレスにカーネル内からダイレクトに黄色を書き込んでいます(TLBミスを引き起こしたり、page tableに書き込んだりする必要が無い)

Page 42: あるmmapの話

44bit 物理アドレス空間

Alpha 21264は44bit 物理アドレス空間をサポートフレームバッファは* 41-bit Physical I/O Space Per PCI Node (8GBytes per) -- 43-bit VA mode* ----------------------------------------------------* 104 0000 0000 - 105 FFFF FFFF - I/O Space for PCI2 の範囲にあると思われる…(0x104.4000.0000?)ただし、I/O空間へのアクセスのため43bit目に1を立てる必要が有る。そこで正解パッチのように、P_PCI_MEM 0x800.0000.0000をORする必要があった。1001.0000.0100.0100.0000.0000.0000.0000.0000.0000.0000(44bit)よって物理アドレス0x904.4000.0000がフレームバッファの物理アドレスになる!ハズ…

Page 43: あるmmapの話

ksegのアドレスをPAとして使う?間違ったパッチの場合、ksegは本来仮想アドレスにも関わらず、物理アドレス(bus_space_mmapの戻り値)として扱われ、PTEが作成される。つまり、PTE経由で物理アドレス0xffff.fd04.4000.0000にアクセスした場合、何故描画できるのか?という問題になる。

1111.1111.1111.1111.1111.1101.0000.0100.0100.0000.0000.0000.0000.0000.0000.0000 1001.0000.0100.0100.0000.0000.0000.0000.0000.0000.0000

0xffff.fd04.4000.0000 0x904.4000.0000

44bit物理アドレスなので無視される? 64 * E.g., we want this: 0x0801##a000##0000

65 * We use this: 0x0101##a000##000066 * ...mix in the old SP: 0xffff##fc00##0000##000067 * ...after PA sign ext: 0xffff##ff00##a000##000068 * (PA<42:41> ignored)(ただしマニュアルには記載無し)

src/sys/arch/alpha/pci/tsreg.h

なんと0x904.4000.0000と同じ値に!∩( ・ω・)∩ばんじゃーい… つまり…

Page 44: あるmmapの話

なっ、なんだってェーーーー!

アドレスの41bit目と42bit目が無視されているために間違ったアドレスに書き込んだけど

動いてしまったんだよ!!

Page 45: あるmmapの話

参考文献 & お世話になった方UVMについてThe UVM Virtual Memory System - Usenix (英語の論文)BSD magazine 13号 特集 NetBSDの設計と実装(論文を翻訳したもの)

OSの一般的な知識BSDカーネルの設計と実装 -FreeBSD詳解-Solarisインターナル ―カーネル構造のすべて(amazonで中古がめちゃ安くてビックり)

AlphaについてAlpha Architecture Reference Manual Fourth EditionAlpha 21264 Microprocessor Hardware Reference Manual

ページングについてhttp://www.nminoru.jp/~nminoru/programming/arch/virtual_memory.html(仮想メモリ方式の分類)

お世話になった方&ファボって元気づけてくれた方Izumi Tsutsui ‏@tsutsuii以下古いフォロワーさんからJun Ebihara ‏@ebijun、hashimoto kenichi ‏@h_kenken、oshimaya ‏@oshimyja、Kenji Aoyama ‏@ao_kenjiNONAKA Kimihiro ‏@nonakap、Hiroshi Tokuda ‏@tokudahiroshi、本尊ではないことが本件寄付の後に判明 ‏@ioriveur波打際のだよもんさん ‏@daemon1995、まあぼ@cub ‏@marbocub ※イカ先生 ‏@impreza_gf8にはAlpha Station XP1000を譲っていただきました!

mltermという神アプリを作成されたarakiken ‏@arakiken

OpenBSD/Sgiでお世話になったので、この場を借りて改めてお礼申し上げます!しゅううさんにゃんぱすー ‏@syuu1228