2007/0910-2007/0914工作周记

原创 2007年09月16日 23:43:00

    经过上个星期的试验,我们觉得有必要关注Leon3MMU

    硬件背景:

    由于Leon3Sparc架构,所以我查看了Sparc V8手册。SparcMMU大体结构是将32位虚拟地址分为4个段。(这里贴图不能直接贴,好麻烦!)

    第一,二层为PTD:PTP为下一级页表的物理地址

    PTD:(Page Table Descriptor)

    |_________PTP_______________|__ET_|

    31                             2 1 0

    第三层为PTE:PPN36位物理地址的高24(Leon3PPN20)

    PTE:(Page Table Entry )

    |_________PPN___________|_________|

    31                       8 7        0

    最后的当然就是偏移地址了。

    具体的寻址过程和x86几乎一样,不过x86cr3就变为一个特定的上下文寄存器,这个寄存器标识当前进程。

    具体过程:

    上下文寄存器从上下文表中获得当前进程的标识Root PointerRoot Pointer指向当前进程的全局页表的物理地址PA,然后从PTD获得偏移量PTPPA+PTP就是第二层页表的物理地址,以此类推,直到第三层。从指向的PTE中获得PPN也就是物理地址的前20位,然后再加上OFFSET12位,最后得到相应的物理地址。

    看看头文件的定义是否和手册说的一样。

../include/asm-sparc/pgtsrmmu.h

/* Number of contexts is implementation-dependent; 64k is the most we support */

#define SRMMU_MAX_CONTEXTS 65536


/* PMD_SHIFT determines the size of the area a second-level page table entry can map */

#define SRMMU_REAL_PMD_SHIFT 18

#define SRMMU_REAL_PMD_SIZE (1UL << SRMMU_REAL_PMD_SHIFT)

#define SRMMU_REAL_PMD_MASK (~(SRMMU_REAL_PMD_SIZE-1))

#define SRMMU_REAL_PMD_ALIGN(__addr) (((__addr)+SRMMU_REAL_PMD_SIZE-1)&SRMMU_REAL_PMD_MASK)


/* PGDIR_SHIFT determines what a third-level page table entry can map */

#define SRMMU_PGDIR_SHIFT 24

#define SRMMU_PGDIR_SIZE (1UL << SRMMU_PGDIR_SHIFT)

#define SRMMU_PGDIR_MASK (~(SRMMU_PGDIR_SIZE-1))

#define SRMMU_PGDIR_ALIGN(addr) (((addr)+SRMMU_PGDIR_SIZE-1)&SRMMU_PGDIR_MASK)


#define SRMMU_REAL_PTRS_PER_PTE 64

#define SRMMU_REAL_PTRS_PER_PMD 64

#define SRMMU_PTRS_PER_PGD 256

    单从上面的定义,可以看出定义和手册说明是一样的。但是现实往往要想象的残酷,在头文件中作者(David Miller)提到了一个问题。原文是:

/*

* To support pagetables in highmem, Linux introduces APIs which

* return struct page* and generally manipulate page tables when

* they are not mapped into kernel space. Our hardware page tables

* are smaller than pages. We lump hardware tabes into big,

* page sized software tables.

*

* PMD_SHIFT determines the size of the area a second-level page

* table entry can map, and our pmd_t is 16 times larger than

* normal. The values which were once defined here are now

* generic for 4c and srmmu, so they're found in pgtable.h.

*/

    大概的意思就是为了映射到高位内存,内存的页表大小必须作出调整。但是很遗憾他没有继续说明如何调整,我也找不到其他的文档说明。这也直接导致了没办法看懂源代码的一些页表操作。

    后来也发现了另外一个问题,Linux2.6.11开始,页表映射统一形式为四层:

pgd->pud->pmd->ptd。这真的让我很诧异,因为从头文件的定义怎样都看不出是分四层,而我们的内核是2.6.21.1。这是否说明这个内核Sparc部分处理MMU是有一定问题的?那么多问题,看来也只能是阅读源代码来解决了。

    以我目前的水平,读内核源代码带来的痛苦远远比快乐要多。错综复杂的宏定义、言简意赅的内嵌汇编、微妙而陌生的Sparc架构都让我沮丧不已,放弃的念头曾经象春天的野草一样在我大脑疯长。不过老子说得好:合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。唉,以此自勉吧。

    我们主要关注的文件是../arch/sparc/srmmu.c。首先要认识一些类型转换宏(特别是:__pte__pgd __pgprot)把一个无符号整数转换成所需的类型。这里值得注意的是__pmd被注释了。

../include/asm/page.h

typedef struct { unsigned long pte; } pte_t;

