あるmmapの話
-
Upload
nullnilaki -
Category
Technology
-
view
1.472 -
download
2
description
Transcript of あるmmapの話
ある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: 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>
動かす(OpenBSDからコピー) (`・ω・´)
Xが動かない… (´・ω・`)
X11 server support is currently broken on alpha(OpenBSD)
/usr/X11R6/bin/XdecNetBSD(あっ...察し)
mikutterが使えない…
mlterm-fbも使えない
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.
パッチにミス(良くわかってない子)========================================================================
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/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);
白くなっちゃった
サンプルコードを実行すると画面が白くなりましたどうやって仮想アドレスと物理アドレスが紐づくか調べてみました
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 APIhttp://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 battlehttp://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 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;
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.cpmap_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 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
src/sys/arch/alpha/alpha/pmap.cpmap_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_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って何なのかな?)
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##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と同じ値に!∩( ・ω・)∩ばんじゃーい… つまり…
なっ、なんだってェーーーー!
アドレスの41bit目と42bit目が無視されているために間違ったアドレスに書き込んだけど
動いてしまったんだよ!!
参考文献 & お世話になった方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