1. 概要

  • 本次实践,基于Ubuntu16.04,x86架构。
  • 分析64位Linux系统虚拟地址转物理地址的原理。
  • 将全程记录虚拟地址换物理地址的全过程。

2. 概念介绍

2.1 段概念的简单回顾

ioremap虚拟地址转物理地址_操作系统

  • 在进程中我们不直接对物理内存进行操作,CPU在运行时需要通过mmu转换后才可以真正的访问到物理内存。
  • 地址转换的过程分为两块分段和分页,分段机制简单的来说就是将进程的代码数据栈存在不同的虚拟地址段上,从而避免进程之间的互相影响。
  • 分段之前的地址我们称为逻辑地址,它由两部分组成高位的段选择符和低位的段内偏移。
  • 在分段过程中我们先用段选择符在全局段描述表中找到相应的段描述符,也就是某个段的基地址,在加上段内偏移,就得到了对应的线性地址。线性地址也称为虚拟地址。
  • Linux为了可移植性,并没有完整的使用段机制,linux让所有段都指向相同的段范围,段的基地址都为0。这样逻辑地址和线性地址在数值上就相等了。

2.2 分页机制简单回顾

ioremap虚拟地址转物理地址_ioremap虚拟地址转物理地址_02

  • Linux为了兼容32为和64为系统,需要一个统一的页表模型,目前常用的就是这个四级页表模型。
  • 分别是:





  • 四级页表结构并不是每个页表都要启用,对于32位系统中2级页表就足够了。在寻址的过程中直接将PUD,PMD置为0,从根本上取消了这两个字段,但这两个页目录在指针序列中的位置任然被保留了下来,在寻址过程中不能跳过这PUD,PMD进行寻页。内核会将PUD,PMD的表项都置为1。
  • 对于64位线性地址来说,由于64位硬件的限制,它的地址线只有48条,所以线性地址实际使用的也就是48位,在64位Linux中使用了4级页表的结构,它的线性地址划分,如下图:
  • ioremap虚拟地址转物理地址_linux_03

  • 其中PGD,PUD,PMD,PTE分别占了9位,而页内偏移占12位,共计48位,剩下的高16位保留下来,作为一个扩展。
  • 这种情况下,每个页面为4Kb,页表项大小为8比特,整个页表可以映射的空间是(4Kb/8b)4 * 4Kb = 256TB。
  • 而新的Intel,mmu硬件规定了5级页表,所以在4.15的内核中,Linux已经在PGD与PUD之间增加了一个页表目录叫P4D,但目前P4D还没有被启动,P4D的页表项为1,但寻页时依旧不能跳过P4D进行寻址。
  • 上图我们还看到了一个重要的寄存器CR3寄存器,用于保存当前进程页全局目录的地址的。
  • 寻页的开始就是从CR3开始的。而页全局目录的地址又在哪呢?
  • 内核在创建一个进程时,就会为其分配也全局目录。在进程描述符task_struct结构中有一个指向mm_struct结构的指针mm,而mm_struct结构就是为了来描述进程的虚拟地址空间的,在mm_struct结构中有一个指针pgd就是用来保存当前进程,页全局目录的地址的。所以在进程切换时操作系统就会将指针pgd的地址赋值给CR3寄存器,就完成了页表的切换。(路径include/linux/sched.h & include/linux/mm_types.h)

3. 测试代码

3.1 paging_lowmem.c

  • paging_lowmem.c的主要作用是在内核中先申请一个页面,使用内核提供的函数,按照寻页的步骤一步步的找到物理地址。这些步骤就相当于我们手动的模拟了mmu的寻页过程。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/export.h>

static unsigned long cr0, cr3;

static unsigned long vaddr = 0;

/*打印页中一些重要参数*/
static void get_pgtable_macro(void)
{
        cr0 = read_cr0();/*读取cr0的值*/
        cr3 = read_cr3_pa();/*读取cr3的值*/

        printk("cr0 = 0x%lx, cr3 = 0x%lx\n", cr0, cr3);
		/* *_SHIFT宏表示在线性地址中相应字段所能映射地址大小的对数
		比如PAGE_SHIFT,我们知道页面就是page_size=4K,PAGE_SHIFT=log2(page_size)=12
		*/
        printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
        printk("P4D_SHIFT   = %d\n", P4D_SHIFT);
        printk("PUD_SHIFT   = %d\n", PUD_SHIFT);
        printk("PMD_SHIFT   = %d\n", PMD_SHIFT);
        printk("PAGE_SHIFT  = %d\n", PAGE_SHIFT);
		/*PTRS_PER_*宏表示该页目录表中项的个数*/
        printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
        printk("PTRS_PER_P4D = %d\n", PTRS_PER_P4D);
        printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
        printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
        printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);
        /*PAGE_MASK页内偏移*/
        printk("PAGE_MASK    = 0x%lx\n", PAGE_MASK);
}

static unsigned long vaddr2padder(unsigned long vaddr)
{
        pgd_t *pgd;
        p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *pte;
        unsigned long paddr = 0;
        unsigned long page_addr = 0;
        unsigned long page_offset = 0;
        /*current->mm:当前进程的mm_struct结构
		  vaddr虚拟地址
		  return 该虚拟地址对应的pgd地址
		  pgd_offset展开后:
		  pgd = current->mm->pgd + (vaddr >> PGDDIR_SHIFT) & (PTRS_PER_PGD -1)
		*/
        pgd = pgd_offset(current->mm, vaddr);
        /*pgd_val(*pgd)打印pgd地址的值
		  pgd_index(vaddr)打印vaddr在页全局目录的索引
		*/
        printk("pgd_val = 0x%lx, pgd_index = %lu\n", pgd_val(*pgd), pgd_index(vaddr));
        if (pgd_none(*pgd)) {
                printk("not mapped in pgd\n");
                return -1;
        }
		/*p4d由于没有启用,所以目录表项为1,即p4d=pgd*/
        p4d = p4d_offset(pgd, vaddr);
        printk("p4d_val = 0x%lx, p4d_index = %lu\n", p4d_val(*p4d), p4d_index(vaddr));
        if (p4d_none(*p4d)) {
                printk("not mapped in p4d\n");
                return -1;
        }

		/*
		pud = *pgd & PAGE_MASK + (vaddr >> PUD_SHIFT) & (PTRS_PER_PUD -1) * sizeof(pud_t);
		*/
        pud = pud_offset(p4d, vaddr);
        printk("pud_val = 0x%lx, pud_index = %lu\n", pud_val(*pud), pud_index(vaddr));
        if (pud_none(*pud)) {
                printk("not mapped in pud\n");
                return -1;
        }
		/*
		pmd = *pud & PAGE_MASK + (vaddr >> PMD_SHIFT) & 		(PTRS_PER_PMD -1) * sizeof(pmd_t);
		*/
        pmd = pmd_offset(pud, vaddr);
        printk("pmd_val = 0x%lx, pmd_index = %lu\n", pmd_val(*pmd), pmd_index(vaddr));
        if (pmd_none(*pmd)) {
                printk("not mapped in pmd\n");
                return -1;
        }
		/*
		pmd = *pmd & PAGE_MASK + (vaddr >> PAGE_SHIFT) & 		(PTRS_PER_PTE -1) * sizeof(pte_t);
		*/
        pte = pte_offset_kernel(pmd, vaddr);
        printk("pte_val = 0x%lx, pte_index = %lu\n", pte_val(*pte), pte_index(vaddr));
        if (pte_none(*pte)) {
                printk("not mapped in pte\n");
                return -1;
        }
		/*pte地址的值加上页内偏移,就是物理地址*/
        page_addr = pte_val(*pte) & PAGE_MASK;
        page_offset = vaddr & ~PAGE_MASK;
        paddr = page_addr | page_offset;
        printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
        printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);
        return paddr;
}

static int __init v2p_init(void)
{
        unsigned long vaddr = 0;
        printk("vaddr to paddr module is running..\n");
        get_pgtable_macro();
        printk("\n");
        /*申请一个缺页的页面*/
        vaddr = __get_free_page(GFP_KERNEL);
        if (vaddr == 0) {
                printk("__get_free_page failed..\n");
                return -1;
        }
        /*往虚拟地址写hello world用于验证逻辑*/
        sprintf((char *)vaddr, "hello world from kernel");
        printk("get_page_vaddr=0x%lx\n", vaddr);
        vaddr2padder(vaddr);
        return 0;
}

static void __exit v2p_exit(void)
{
        printk("vaddr to paddr module is leaving..\n");
        free_page(vaddr);
}

module_init(v2p_init);
module_exit(v2p_exit);
/*模块的许可证声明GPL*/
MODULE_LICENSE("GPL");

3.2 dram.c

  • 旧金山大学高级编程课上找的小工具,主要是通过mmap将物理内存的数据映射到一个设备文件中。
  • 通过访问该设备就可以实现访问物理内存的功能。
//-------------------------------------------------------------------
//      dram.c
//
//      This module implements a Linux character-mode device-driver
//      for the processor's installed physical memory.  It utilizes
//      the kernel's 'kmap()' function, as a uniform way to provide
//      access to all the memory-zones (including the "high memory"
//      on systems with more than 896MB of installed physical ram).
//      The access here is 'read-only' because we deem it too risky
//      to the stable functioning of our system to allow every user
//      the unrestricted ability to arbitrarily modify memory-areas
//      which might contain some "critical" kernel data-structures.
//      We implement an 'llseek()' method so that users can readily
//      find out how much physical processor-memory is installed.
//
//      NOTE: Developed and tested with Linux kernel version 2.6.10
//
//      programmer: ALLAN CRUSE
//      written on: 30 JAN 2005
//      revised on: 28 JAN 2008 -- for Linux kernel version 2.6.22.5
//      revised on: 06 FEB 2008 -- for machines having 4GB of memory
//-------------------------------------------------------------------

#include <linux/module.h>       // for module_init()
#include <linux/highmem.h>      // for kmap(), kunmap()
#include <asm/uaccess.h>        // for copy_to_user()

char modname[] = "dram";        // for displaying driver's name
int my_major = 85;              // note static major assignment
unsigned long   dram_size;              // total bytes of system memory

loff_t my_llseek( struct file *file, loff_t offset, int whence );
ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos );

struct file_operations
my_fops =       {
                owner:          THIS_MODULE,
                llseek:         my_llseek,
                read:           my_read,
                };

static int __init dram_init( void )
{
        printk( "<1>\nInstalling \'%s\' module ", modname );
        printk( "(major=%d)\n", my_major );

        dram_size = (unsigned long)get_num_physpages() << PAGE_SHIFT;
		/* dram_size = 0x25f5ffff8; */
        printk( "<1>  ramtop=%08lX (%lu MB)\n", dram_size, dram_size >> 20 );
        return  register_chrdev( my_major, modname, &my_fops );
}

static void __exit dram_exit( void )
{
        unregister_chrdev( my_major, modname );
        printk( "<1>Removing \'%s\' module\n", modname );
}

ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos )
{
        struct page     *pp;
        void            *from;
        int             page_number, page_indent, more;

        // we cannot read beyond the end-of-file
        if ( *pos >= dram_size ) return 0;

        // determine which physical page to temporarily map
        // and how far into that page to begin reading from
        page_number = *pos / PAGE_SIZE;
        page_indent = *pos % PAGE_SIZE;

        // map the designated physical page into kernel space
        /*If kerel vesion is 2.6.32 or later, please use pfn_to_page() to get page, and include
            asm-generic/memory_model.h*/
        pp = pfn_to_page( page_number);
        //pp = &mem_map[ page_number ];

        from = kmap( pp ) + page_indent;

        // cannot reliably read beyond the end of this mapped page
        if ( page_indent + count > PAGE_SIZE ) count = PAGE_SIZE - page_indent;

        // now transfer count bytes from mapped page to user-supplied buffer
        more = copy_to_user( buf, from, count );

        // ok now to discard the temporary page mapping
        kunmap( pp );

        // an error occurred if less than count bytes got copied
        if ( more < count) return -EFAULT;

        // otherwise advance file-pointer and report number of bytes read
        *pos += count;
        return  count;
}

loff_t my_llseek( struct file *file, loff_t offset, int whence )
{
        unsigned long   newpos = -1;

        switch( whence )
                {
                case 0: newpos = offset; break;                 // SEEK_SET
                case 1: newpos = file->f_pos + offset; break;   // SEEK_CUR
                case 2: newpos = dram_size + offset; break;     // SEEK_END
                }

        if (( newpos < 0 )||( newpos > dram_size )) return -EINVAL;
        file->f_pos = newpos;

        return  newpos;
}

MODULE_LICENSE("GPL");
module_init( dram_init );
module_exit( dram_exit );

3.3 fileview.c

  • 主要功能,能让我们用想要的格式阅读二进制文件。
//----------------------------------------------------------------
//      fileview.cpp
//
//      This program displays the contents of a specified file
//      in hexadecimal and ascii formats (including any device
//      special files representing storage media).  A user may
//      navigate the file's contents using arrow-key commands,
//      or may adjust the format of the hexadecimal display to
//      select from among five data-sizes: byte (B), word (W),
//      doubleword (D), quadword (Q) or octaword (O).  It also
//      is possible to seek to a specified position within the
//      file by hitting the <ENTER>-key and then typing in the
//      desired (hexadecimal) address.  Type <ESCAPE> to quit.
//
//             compile-and-link using: $ make fileview
//
//      programmer: ALLAN CRUSE
//      written on: 26 OCT 2002
//      revised on: 07 JUN 2006 -- removed reliance on 'ncurses'
//----------------------------------------------------------------

#include <stdio.h>      // for printf(), perror(), fflush()
#include <fcntl.h>      // for open()
#include <string.h>     // for strncpy()
#include <unistd.h>     // for read(), lseek64()
#include <stdlib.h>     // for exit()
#include <termios.h>    // for tcgetattr(), tcsetattr()

#define MAXNAME 80
#define BUFHIGH 16
#define BUFWIDE 16
#define BUFSIZE 256
#define ROW     6
#define COL     2

#define KB_SEEK 0x0000000A
#define KB_QUIT 0x0000001B
#define KB_BACK 0x0000007F
#define KB_HOME 0x00315B1B
#define KB_LNUP 0x00415B1B
#define KB_PGUP 0x00355B1B
#define KB_LEFT 0x00445B1B
#define KB_RGHT 0x00435B1B
#define KB_LNDN 0x00425B1B
#define KB_PGDN 0x00365B1B
#define KB_END  0x00345B1B
#define KB_DEL  0x00335B1B


char progname[] = "FILEVIEW";
char filename[ MAXNAME + 1 ];
char buffer[ BUFSIZE ];
char outline[ 80 ];

int main( int argc, char *argv[] )
{
        // setup the filename (if supplied), else terminate
        if ( argc > 1 ) strncpy( filename, argv[1], MAXNAME );
        else { fprintf( stderr, "argument needed\n" ); exit(1); }

        // open the file for reading
        int     fd = open( filename, O_RDONLY );
        if ( fd < 0 ) { perror( filename ); exit(1); }

        // obtain the filesize (if possible)
        unsigned long long      filesize = lseek64( fd, 0LL, SEEK_END );

        if ( filesize < 0LL )
                {
                fprintf( stderr, "cannot locate \'end-of-file\' \n" );
                exit(1);
                }

        long long       incmin = ( 1LL <<  8 );
        long long       incmax = ( 1LL << 36 );
        long long       posmin = 0LL;
        long long       posmax = (filesize - 241LL)&~0xF;
        if ( posmax < posmin ) posmax = posmin;

        // initiate noncanonical terminal input
        struct termios  tty_orig;
        tcgetattr( STDIN_FILENO, &tty_orig );
        struct termios  tty_work = tty_orig;
        tty_work.c_lflag &= ~( ECHO | ICANON );  // | ISIG );
        tty_work.c_cc[ VMIN ]  = 1;
        tty_work.c_cc[ VTIME ] = 0;
        tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_work );
        printf( "\e[H\e[J" );

        // display the legend
        int     i, j, k;
        k = (77 - strlen( progname ))/2;
        printf( "\e[%d;%dH %s ", 1, k, progname );
        k = (77 - strlen( filename ))/2;
        printf( "\e[%d;%dH\'%s\'", 3, k, filename );
        char    infomsg[ 80 ];
        sprintf( infomsg, "filesize: %llu (=0x%013llX)", filesize, filesize );
        k = (78 - strlen( infomsg ));
        printf( "\e[%d;%dH%s", 24, k, infomsg );
        fflush( stdout );

        // main loop to navigate the file
        long long       pageincr = incmin;
        long long       lineincr = 16LL;
        long long       position = 0LL;
        long long       location = 0LL;
        int             format = 1;
        int             done = 0;
        while ( !done )
                {
                // erase prior buffer contents
                for (j = 0; j < BUFSIZE; j++) buffer[ j ] = ~0;

                // restore 'pageincr' to prescribed bounds
                if ( pageincr == 0LL ) pageincr = incmax;
                else if ( pageincr < incmin ) pageincr = incmin;
                else if ( pageincr > incmax ) pageincr = incmax;

                // get current location of file-pointer position
                location = lseek64( fd, position, SEEK_SET );

                // try to fill 'buffer[]' with data from the file
                char    *where = buffer;
                int     to_read = BUFSIZE;
                while ( to_read > 0 )
                        {
                        int     nbytes = read( fd, where, to_read );
                        if ( nbytes <= 0 ) break;
                        to_read -= nbytes;
                        where += nbytes;
                        }
                int     datalen = BUFSIZE - to_read;

                // display the data just read into the 'buffer[]' array
                unsigned char           *bp;
                unsigned short          *wp;
                unsigned int            *dp;
                unsigned long long      *qp;
                for (i = 0; i < BUFHIGH; i++)
                        {
                        int     linelen;

                        // draw the line-location (13-digit hexadecimal)
                        linelen = sprintf( outline, "%013llX ", location );

                        // draw the line in the selected hexadecimal format
                        switch ( format )
                                {
                                case 1: // 'byte' format
                                bp = (unsigned char*)&buffer[ i*BUFWIDE ];
                                for (j = 0; j < BUFWIDE; j++)
                                        linelen += sprintf( outline+linelen,
                                                "%02X ", bp[j] );
                                break;

                                case 2: // 'word' format
                                wp = (unsigned short*)&buffer[ i*BUFWIDE ];
                                for (j = 0; j < BUFWIDE/2; j++)
                                        linelen += sprintf( outline+linelen,
                                                " %04X ", wp[j] );
                                break;

                                case 4: // 'dword' format
                                dp = (unsigned int*)&buffer[ i*BUFWIDE ];
                                for (j = 0; j < BUFWIDE/4; j++)
                                        linelen += sprintf( outline+linelen,
                                                "  %08X  ", dp[j] );
                                break;

                                case 8: // 'qword' format
                                qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
                                for (j = 0; j < BUFWIDE/8; j++)
                                        linelen += sprintf( outline+linelen,
                                                "    %016llX    ", qp[j] );
                                break;

                                case 16: // 'octaword'
                                qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
                                linelen += sprintf( outline+linelen, "     " );
                                linelen += sprintf( outline+linelen,
                                        "   %016llX%016llX   ", qp[1], qp[0] );
                                linelen += sprintf( outline+linelen, "     " );
                                break;
                                }

                        // draw the line in ascii format
                        for (j = 0; j < BUFWIDE; j++)
                                {
                                char    ch = buffer[ i*BUFWIDE + j ];
                                if (( ch < 0x20 )||( ch > 0x7E )) ch = '.';
                                linelen += sprintf( outline+linelen, "%c", ch);
                                }

                        // transfer this output-line to the screen
                        printf( "\e[%d;%dH%s", ROW+i, COL, outline );

                        // advance 'location' for the next output-line
                        location += BUFWIDE;
                        }
                printf( "\e[%d;%dH", 23, COL );
                fflush( stdout );

                // await keypress
                long long       inch = 0LL;
                read( STDIN_FILENO, &inch, sizeof( inch ) );
                printf( "\e[%d;%dH%60s", 23, COL, " " );

                // interpret navigation or formatting command
                inch &= 0x00FFFFFFLL;
                switch ( inch )
                        {
                        // move to the file's beginning/ending
                        case 'H': case 'h':
                        case KB_HOME:   position = posmin; break;
                        case 'E': case 'e':
                        case KB_END:    position = posmax; break;

                        // move forward/backward by one line
                        case KB_LNDN:   position += BUFWIDE; break;
                        case KB_LNUP:   position -= BUFWIDE; break;

                        // move forward/packward by one page
                        case KB_PGDN:   position += pageincr; break;
                        case KB_PGUP:   position -= pageincr; break;

                        // increase/decrease the page-size increment
                        case KB_RGHT:   pageincr >>= 4; break;
                        case KB_LEFT:   pageincr <<= 4; break;

                        // reset the hexadecimal output-format
                        case 'B': case 'b':     format = 1; break;
                        case 'W': case 'w':     format = 2; break;
                        case 'D': case 'd':     format = 4; break;
                        case 'Q': case 'q':     format = 8; break;
                        case 'O': case 'o':     format = 16; break;

                        // seek to a user-specified file-position
                        case KB_SEEK:
                        printf( "\e[%d;%dHAddress: ", 23, COL );
                        fflush( stdout );
                        {
                        char    inbuf[ 16 ] = {0};
                                //tcsetattr( STDIN_FILENO, TCSANOW, &tty_orig );
                        int     i = 0;
                        while ( i < 15 )
                                {
                                long long       ch = 0;
                                read( STDIN_FILENO, &ch, sizeof( ch ) );
                                ch &= 0xFFFFFF;
                                if ( ch == '\n' ) break;
                                if ( ch == KB_QUIT ) { inbuf[0] = 0; break; }
                                if ( ch == KB_LEFT ) ch = KB_BACK;
                                if ( ch == KB_DEL ) ch = KB_BACK;
                                if (( ch == KB_BACK )&&( i > 0 ))
                                        {
                                        inbuf[--i] = 0;
                                        printf( "\b \b" );
                                        fflush( stdout );
                                        }
                                if (( ch < 0x20 )||( ch > 0x7E )) continue;
                                inbuf[ i++ ] = ch;
                                printf( "%c", ch );
                                fflush( stdout );
                                }
                        printf( "\e[%d;%dH%70s", 23, COL, " " );
                        fflush( stdout );
                        position = strtoull( inbuf, NULL, 16 );
                        position &= ~0xFLL;     // paragraph align
                        }
                        break;

                        // program termination
                        case KB_QUIT:   done = 1; break;

                        default:
                        printf( "\e[%d;%dHHit <ESC> to quit", 23, 2 );
                        }
                fflush( stdout );

                // insure that 'position' remains within bounds
                if ( position < posmin ) position = posmin;
                if ( position > posmax ) position = posmax;
                }

        // restore canonical terminal behavior
        tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_orig );
        printf( "\e[%d;%dH\e[0J\n", 23, 0 );
}

3.4 Makefile

obj-m:=paging_lowmem.o dram.o                 #产生1_hello模块的目标文件
CURRENT_PATH := $(shell pwd)            #模块所在的当前路径
LINUX_KERNEL := $(shell uname -r)       #Linux内核源码代码的当前版本
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)     #Linux内核源代码的绝对路径
CROSS_COMPILE := /usr/bin

# $(CROSS_COMPILE)gcc $(MODULES)_app.c -o $(MODULES)_app
all:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules  #编译模块

clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean    #清理模块

4. 测试与验证

4.1 编译模块与应用

make clean && make -j
gcc -o fileview.cpp fileview

4.2 加载模块

sudo insmod dram.ko
sudo insmod paging_lowmem.ko
sudo mknod /dev/dram c 85 0

4.3 打印log

  • 输入dmesg,查看内核log
[17858.497003] <1>
               Installing 'dram' module
[17858.497005] (major=85)
[17858.497009] <1>  ramtop=BFF7D000 (3071 MB)
[19443.347014] vaddr to paddr module is leaving..
[19454.265539] vaddr to paddr module is running..
[19454.265542] cr0 = 0x80050033, cr3 = 0x6e3f8000
[19454.265543] PGDIR_SHIFT = 39
[19454.265543] P4D_SHIFT   = 39
[19454.265544] PUD_SHIFT   = 30
[19454.265544] PMD_SHIFT   = 21
[19454.265545] PAGE_SHIFT  = 12
[19454.265545] PTRS_PER_PGD = 512
[19454.265546] PTRS_PER_P4D = 1
[19454.265546] PTRS_PER_PUD = 512
[19454.265547] PTRS_PER_PMD = 512
[19454.265548] PTRS_PER_PTE = 512
[19454.265548] PAGE_MASK    = 0xfffffffffffff000

[19454.265551] get_page_vaddr=0xffff991773383000
[19454.265552] pgd_val = 0x8cd97067, pgd_index = 306
[19454.265552] p4d_val = 0x8cd97067, p4d_index = 0
[19454.265553] pud_val = 0x8cd9b067, pud_index = 93
[19454.265554] pmd_val = 0xb33b2063, pmd_index = 409
[19454.265555] pte_val = 0x80000000b3383063, pte_index = 387
[19454.265556] page_addr = 80000000b3383000, page_offset = 0
[19454.265556] vaddr = ffff991773383000, paddr = 80000000b3383000

4.4 运行fileview

  • ./fileview /dev/dram

4.5 计算所有页目录索引字段

ioremap虚拟地址转物理地址_linux_04

  • pgd_index = (0x991773383000 >> 39) & 0x1ff = 306
  • p4d_index = 0
  • pud_index = (0x991773383000 >> 30) & 0x1ff = 93
  • pmd_index = (0x991773383000 >> 21) & 0x1ff = 409
  • pte_index = (0x991773383000 >> 12) & 0x1ff = 387

4.6 计算各页目录的偏移

  • pgd_offset = pgd_index * 8 = 0x990
  • p4d_offset = 0
  • pud_offset = pud_index * 8 = 0x2e8
  • pmd_offset = pmd_index * 8 = 0xCC8
  • pte_offset = pte_index * 8 = 0xC18
  • pgae_offset = 0x991773383000 & 0xfff = 0

4.7 利用fileview模拟寻页的过程

  • pgd_val = *(cr3 + pgd_offset) = *(0x6eb08000 + 0x990) = 0x8cd97067
  • pud_val = *(pgd_val & PAGE_MASK + pud_offset) = *(0x8cd97067 & 0xfffffffffffff000 + 0x2e8) = 0x8cd9b067
  • pmd_val = *(pud_val & PAGE_MASK + pmd_offset) = *(0x8cd9b067 & 0xfffffffffffff000 + 0xCC8) = 0xb33b2063
  • pte_val = *(pmd_val & PAGE_MASK + pte_offset) = *(0xb33b2063 & 0xfffffffffffff000 + 0xC18) = 0x80000000b3383063
  • paddr = pte_val & PAGE_MASK + page_offset = 0x80000000b3383000
  • 可以看到这个正式我们写的"hello world from kernel"