捉虫日记 0003: MIPS Cache 操作时的奇怪现象
来自Jack's Lab
1 现象
本意想用如下的函数人为地将 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 的原因。
2 Reference