あるmmapの話
Upcoming SlideShare
Loading in...5
×
 

あるmmapの話

on

  • 1,260 views

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

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

Statistics

Views

Total Views
1,260
Views on SlideShare
1,241
Embed Views
19

Actions

Likes
3
Downloads
2
Comments
0

2 Embeds 19

https://twitter.com 13
http://www.slideee.com 6

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

あるmmapの話 あるmmapの話 Presentation Transcript

  • あるmmapの話 2014/3/9 えとみ なるあき カーネル/VM探検隊@関西 6
  • お約束 ※ ビットの話が出てきますが、ビットはゼロからはじまるものとします。 ※ 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しているというのも重要キーワードです。
  • 超カッコイイワークステーション(死語)
  • 動かない (´・ω・`) NetBSD/alpha 6.1.1 Secondary Bootstrap, Revision 1.13 VMS PAL rev: 0x1000700010162 OSF PAL rev: 0x100070002015c Switch to OSF PAL code succeeded. Boot flags: a 10205376+201944=0x9ed480 Entering netbsd at 0xfffffc0000431230... NetBSD does not yet support system type 38 (???). panic: platform not supported Stopped in pid 0.1 (system) at fffffc0000496e18: ret zero,(ra) db>
  • 動かす(OpenBSDからコピー) (`・ω・´)
  • Xが動かない… (´・ω・`) X11 server support is currently broken on alpha(OpenBSD) /usr/X11R6/bin/XdecNetBSD(あっ...察し)
  • mikutterが使えない…
  • mlterm-fbも使えない
  • mikutter 動いた。完! (`・ω・´) pr-48148 NetBSD/alpha fails to install on AlphaStaion DS15 pr-48431 radeonfb fails to initialize on Alpha Station DS15 and Alpha Station XP1000 pr-48492 sys/dev/wscons/wsdisplay_glyphcache.c/ glyphcache_wipe causes kernel panic.
  • パッチにミス(良くわかってない子) ======================================================================== 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 ========================================================================
  • 正解! > > --- 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).
  • 教授!!これはいったい!? 間違ったパッチで取得できる フレームバッファの??物理??アドレス 0xffff.fd04.4000.0000 正しいパッチで取得できる フレームバッファの物理アドレス 0x904.4000.0000 何故動くのか調査開始や!!
  • ? ( ˘⊖˘)。o(待てよ、mlterm-fbや X with wfsb driver はどうやって       画面に描画しているのか…?) ☛ ☛ mmap(2)でした… ▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわああああああ
  • mmap(2) mmap() は、UNIXのシステムコールのひとつで、 ファイルやデバイスなどの オペレーティングシステム (OS) 上のリソースの一部または 全部を連続した仮想アドレス空間にマッピングする関数である。 ファイルシステム上のリソースに対するアクセス方法として、 ストリームI/Oを行うシステムコールとの比較で、 ユーザ空間とカーネル空間の間で読み書きされるデータの ブロック転送が多くのアーキテクチャ上では発生しないことから、好まれる場合がある。 デバイスでは、ioctl()とともにメモリマップドI/Oや DMAなどの操作を抽象化するものとして ドライバからファイルI/Oサービスの一部として提供されることがある。 wikipediaより
  • Device pager is 何? ハードウェアに対するmmap(2)
  • Device pager is 何? ※wsdisplay + wskbd がセットになった端末が ttyE* if ((fd = open( "/dev/ttyE0", O_RDWR)) < 0) { fprintf(stderr, "Failed to open /dev/ttyE0n") ; 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);
  • 白くなっちゃった サンプルコードを実行すると画面が白くなりました どうやって仮想アドレスと物理アドレスが紐づくか調べてみました
  • src/sys/uvm/uvm_mmap.c ←匿名マップかどうか判断 uvm_mmap 処理のポイントは赤線の3点です 匿名マップは今回扱いません!
  • 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 * の処理も参照してください
  • キャラクタデバイスか判断 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構造体を参照して下さい
  • デバイスだったら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));
  • /src/sys/uvm/uvm_device.c ← /dev/ttyE0のデバイスナンバー取得 ← bus_space_mmap経由で mmapされる範囲の物理アドレスが   有効か判断(フレームバッフアの領域) sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c
  • /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も参照して下さい
  • /src/sys/uvm/uvm_device.c ← /dev/ttyE0のデバイスナンバー取得 ← bus_space_mmap経由で mmapされる範囲の物理アドレスが   有効か判断(フレームバッフアの領域) sys/arch/alpha/pci/pci_bwx_bus_mem_chipdep.c
  • 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を呼び出します
  • 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 }
  • 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 API http://www.ceres.dti.ne.jp/tsutsui/netbsd/doc/bus_space2.html が参考になります ようやくフレームバッファの物理アドレスが出てくる!!
  • 突然の質問コ ナ せんせ 、 CHIP_MEM_SYS_STARTがこの機種のPCIバスのベース値になる と言いましたけど、そういう情報はどこから調べるんですか? addrはフレームバッファのアドレスでPCIバスのベース値からのオフセット と言いましたけど、どうやって調べるんですか? OSの移植にはメモリマップが必要なんだ。(凄い人はいらない) PCIバスのベース値などのハード固有の情報はメモリマップに書いて あるんだよ。今回の場合はTru64 UNIXのヘッダファイルを参考にしたんだ。 フレームバッファのアドレスはPCIコンフィギュレーション空間を読めば 分かるんだよ。 ハードがメモリマップと違った動きをすると、 クリスマスに一人PS2キーボードを抜き差しするような事態に陥るんだ。 OpenBSD/sgi install battle http://togetter.com/li/599343 「NetBSD移植 いまむかし」 http://www.ceres.dti.ne.jp/tsutsui/osc12eh/img00.html も参考にしてね。 <
  • /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 };
  • src/sys/uvm/uvm_mmap.c ← udv_attachの後、様々な関数を経てmmapへ マップされる領域へのポインタを返します sys_mmap関数は mmapのsystem call ちなみに ユーザーランドから引数 を受け取るところです
  • ここまでのおさらい ※ mmapするフレームバッファの物理アドレスの範囲が有効だったら、 uvm_device構造体を割り当てる ※ mmapするフレームバッファの物理アドレスの範囲が有効だったら、   mmapへマップされる領域へのポインタを返す ※ まだ、 mmapへマップされる領域へのポインタ(仮想アドレス)と フレームバッファの物理アドレスは紐づいていない その後、memsetすると…
  • page fault src/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;
  • 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
  • src/sys/uvm/uvm_device.c フレームバッファの物理アドレス取得 (bus_space_mmap経由で) ← pmap_enterして 仮想アドレスと物理アドレスを紐づける
  • 仮想アドレスと物理アドレスを紐づける  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) {
  • src/sys/arch/alpha/alpha/pmap.c pmap_enter ← フレームバッファのアドレス(VA) からPTEを作成 ← PTEをセット(VA→PAが変換可能になる)
  • フレームバッファのアドレス(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 0x0001 212 213 #define ALPHA_PTE_FAULT_ON_READ 0x0002 214 #define ALPHA_PTE_FAULT_ON_WRITE 0x0004 215 #define ALPHA_PTE_FAULT_ON_EXECUTE 0x0008 216 217 #define ALPHA_PTE_ASM 0x0010 /* addr. space match */ 218 #define ALPHA_PTE_GRANULARITY 0x0060 /* granularity hint */ 219 220 #define ALPHA_PTE_PROT 0xff00 221 #define ALPHA_PTE_KR 0x0100 222 #define ALPHA_PTE_UR 0x0200 223 #define ALPHA_PTE_KW 0x1000 224 #define ALPHA_PTE_UW 0x2000 src/sys/arch/alpha/include/alpha_cpu.h
  • src/sys/arch/alpha/alpha/pmap.c pmap_enter ← フレームバッファのアドレス(VA) からPTEを作成 ← PTEをセット(VA→PAが変換可能になる)
  • 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_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 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 0xfffffdffffffffff 205 #define ALPHA_K1SEG_BASE 0xfffffe0000000000 /* virtual */ 206 #define ALPHA_K1SEG_END 0xffffffffffffffff 207 208 #define ALPHA_K0SEG_TO_PHYS(x) ((x) & ~ALPHA_K0SEG_BASE) 209 #define ALPHA_PHYS_TO_K0SEG(x) ((x) | ALPHA_K0SEG_BASE) ( ˘⊖˘)。o(結局ksegって何なのかな?)
  • ALPHA_PHYS_TO_K0SEG is 何 kseg = 0xffff.fc00.0000.0000 物理アドレス ※ ksegのアドレスには 物理アドレスが1:1でマップされている ※ alphaにはそういうアドレスがある
  • 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に書き込んだりする必要が無い)
  • 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がフレームバッファの物理アドレスになる! ハズ…
  • 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##0000 66 * ...mix in the old SP: 0xffff##fc00##0000##0000 67 * ...after PA sign ext: 0xffff##ff00##a000##0000 68 * (PA<42:41> ignored)(ただしマニュアルには記載無し) src/sys/arch/alpha/pci/tsreg.h なんと0x904.4000.0000と同じ値に! ∩( ・ω・)∩ばんじゃーい… つまり…
  • なっ、なんだってェーーーー! アドレスの41bit目と42bit目 が無視されているために間違ったアドレスに書き込んだけど 動いてしまったんだよ!!
  • 参考文献 & お世話になった方 UVMについて The UVM Virtual Memory System - Usenix (英語の論文) BSD magazine 13号 特集 NetBSDの設計と実装(論文を翻訳したもの) OSの一般的な知識 BSDカーネルの設計と実装 -FreeBSD詳解- Solarisインターナル ―カーネル構造のすべて(amazonで中古がめちゃ安くてビックり) Alphaについて Alpha Architecture Reference Manual Fourth Edition Alpha 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_kenji NONAKA Kimihiro ‫‏‬@nonakap、Hiroshi Tokuda ‫‏‬@tokudahiroshi、本尊ではないことが本件寄付の後に判明 ‫‏‬@ioriveur 波打際のだよもんさん ‫‏‬@daemon1995、まあぼ@cub ‫‏‬@marbocub ※イカ先生 ‫‏‬@impreza_gf8にはAlpha Station XP1000を譲っていただきました! mltermという神アプリを作成された arakiken ‫‏‬@arakiken OpenBSD/Sgiでお世話になったので、この場を借りて改めてお礼申し上げます! しゅううさんにゃんぱすー ‫‏‬@syuu1228