typedef struct { unsigned long iopte; } iopte_t;

typedef struct { unsigned long pmdv[16]; } pmd_t;

typedef struct { unsigned long pgd; } pgd_t;

typedef struct { unsigned long ctxd; } ctxd_t;

typedef struct { unsigned long pgprot; } pgprot_t;

typedef struct { unsigned long iopgprot; } iopgprot_t;



#define __pte(x) ((pte_t) { (x) } )

#define __iopte(x) ((iopte_t) { (x) } )

/* #define __pmd(x) ((pmd_t) { (x) } ) */ /* XXX procedure with loop */

#define __pgd(x) ((pgd_t) { (x) } )

#define __ctxd(x) ((ctxd_t) { (x) } )

#define __pgprot(x) ((pgprot_t) { (x) } )

#define __iopgprot(x) ((iopgprot_t) { (x) } )

    另外的类型转换宏执行和以上宏相反的转换,即把上面提到的特殊的类型转换成一个无符号整数。

#define pte_val(x) ((x).pte)

#define iopte_val(x) ((x).iopte)

#define pmd_val(x) ((x).pmdv[0])

#define pgd_val(x) ((x).pgd)

#define ctxd_val(x) ((x).ctxd)

#define pgprot_val(x) ((x).pgprot)

#define iopgprot_val(x) ((x).iopgprot)

    下面还有很多操作页表的宏和函数。不过这些函数用得并不多,但是理解它们对我们理解Sparc架构很有好处。

../arch/sparc/mm/srmmu.c

    向一个pte页表写入指定的值。

static inline void srmmu_set_pte(pte_t *ptep, pte_t pteval)

{

    srmmu_swap((unsigned long *)ptep, pte_val(pteval));

}


static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)

{

    __asm__ __volatile__(

        "swap [%2], %0" :

        "=&r" (value) :

        "0" (value), "r" (addr));

        return value;

}

    汇编代码的意思也很简单,执行的语句就是Sparcswap汇编指令,%2就是指addr%0就是value。语句中的“0”也可以用r互换。

如果pte页表为0,返回1,否则为0

static inline int srmmu_pte_none(pte_t pte)

{

    return !(pte_val(pte) & 0xFFFFFFF);

}

    利用标志位,检测页面是否pte页面,是返回1,否则为0

static inline int srmmu_pte_present(pte_t pte)

{

    return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE);

}

    检测pte页指向的物理页是否有执行和读的权限。

static inline int srmmu_pte_read(pte_t pte)

{

    return !(pte_val(pte) & SRMMU_NOREAD);

}

    清除相应pte页表的一个表项,由此禁止进程使用由该页表 项映射的线性地址。

static inline void srmmu_pte_clear(pte_t *ptep)

{

    srmmu_set_pte(ptep, __pte(0));

}

    如果pmd表为0,返回1,否则为0

static inline int srmmu_pmd_none(pmd_t pmd)

{

    return !(pmd_val(pmd) & 0xFFFFFFF);

}

    利用标志位,检测页面是否pmd页面,是返回0,否则为1(yes is bad)

static inline int srmmu_pmd_bad(pmd_t pmd)

{

    return (pmd_val(pmd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }

    利用标志位,检测页面是否pmd页面,是返回1,否则为0

static inline int srmmu_pmd_present(pmd_t pmd)

{

    return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD);

}

    清除相应pmd页表的一个表项,由此禁止进程使用由该页表 项映射的线性地址。只是代码的for循环实现的有点莫名其妙。强制转换为pte_t类型是为了重用srmmu_set_pte 函数。

static inline void srmmu_pmd_clear(pmd_t *pmdp)

{

    int i;

    for (i = 0; i < PTRS_PER_PTE/SRMMU_REAL_PTRS_PER_PTE; i++)

    srmmu_set_pte((pte_t *)&pmdp->pmdv[i], __pte(0));

}

pgtable.h:

#define PTRS_PER_PTE 1024

#define PTE_SIZE (PTRS_PER_PTE*4)

pgtsrmmu.h:

typedef struct { unsigned long pmdv[16]; } pmd_t;(page.h)

#define SRMMU_REAL_PTRS_PER_PTE 64

#define SRMMU_REAL_PTE_TABLE_SIZE (SRMMU_REAL_PTRS_PER_PTE*4)


    如果pgd表为0,返回1,否则为0

static inline int srmmu_pgd_none(pgd_t pgd)

{

    return !(pgd_val(pgd) & 0xFFFFFFF);

}

    利用标志位,检测页面是否pgd页面,是返回0,否则为1(yes is bad)

static inline int srmmu_pgd_bad(pgd_t pgd)

{

    return (pgd_val(pgd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }

    利用标志位,检测页面是否pmd页面,是返回1,否则为0

static inline int srmmu_pgd_present(pgd_t pgd)

{

    return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD);

}

    清除相应pgd页表的一个表项,由此禁止进程使用由该页表 项映射的线性地址。

static inline void srmmu_pgd_clear(pgd_t * pgdp)

{

    srmmu_set_pte((pte_t *)pgdp, __pte(0));

}

page.h:

typedef struct { unsigned long pgd; } pgd_t;

    清除pte的读写标志。

static inline pte_t srmmu_pte_wrprotect(pte_t pte)

{

    return __pte(pte_val(pte) & ~SRMMU_WRITE);

}

    清除ptedirty位。

static inline pte_t srmmu_pte_mkclean(pte_t pte)

{

    return __pte(pte_val(pte) & ~SRMMU_DIRTY);

}

    清除pteReferenced位,意味着把此页标志为未访问。

static inline pte_t srmmu_pte_mkold(pte_t pte)

{

    return __pte(pte_val(pte) & ~SRMMU_REF);

}

    设置pte的读写位。

static inline pte_t srmmu_pte_mkwrite(pte_t pte)

{

    return __pte(pte_val(pte) | SRMMU_WRITE);

}

    设置ptedirty位。

static inline pte_t srmmu_pte_mkdirty(pte_t pte)

{

    return __pte(pte_val(pte) | SRMMU_DIRTY);

}

    设置pteReferenced位,意味着把此页标志为访问过。

static inline pte_t srmmu_pte_mkyoung(pte_t pte)

{

    return __pte(pte_val(pte) | SRMMU_REF);

}



求职两周记

求职两周记我是一个小测试,最近两周一直在找工作,还好最近有了些眉目。在此写些自己的经历、看法,欢迎拍砖。自我的一个定位我给自己定位是中级测试,这个定位本身也没有什么意义,只是为了让大家能大概明白我处于...
  • gaogaorimu
  • gaogaorimu
  • 2016年05月24日 20:22
  • 769

2007/0903-2007/0907工作周记

    这个星期的工作计划主要是使linux2.6能在开发板上跑起来。这次就不加-u参数,而是用minicon观察,结果是大大出乎我的意料,minicom只打印...
  • BtInside
  • BtInside
  • 2007年09月09日 13:43
  • 762

2007/0827-2007/0831工作周记

    首先说明我从事的是嵌入式行业,所以以后的linux文章都是嵌入式相关的,除非有特别说明。     我用的芯片是欧洲Gaisler Research公司...
  • BtInside
  • BtInside
  • 2007年09月09日 13:33
  • 2562

工作周记

1、调试Java Web程序在servlet或业务逻辑Impl.java里
  • sz312802102
  • sz312802102
  • 2014年07月29日 19:17
  • 181

USACO2007 Open Cheappal

USACO 2007Open Cheappal 最优回文
  • Kanosword
  • Kanosword
  • 2016年09月13日 14:46
  • 400

【USACO】2007 Feb Silver Lilypad Pond 白银莲花池

Silver Lilypad Pond 白银莲花池 Description 为了让奶牛们娱乐和锻炼,约翰建造了一个美丽的池塘。这个池塘是矩形的,可以分成 M×N个方格。一些格子是坚固得令人惊讶的莲花,...
  • Bobby_Z
  • Bobby_Z
  • 2016年11月07日 23:59
  • 170

Web前端学习周记7.23

这一周下来。其实浪费的时间挺多的,在极客学院的课程进度也进行的很慢,一个简单的任务十二,我硬是写了5天才弄完,主要是了解了点javascript的设计模式~学了两个,一个是单例模式,一个是工厂模式,其...
  • u013637066
  • u013637066
  • 2017年07月23日 20:42
  • 282

工作周记(2015.9.20回来了)

从去年毕业以来,好久没有使用博客了。一方面工作太忙了,另一方面是工作和这个相关性不大。工作一年多以来,感觉过的算是浑浑噩噩的。没有很多的总结和输出,没有学习额外的新知识,感觉只限于工作。工作中的苦恼和...
  • wangshouchao
  • wangshouchao
  • 2015年09月20日 23:27
  • 616

0914

1. js 2.过查询 3.供应商查询 1.coffeescript 2.rails
  • legend_x
  • legend_x
  • 2013年09月14日 11:11
  • 434

BZOJ 1646: [Usaco2007 Open]Catch That Cow 抓住那只牛

宽搜 不解释
  • bababaab
  • bababaab
  • 2016年07月25日 14:44
  • 287
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:2007/0910-2007/0914工作周记
举报原因:
原因补充:

(最多只允许输入30个字)