什么是更新MMU转换表的正确方法 [英] what is the right way to update MMU translation table
问题描述
我启用了MMU我S3C2440板(3G - 4G内存::故障属性),当我没读/写3G,一切都很好 - 4G内存。所以测试页面错误矢量,我写信给一个0xFF即3G地址,如我所料,我从FSR正确的价值,所以我在_do_page_fault()这样做,步骤是这样的:
..... //设置新的页面转换表
.....
invlidate_icache(); //明确ICACHE
clr_dcache(); // WB时,明确DCACHE
invalidate_ttb(); //无效转换表
然后ISR_dataabort回来,我读了3G地址得到0xFF的,我之前沃特。
不幸的是我得到了一个数据再次中止。(我相信翻译表值我设置就可以了)
那么,什么是更新MMU转换表。任何帮助,正确的方法是AP preciate!谢谢
下面是主要code我用(只是一些测试)
(我是littlte奇怪ARM ARCH,所以这code可能是urgly)
/ * MMU TTB 0 BASE ATTR * /
的#define TTB0_FAULT(0 |(1 <<; 4;))/ * TTB FAULT * /
的#define TTB0_COARSE(1 |(1 <<; 4;))/ * COARSE页BASE ADDR * /
的#define TTB0_SEG(2 |(1 <<; 4;))/ *赛格BASE ADDR * /
的#define TTB0_FINE(3 |(1 <<; 4;))/ *精细PAGE BASE ADDR * // * MMU TTB 1 BASE ATTR * /
的#define TTB1_FAULT(0)
#定义TTB1_LPG(1)/ *大*页/
#定义TTB1_SPG(2)/ *小*页/
#定义TTB1_TPG(3)/ *微小的页* // *域访问优先级* /
#定义FAULT_PL(为0x0)/ *域故障* /
#定义USR_PL(为0x1)/ * USR模式* /
#定义RSV_PL(0X2)/ * *保留/
#定义SYS_PL段(0x3)/ * SYS模式* /#定义DOMAIN_FAULT(为0x0&LT;&LT; 5)/ *故障0 * /
的#define DOMAIN_SYS(为0x1&所述;小于5)/ * SYS 1 * /
的#define DOMAIN_USR(0X2&所述;小于5)/ * USR 2 * // * C,B位* /
的#define CB(3'; 2)/ * cache_on,write_back * /
的#define CNB(2'; 2)/ * cache_on,write_through * /
#定义NCB(1 <<; 2)/ * cache_off,WR_BUF上* /
#定义NCNB(0℃; 2)/ * cache_off,WR_BUF关* // * AP 2位* /
#定义AP_FAULT(0℃,小于10)/ *访问拒绝* /
的#define AP_SU_ONLY(1 <<;小于10)/ * RW仅ス* /
的#define AP_USR_RO(2';小于10)/ * SUP = RW,用户= RO * /
的#define AP_RW(3';小于10)/ *ス= RW,用户= RW * // *页目录1 AP0 * /
#定义AP0_SU_ONLY(1 LT;&4;)/ * RW苏只* /
的#define AP0_USR_RO(2'; 4;)/ * SUP = RW,用户= RO * /
的#define AP0_RW(3'; 4;)/ *ス= RW,用户= RW * // *页目录1 AP1 * /
#定义AP1_SU_ONLY(1 LT; 6;)/ * RW苏只* /
的#define AP1_USR_RO(2'; 6;)/ * SUP = RW,用户= RO * /
的#define AP1_RW(3'; 6;)/ *ス= RW,用户= RW * /
/ *页目录1 AP2 * /
#定义AP2_SU_ONLY(1 LT;&LT; 8)/ * RW苏只* /
#定义AP2_USR_RO(2';&LT; 8)/ * SUP = RW,用户= RO * /
#定义AP2_RW(3';&LT; 8)/ *苏= RW,用户= RW * // *页目录1 AP3 * /
的#define AP3_SU_ONLY(1 <<;小于10)/ * RW仅ス* /
的#define AP3_USR_RO(2';小于10)/ * SUP = RW,用户= RO * /
的#define AP3_RW(3';小于10)/ *ス= RW,用户= RW * /
#定义RAM_START(0x30000000)
#定义KERNEL_ENTRY(0x30300000)/ *银行6(3M)* /
#定义KERNEL_STACK(0x3001A000)/ *银行6(16K + 64K + 16K + 8K)(8K的内核堆栈)* /
#定义IRQ_STACK(0x3001B000)/ * 4K IRQ STACK * /
#定义KERNEL_IMG_SIZE(地址0x20000)#定义IRQ_STACK(0x3001B000)/ * 16K aignment * /
#定义TTB_BASE(0x30000000)
#定义PAGE_DIR0(TTB_BASE)
#定义TTB_FULL_SIZE(0x4000的)
#定义PAGE_DIR1(TTB_BASE + TTB_FULL_SIZE)#定义PAGE_DIR0_SIZE(0x4000的)/ * 16K * /
无效_do_page_fault(无效)
{
//
...........
//
//读取FSR和放大器;&安培;得到VADDR&放大器;&安培;在此输入
挥发性无符号* page_dir =(挥发性无符号*)(TTB_BASE);
无符号指数= VADDR&GT;&GT; 20,I = 0,J = 0;
签名页= 0; 如果((page_dir [指数]&放大器;!〜(0x3FF处)及及(类型== 0x0B中))){/ * page_dir空* /
I =指数和放大器; 〜0×03;
如果((page_dir第[i + 0]&放大器;〜(为0x3FF))||(page_dir第[i + 1]&放大器;〜(为0x3FF))
|| (page_dir第[i + 2]&放大器;〜(为0x3FF))|| (page_dir [1 + 3]安培;〜(0x3FF处)))
{
恐慌(页面dir是坏\\ n!); / * 4连续page_dir必须为0 * /
} 如果(!(页= find_free_page()))
恐慌(没有更多的免费网页\\ n!); / * ALLOC页页目录* / page_dir [I + 0] =(页+量0x000)| DOMAIN_USR | TTB0_COARSE; / *小页1 1KB * /
page_dir [I + 1] =(页+ 0x400的)| DOMAIN_USR | TTB0_COARSE; / *小页第2 1KB * /
page_dir第[i + 2] =(页+为0x800)| DOMAIN_USR | TTB0_COARSE; / *小页第3 1KB * /
page_dir [I + 3] =(页+量0xC00)| DOMAIN_USR | TTB0_COARSE; / *小页4 1KB * / 如果(!(页= find_free_page()))
恐慌(没有更多的免费网页\\ n!); / * ALLOC一个页的页表* / 挥发性无符号* page_tbl =(挥发性无符号*)(page_dir [指数]&放大器;〜(0x3FF处)); * page_tbl =页| AP0_RW | AP1_RW | AP2_RW | AP3_RW | NCNB | TTB1_SPG; / *较小的页面使用* /
invalidate_icache(); 对于(i = 0; I&LT; 64;我++)
{
为(J = 0; J&下; 8; J ++)
clr_invalidate_dcache((ⅰ&所述;&下; 26)|(J&所述;小于5));
}
invalidate_tlb();
}
........
// } / *这里是宏* / #定义invalidate_tlb()\\
{\\
__asm__ __volatile__(\\
MOV R0,#0 \\ N\\
MCR p15,0,R0,C8,c7,0 \\ N\\
:::R0\\
); \\
} #定义clr_invalidate_dcache(指数)\\
{\\
__asm__ __volatile__(\\
MCR p15,0,%[I],C7,c14,2 \\ N\\
:: [I]的r(指数)\\
); \\
}
#定义invalidate_icache()\\
{\\
__asm__ __volatile__(\\
MOV R0,#0 \\ N\\
MCR p15,0,R0,C7,c5,0 \\ N\\
:::R0\\
); \\
} #定义invalidate_dcache()\\
{\\
__asm__ __volatile__(\\
MOV R0,#0 \\ N\\
MCR p15,0,R0,C7,c6,0 \\ N\\
:::R0\\
); \\
} #定义invalidate_idcache()\\
{\\
__asm__ __volatile__(\\
MOV R0,#0 \\ N\\
MCR p15,0,R0,C7,c7,0 \\ N\\
:::R0\\
); \\
} \\
注意:我假设 TTB_BASE
是主ARM的 L1 的页表。如果是一些阴影,你需要表现出更多的code按 unixsmurf 。这是我最好的猜测...
<击>您 page_dir
既作为主的 L1 的条目,为的 L2 的细页表是否正常罢工>的 TTB_BASE
应该只包含部分,超的部分或指向子页表。你需要为的 L2 的页表分配更多的物理内存。
<击>我猜你的 page_dir [I + 0]
, page_dir [I + 1]
等被覆盖其他的 L1 的部分条目和击>您 invalidate_tlb()
使这个混凝土到CPU中。你应该使用的 L2 的 page_tbl
指针,设置/指数小/精细页面。也许你的内核code是在 3G-4G 的空间和你被过度写一些关键的 L1 的映射有;任何数量的奇怪的事情可能发生。目前使用 page_tbl
目前还不清楚。
<击>你不能的两用的主要的 L1 的表,因为他们更有意义的硬件。我想,这仅仅是一个错误?击>
有只有三种类型的条目在主要的 L1 的页表,
- 部分 - 1MB的内存映射直接向VIRT物理,没有2 第二页表
- 超级节 - 一个4MB的内存映射按节,但尽量减少TLB pressure 。
- A 2 第二页表。参赛作品可以是1K,4K和64K。 精细页表的是台面尺寸4K和不在现代ARM的设计。每个条目为细页表的地址1K。
主要的页表的必须上这是一个物理地址的 16K对齐,这也是它的大小。 16K /(4字节/输入)* 1MB
给出的 L1 的表的 4GB 地址范围。所以在初级每个条目的 L1 的页表总是指的 1MB 的条目。 粗页表是在仅在新的ARM的选项,他们指的是1K的 L2 的表。
1K_L2_size * 4K_entry /(4bytes_per_entry)给出了1MB的地址空间。
1K_L2_size * 64K_entry /(16bytes_per_entry)给出1MB地址空间。
有四个项的 1MB 每个初级的 L1 的一个超部分。有每四个条目为64K的大页在 L2 的表。如果你正在使用的超节的,你没有的 L2 的条目。
我想你可能会有的超级节的和的大页的混了?对于某些具有格式不正确的页表只能在清单中的 TLB无效的,所以从通过散步表的MMU映射重新获取。
最后,您应该刷新的ð缓存的和的漏的的的写缓冲的保证内存的一致性。你可能希望有一个的内存屏障的为好。
静态内嵌无效dcache_clean(无效)
{
const int的零= 0;
ASM挥发性(:::内存); /* 屏障 */
/ *清洁整个ð缓存 - &GT;推到外部存储器。 * /
ASM挥发性(1:MRC P15,0,R15,C7,C10,3 \\ n
BNE 1B \\ N:::CC);
/ *排空写缓冲器* /
ASM挥发性(MCR 15,0,0%,C7,C10,4::R(零));
}
有也被协处理器指令无效单一的TLB项,你只能改变一个部分的 VADDR / PADDR 的数据故障的映射。
另请参阅: ARM MMU教程,<一个HREF =http://www.heyrick.co.uk/assembler/memmult.html相对=nofollow>虚拟内存结构和您的 ARM体系结构参考手册。
I enabled MMU on my s3c2440 board (3G - 4G memory :: the fault attribute),everything was just fine when I didn't read/write 3G - 4G memory .So to test the page fault vector ,I wrote to a 0xFF to the 3G address,as I expected ,I got the right value from FSR ,So I did this in _do_page_fault (), the step was like this :
..... // set new page to translation table
.....
invlidate_icache (); // clear icache
clr_dcache (); // wb is used ,clear dcache
invalidate_ttb (); // invalidate translation table
and then ISR_dataabort returned ,I read the 3G address to get the 0xFF which I wote before . Unfortunately I got a data abort again .(I am sure the translation table value I set is ok)
So what is the correct way to update the MMU translation table .Any help is appreciate ! thanks
here is the Main code I used (just for some test), (I am a littlte strange to ARM ARCH,so this code might be urgly )
/* MMU TTB 0 BASE ATTR */
#define TTB0_FAULT (0|(1<<4)) /* TTB FAULT */
#define TTB0_COARSE (1|(1<<4)) /* COARSE PAGE BASE ADDR */
#define TTB0_SEG (2|(1<<4)) /* SEG BASE ADDR */
#define TTB0_FINE (3|(1<<4)) /* FINE PAGE BASE ADDR */
/* MMU TTB 1 BASE ATTR */
#define TTB1_FAULT (0)
#define TTB1_LPG (1) /* Large page */
#define TTB1_SPG (2) /* small page */
#define TTB1_TPG (3) /* tiny page */
/* domain access priority level */
#define FAULT_PL (0x0) /* domain fault */
#define USR_PL (0x1) /* usr mode */
#define RSV_PL (0x2) /* reserved */
#define SYS_PL (0x3) /* sys mode */
#define DOMAIN_FAULT (0x0<<5) /* fault 0*/
#define DOMAIN_SYS (0x1<<5) /* sys 1*/
#define DOMAIN_USR (0x2<<5) /* usr 2*/
/* C,B bit */
#define CB (3<<2) /* cache_on, write_back */
#define CNB (2<<2) /* cache_on, write_through */
#define NCB (1<<2) /* cache_off,WR_BUF on */
#define NCNB (0<<2) /* cache_off,WR_BUF off */
/* ap 2 bits */
#define AP_FAULT (0<<10) /* access deny */
#define AP_SU_ONLY (1<<10) /* rw su only */
#define AP_USR_RO (2<<10) /* sup=RW, user=RO */
#define AP_RW (3<<10) /* su=RW, user=RW */
/* page dir 1 ap0 */
#define AP0_SU_ONLY (1<<4) /* rw su only */
#define AP0_USR_RO (2<<4) /* sup=RW, user=RO */
#define AP0_RW (3<<4) /* su=RW, user=RW */
/* page dir 1 ap1 */
#define AP1_SU_ONLY (1<<6) /* rw su only */
#define AP1_USR_RO (2<<6) /* sup=RW, user=RO */
#define AP1_RW (3<<6) /* su=RW, user=RW */
/* page dir 1 ap2 */
#define AP2_SU_ONLY (1<<8) /* rw su only */
#define AP2_USR_RO (2<<8) /* sup=RW, user=RO */
#define AP2_RW (3<<8) /* su=RW, user=RW */
/* page dir 1 ap3 */
#define AP3_SU_ONLY (1<<10) /* rw su only */
#define AP3_USR_RO (2<<10) /* sup=RW, user=RO */
#define AP3_RW (3<<10) /* su=RW, user=RW */
#define RAM_START (0x30000000)
#define KERNEL_ENTRY (0x30300000) /* BANK 6 (3M) */
#define KERNEL_STACK (0x3001A000) /* BANK 6 (16K + 64K + 16K + 8K) (8k kernel stack) */
#define IRQ_STACK (0x3001B000) /* 4K IRQ STACK */
#define KERNEL_IMG_SIZE (0x20000)
#define IRQ_STACK (0x3001B000)
/* 16K aignment */
#define TTB_BASE (0x30000000)
#define PAGE_DIR0 (TTB_BASE)
#define TTB_FULL_SIZE (0x4000)
#define PAGE_DIR1 (TTB_BASE+TTB_FULL_SIZE)
#define PAGE_DIR0_SIZE (0x4000) /* 16k */
void _do_page_fault (void)
{
//
...........
//
// read the FSR && get the vaddr && type here
volatile unsigned *page_dir = (volatile unsigned*)(TTB_BASE);
unsigned index = vaddr >> 20,i = 0, j = 0;
unsigned page = 0;
if (!(page_dir[index] & ~(0x3FF) && (type == 0x0B))) { /* page_dir empty */
i = index & ~0x03;
if ( (page_dir[i+0] & ~(0x3FF)) || (page_dir [i+1] & ~(0x3FF))
|| (page_dir[i+2] & ~(0x3FF)) || (page_dir [i+3] & ~(0x3FF)) )
{
panic ( "page dir is bad !\n" ); /* 4 continuous page_dir must be 0 */
}
if (!(page = find_free_page ()))
panic ( "no more free page !\n" ); /* alloc a page page dir*/
page_dir[i+0] = (page + 0x000) | DOMAIN_USR | TTB0_COARSE ; /* small page 1st 1KB */
page_dir[i+1] = (page + 0x400) | DOMAIN_USR | TTB0_COARSE ; /* small page 2nd 1KB */
page_dir[i+2] = (page + 0x800) | DOMAIN_USR | TTB0_COARSE ; /* small page 3rd 1KB */
page_dir[i+3] = (page + 0xC00) | DOMAIN_USR | TTB0_COARSE ; /* small page 4th 1KB */
if (!(page = find_free_page ()))
panic ( "no more free page !\n" ); /* alloc a page page table*/
volatile unsigned *page_tbl = (volatile unsigned*) (page_dir[index] & ~(0x3FF));
*page_tbl = page|AP0_RW|AP1_RW|AP2_RW|AP3_RW| NCNB|TTB1_SPG;/* small page is used */
invalidate_icache ();
for (i = 0; i < 64; i++)
{
for (j = 0;j < 8;j ++)
clr_invalidate_dcache ( (i<<26)|(j<<5) );
}
invalidate_tlb ();
}
........
//
}
/* here is the macros */
#define invalidate_tlb() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c8,c7,0\n"\
:::"r0" \
);\
}
#define clr_invalidate_dcache(index) \
{\
__asm__ __volatile__ (\
"mcr p15,0,%[i],c7,c14,2\n"\
:: [i]"r"(index)\
);\
}
#define invalidate_icache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c5,0\n"\
::: "r0"\
);\
}
#define invalidate_dcache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c6,0\n"\
::: "r0"\
);\
}
#define invalidate_idcache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c7,0\n"\
:::"r0"\
);\
}\
Note: I assume TTB_BASE
is the primary ARM L1 page table. If it is some shadow, you need to show more code as per unixsmurf. Here is my best guess...
Your The page_dir
is functioning as both the primary L1 entries and as the L2 fine page table.TTB_BASE
should only contain sections, super sections or pointers to sub-page tables. You need to allocate more physical memory for the L2 page tables.
I guess your Your page_dir[i+0]
, page_dir[i+1]
, etc are overwriting other L1 section entries and invalidate_tlb()
make this concrete to the CPU. You should be using the L2 page_tbl
pointer to set/index the small/fine pages. Perhaps your kernel code is in the 3G-4G space and your are over-writing some critical L1 mappings there; any number of strange things could happen. The current use of page_tbl
is unclear.
You can not double use the primary L1 tables as they have meaning to hardware. I guess that this is just a mistake?
There are only three types of entries in the primary L1 page tables,
- Sections - a 1MB memory mapping straight virt to phys, with no 2nd page table.
- Super-sections - a 4MB memory mapping as per sections, but minimizing TLB pressure.
- A 2nd page table. Entries can be 1k,4k,and 64k. Fine page tables are 4k in table size and are not in modern ARM designs. Each entry is 1k of address in a fine page table.
The primary page table must be on a physical address that is 16k aligned, which is also it's size. 16k/(4bytes/entry)*1MB
gives a 4GB address range of the L1 tables. So each entry in the primary L1 page table always refers to a 1MB entry. Coarse page tables are the only option on newer ARMs and they refer to a 1K L2 table.
1K_L2_size * 4K_entry / (4bytes_per_entry) gives 1MB address space.
1K_L2_size * 64K_entry / (16bytes_per_entry) gives 1MB address space.
There are four entries of 1MB each in the primary L1 for a super-section. There are four entries each for a 64k large page in the L2 table. If you are using super-sections, you do not have L2 entries.
I think you may have Super-sections and large pages mixed up? For certain having improperly formatted page tables will only manifest on a TLB invalidate, so that MMU mappings are re-fetched from the tables via a walk.
Finally, you should flush the D cache and drain the write buffer to ensure consistency with memory. You may wish to have a memory barrier as well.
static inline void dcache_clean(void)
{
const int zero = 0;
asm volatile ("" ::: "memory"); /* barrier */
/* clean entire D cache -> push to external memory. */
asm volatile ("1: mrc p15, 0, r15, c7, c10, 3\n"
" bne 1b\n" ::: "cc");
/* drain the write buffer */
asm volatile ("mcr 15, 0, %0, c7, c10, 4"::"r" (zero));
}
There are also co-processor commands to invalidate single TLB entries as you are only changing a portion of the vaddr/paddr mappings in the data fault.
See also: ARM MMU tutorial, Virtual Memory structures, and your ARM Architecture Reference Manual.
这篇关于什么是更新MMU转换表的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!