2009年4月25日星期六

ARM Linux的一些片段(一)

因为工作原因,对这个部分有接触。平时看代码和一些文档的细节感觉过段时间就忘记了。所以要做个记录。可能其中还有错误。吧一个对人很有启发的记录下来。不过这些内容很零散,平时也是需要才看。以下说的arm是支持mmu的arm9以上.

Linux支持三级页表,作为其默认的页表结构。ARM是两极页表。PGD和PTE。从pgtable.h里面可以可以看出一个work around的实现。PGD和PTE并不是直接对应ARM硬件的页表目录项。而是做了一些为了linux上层的要求的一个方案。首先,他把4096个pgd项变成2048个,物理上还是一个pgd指向一个256个pte项的数组的,这没办法改。但是pgd指针逻辑上合并成一个,各自指向的的pte数组也合并。并且是连续的。

   pgd             pte
 57  * |        |
 58  * +--------+ +0
 59  * |        |-----> +------------+ +0
 60  * +- - - - + +4    |  h/w pt 0  |
 61  * |        |-----> +------------+ +1024
 62  * +--------+ +8    |  h/w pt 1  |
 63  * |        |       +------------+ +2048
 64  * +- - - - +       | Linux pt 0 |
 65  * |        |       +------------+ +3072
 66  * +--------+       | Linux pt 1 |
 67  * |        |       +------------+ +4096

以上内容摘自Linux ARM pgtable.h, GPL.
这512个pte项合并起来,这个pte分配的页(一般linux需要一个pte表在一个页里,代码注释也写了)还剩下一半的内容,刚好可以存放arm不支持的一些标记(Linux pt 0, 1),而这些标记是linux必须的,比如dirty。这个方案还非常具有可扩展性,不依赖arm本身的标记。dirty标记的实现是通过对arm支持的权限fault的中断来写这个标记。这样方式是相当于一种模拟。

对于不同cpu版本,set_pte的实现是用宏##拼的函数,这些函数不再arch/arm/kernel而是arch/arm/mm里面,找个v6的实现里面可以看到实现:(这段代码上面的一段注释不知道是不是写错了, ptep - pointer to level 2 translation table entry (hardware version is stored at -1024 bytes),明明是2048个字节,代码也是,也许是我理解错)
这里就看到先偏移2048个字节找到“真的”pte做事。
ENTRY(cpu_v6_set_pte)
154         str     r1, [r0], #-2048                @ linux version
155 
156         bic     r2, r1, #0x000003f0
157         bic     r2, r2, #0x00000003
158         orr     r2, r2, #PTE_EXT_AP0 | 2
...
            tst     r1, #L_PTE_PRESENT
176         moveq   r2, #0
177 
178         str     r2, [r0]
179         mcr     p15, 0, r0, c7, c10, 1 @ flush_pte
180         mov     pc, lr
关于ARM的模式,arm本来有7个模式,而linux只有两个user和kernel。于是Linux的实现就把包括中断处理模式页转到同一个模式下做事,就是arm的所谓超级用户(svc)模式,实现这个的办法就是改写备份寄存器spsr为svc的模式,然后movs pc到svc模式,然后再调用处理函数,这样除了用户模式其他的模式执行环境都是同一个模式了。
感觉选择svc模式作为kernel也是很自然的,swi一看就是用来做系统调用的,直接进入svc模式。
这段代码是在宏里面,然后根据不同模式进行展开在头部。
.macro  vector_stub, name, mode, correction=0
885         .align  5
886 
887 vector_\name:
888         .if \correction
889         sub     lr, lr, #\correction
890         .endif
891 
892         @
893         @ Save r0, lr_ (parent PC) and spsr_
894         @ (parent CPSR)
895         @
896         stmia   sp, {r0, lr}            @ save r0, lr
897         mrs     lr, spsr
898         str     lr, [sp, #8]            @ save spsr
899 
900         @
901         @ Prepare for SVC32 mode.  IRQs remain disabled.
902         @
903         mrs     r0, cpsr
904         eor     r0, r0, #(\mode ^ SVC_MODE)
905         msr     spsr_cxsf, r0
906 
907         @
908         @ the branch table must immediately follow this code
909         @
910         and     lr, lr, #0x0f
911         mov     r0, sp
912         ldr     lr, [pc, lr, lsl #2]
913         movs    pc, lr                  @ branch to handler in SVC mode
914         .endm
另外,对于不支持高向量的arm,因为中断向量表从0开始,所以需要把第一页保留,不作为用户空间。

0 COMMENTS: