捉虫日记 0003: MIPS Cache 操作时的奇怪现象
来自Jack's Lab
现象
本意想用如下的函数人为地将 Cache 的某个组的所有行的Tag 强制写为相同的值,然后访问一个索引到这个组的地址,人造一个多命中的情况 ;)
static void sysrq_handle_comcat(int key, struct tty_struct *tty) { const u32 addr_cool = 0x80400000; const u32 ws_inc = 1UL << current_cpu_data.dcache.waybit; const u32 ws_end = current_cpu_data.dcache.ways << current_cpu_data.dcache.waybit; int i = 0; u32 taglo = 0; /* dump all line's tag in set 0*/ printk("<0>----------------------- Before invalidated ---------------------- \n"); do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo_%d = 0x%08x\n", i/ws_inc, taglo); i += ws_inc; } while (i < ws_end); printk("<0> -------------- Invalidated all lines in set_0 ------------------ \n"); asm ("mtc0 $0, $28, 2\n\t"); // set TagLo to zero /* clear all line's tag in set 0 */ do { asm ( "lui $8, 0x8040\n\t" "mtc0 $0, $28, 2\n\t" "addu $3, $8, %0\n\t" //"cache 0x01, 0($3)\n\t" /* invalidate write-back a line */ "cache 0x09, 0($3)\n\t" /* clear this line's tag to zero */ : : "r"(i) : "$8", "$3" ); i += ws_inc; } while (i < ws_end); /* dump all line's tag in set 0*/ i = 0; do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo_%d = 0x%08x\n", i/ws_inc, taglo); i += ws_inc; } while (i < ws_end); printk("<0> ----------------- Cache a line into set 0 ---------------------- \n"); /* cache a line into set 0 */ u32 tmp = *(u32 *) addr_cool; printk("<0> *(u32 *)0x%08x = 0x%08x\n", addr_cool, tmp); /* dump all line's tag in set 0*/ i = 0; do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo_%d = 0x%08x\n", i/ws_inc, taglo); i += ws_inc; } while (i < ws_end); printk("<0> -- Get a validated line, read its Tag, write it to all lines -- \n"); /* find a validated line, read its tag */ i = 0; do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo = 0x%08x\n", taglo); i += ws_inc; } while ((((taglo >> 6) & 3) == 0) && i < ws_end); /* cache line is invalide */ /* set all other line's tag to the validated line's tag */ do { asm ( "lui $8, 0x8040\n\t" "addu $3, $8, %0\n\t" "cache 0x09, 0($3)\n\t" /* store tag */ : : "r" (i) : "$8", "$3" ); i += ws_inc; } while (i < ws_end); /* dump all line's tag in set 0*/ i = 0; do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo_%d = 0x%08x\n", i/ws_inc, taglo); i += ws_inc; } while (i < ws_end); /* multi-hit cache line */ printk("<0> --------------------- Multi-Hit cache line --------------------- \n"); tmp = *(u32 *) addr_cool; printk("<0> *(u32 *)0x%08x = 0x%08x\n\n", addr_cool, tmp); printk("<0> ------------------------ After Multi-Hit ----------------------- \n"); /* dump all line's tag in set 0*/ i = 0; do { asm ( "lui $3, 0x8040\n\t" "addu $8, $3, %1\n\t" "cache 0x05, 0($8)\n\t" "mfc0 %0, $28, 2\n\t" : "=r"(taglo) : "r"(i) : "$8", "$3" ); printk("<0> taglo_%d = 0x%08x\n", i/ws_inc, taglo); i += ws_inc; } while (i < ws_end); printk("<0> ---------------------------------------------------------------- \n\n"); } static struct sysrq_key_op sysrq_jack_op = { .handler = sysrq_handle_jack, .help_msg = "Jack's Debug", .action_msg = "Jack's Debug", .enable_mask = SYSRQ_ENABLE_DUMP, };
将这个函数映射到 sysrq 的 j 键方便操作。
哪知在多个 MIPS 平台上,第二步将组内所用行的 Tag 置为 0 的操作总是有一行不为所动 :( ,一下是一些 log:
4KEc 平台,8KB, 2-way, linesize 16 bytes L1 DCache:
telnet> send brk SysRq : Jack's Debug ----------------------- Before invalidated ---------------------- taglo_0 = 0x003260c0 taglo_1 = 0x002b4080 -------------- Invalidated all lines in set_0 ------------------ taglo_0 = 0x00000000 taglo_1 = 0x002b4080 ----------------- Cache a line into set 0 ---------------------- *(u32 *)0x80000000 = 0x3c1b802f taglo_0 = 0x00000080 taglo_1 = 0x002b4080 -- Get a validated line, read its Tag, write it to all lines -- taglo = 0x00000080 taglo_0 = 0x00000080 taglo_1 = 0x00000080 --------------------- Multi-Hit cache line --------------------- *(u32 *)0x80000000 = 0x802b6188 ------------------------ After Multi-Hit ----------------------- taglo_0 = 0x002b4080 taglo_1 = 0x00000080 ----------------------------------------------------------------
下面的这次,因为残留的一行有效,且在目标行的前面,所以这次造多命中失败了:(
telnet> send brk SysRq : Jack's Debug ----------------------- Before invalidated ---------------------- taglo_0 = 0x002b4080 taglo_1 = 0x003260c0 -------------- Invalidated all lines in set_0 ------------------ taglo_0 = 0x00000000 taglo_1 = 0x003260c0 ----------------- Cache a line into set 0 ---------------------- *(u32 *)0x80000000 = 0x3c1b802f taglo_0 = 0x002b4080 taglo_1 = 0x00000080 -- Get a validated line, read its Tag, write it to all lines -- taglo = 0x002b4080 taglo_0 = 0x002b4080 taglo_1 = 0x002b4080 --------------------- Multi-Hit cache line --------------------- *(u32 *)0x80000000 = 0x3c1b802f ------------------------ After Multi-Hit ----------------------- taglo_0 = 0x002b4080 taglo_1 = 0x00000080 ----------------------------------------------------------------
VR5500 平台,64kB, 4-way, linesize 32 bytes L1 DCache:
----------------------- Before invalidated ---------------------- taglo_0 = 0x000458c0 taglo_1 = 0x0004a481 taglo_2 = 0x0004e8c1 taglo_3 = 0x0014dcc1 -------------- Invalidated all lines in set_0 ------------------ taglo_0 = 0x00000000 taglo_1 = 0x00000000 taglo_2 = 0x00000000 taglo_3 = 0x000458c0 ----------------- Cache a line into set 0 ---------------------- *(u32 *)0x80000000 = 0x3c1b804b taglo_0 = 0x00000000 taglo_1 = 0x00000000 taglo_2 = 0x00000081 taglo_3 = 0x000458c0 -- Get a validated line, read its Tag, write it to all lines -- taglo = 0x00000000 taglo = 0x00000000 taglo = 0x00000081 taglo_0 = 0x00000081 taglo_1 = 0x000458c0 taglo_2 = 0x00000081 taglo_3 = 0x00000081 --------------------- Multi-Hit cache line --------------------- *(u32 *)0x80000000 = 0x8045a188 ------------------------ After Multi-Hit ----------------------- taglo_0 = 0x00000081 taglo_1 = 0x000458c0 taglo_2 = 0x00000081 taglo_3 = 0x00000081 ----------------------------------------------------------------
多次测试表明,似乎每次顽强生存着的 Tag 值皆一样,但位置可能会改变。这真是个诡异的现象,不知是不是 Cache lock 的原因。