zoukankan      html  css  js  c++  java
  • 設置Linux保留物理內存並使用 (1)

    在Linux系統中可以通過memblock來設置系統保留物理內存,防止這些內存被內存管理系統分配出去。

    作者: 彭東林

    郵箱: pengdonglin137@163.com

    平臺

    硬件平臺: TQ2440

    Linux版本:Linux 3.14.45

    說明

    1. 在tq2440上,物理內存的起始地址是0x30000000,一共有64MB的內存,所以物理內存地址範圍是: 0x30000000 -> 0x33ffffff

    2. 可以在uboot傳給kernel的參數bootargs中添加一個"memblock=debug",這樣在Linux啓動的時候,會將設置memblock的信息打印出來

    參考博文

    Linux内核---41.arm 内存初始化

    http://bbs.chinaunix.net/thread-4143403-1-1.html

    代碼調用

    在Linux啓動的時候會調用machine相關的代碼定製部分系統保留內存,函數調用如下:

    start_kernel

        ----> setup_arch

                ----> arm_memblock_init

                         ----> mdesc->reserve()

    所以我們可以修改machine相關的代碼,添加reserve函數的實現。

    方法一

    修改mach-tq2440.c如下:

     1 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
     2 index f9679fb..da75db2 100644
     3 --- a/arch/arm/mach-s3c24xx/mach-tq2440.c
     4 +++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
     5 @@ -23,6 +23,7 @@
     6  #include <linux/platform_device.h>
     7  #include <linux/io.h>
     8  #include <linux/dm9000.h>
     9 +#include <linux/memblock.h>
    10  
    11  #include <asm/mach/arch.h>
    12  #include <asm/mach/map.h>
    13 @@ -503,11 +504,28 @@ static void __init tq2440_machine_init(void)
    14         s3c_pm_init();
    15  }
    16  
    17 +static void __init tq2440_reserve(void) {
    18 +    u32 paddr,size;
    19 +
    20 +    printk("%s enter.
    ", __func__);
    21 +
    22 +    paddr = 0x32000000;
    23 +    size = 0x200000;
    24 +
    25 +    if (memblock_reserve(paddr, size) < 0) {
    26 +        pr_err("failed to reserve DRAM - no memory
    ");
    27 +               return;
    28 +    }
    29 +
    30 +    printk("reserve : reserve %dM mem
    ", size>>20);
    31 +}
    32 +
    33  MACHINE_START(TQ2440, "TQ2440")
    34         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
    35         .atag_offset    = 0x100,
    36  
    37         .init_irq       = s3c2440_init_irq,
    38 +    .reserve    = tq2440_reserve,
    39         .map_io         = tq2440_map_io,
    40         .init_machine   = tq2440_machine_init,
    41         .init_time      = samsung_timer_init,

     上面我們在0x32000000開始的地方保留了2MB的物理內存,然後調用memblock_reserve告訴系統。這個在系統的啓動信息中可以看到:

     1 [    0.000000] Machine: TQ2440
     2 [    0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
     3 [    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
     4 [    0.000000] tq2440_reserve enter.
     5 [    0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x50
     6 [    0.000000] reserve : reserve 2M mem
     7 [    0.000000] MEMBLOCK configuration:
     8 [    0.000000]  memory size = 0x4000000 reserved size = 0x77ba04
     9 [    0.000000]  memory.cnt  = 0x1
    10 [    0.000000]  memory[0x0]    [0x00000030000000-0x00000033ffffff], 0x4000000 bytes flags: 0x0
    11 [    0.000000]  reserved.cnt  = 0x3
    12 [    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
    13 [    0.000000]  reserved[0x1]    [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0
    14 [    0.000000]  reserved[0x2]    [0x00000032000000-0x000000321fffff], 0x200000 bytes flags: 0x0

      上面的第5行和第14行就是我們自己設置的保留內存範圍信息。在Linux啓動後,在debugfs中也可以看到memblock的信息:

    [root@TQ2440 /]# cat /sys/kernel/debug/memblock/memory 
       0: 0x30000000..0x33ffffff
    [root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved 
       0: 0x30004000..0x30007fff
       1: 0x30008200..0x3057fc03
       2: 0x32000000..0x321fffff
       3: 0x33f60000..0x33ffbfff
       4: 0x33ffc540..0x33ffc96b
       5: 0x33ffc980..0x33ffc9f7
       6: 0x33ffca00..0x33ffca03
       7: 0x33ffca20..0x33ffca23
       8: 0x33ffca40..0x33ffca43
       9: 0x33ffca60..0x33ffca63
      10: 0x33ffca80..0x33ffcad2
      11: 0x33ffcae0..0x33ffcb32
      12: 0x33ffcb40..0x33ffcb92
      13: 0x33ffcba0..0x33ffcbbb
      14: 0x33ffcbc0..0x33ffcdc7
      15: 0x33ffcdd0..0x33ffffff

      其中memory節點中存放的是可用的物理內存地址範圍,reserved表示已經分配出去的物理地址,第2行記錄的就是我們設置的。

    既然添加了保留物理內存,那麼如何使用呢?下面我寫了一個簡單的內核模塊,使用我們添加的保留物理內存,下面是內核模塊的代碼:

     1 #include <linux/module.h>
     2 
     3 #define RESERVE_PHY  0x32000000
     4 #define RESERVE_SIZE 0x200000
     5 
     6 static char str[] = "pengdonglin137@163.com
    ";
     7 
     8 static __init int reserve_demo_init(void)
     9 {
    10     memcpy(phys_to_virt(RESERVE_PHY), str, sizeof(str));
    11 
    12     printk("%s: virt: %p
    ", __func__, phys_to_virt(RESERVE_PHY));
    13 
    14     return 0;
    15 }
    16 
    17 static __exit void reserve_demo_exit(void)
    18 {
    19     printk("%s: %s
    ", __func__, (char *)phys_to_virt(RESERVE_PHY));
    20 }
    21 
    22 module_init(reserve_demo_init);
    23 module_exit(reserve_demo_exit);
    24 MODULE_LICENSE("GPL");

      可以看到,我們直接調用函數phys_to_virt將物理地址轉換成虛擬地址,然後直接向這個虛擬地址中寫入數據,在模塊卸載時再將內容打印出出來。

    1 [root@TQ2440 /]# insmod nfs/demo.ko 
    2 [ 1417.153362] reserve_demo_init: virt: c2000000
    3 [root@TQ2440 /]# 
    4 [root@TQ2440 /]# rmmod demo
    5 [ 1420.986938] reserve_demo_exit: pengdonglin137@163.com
    6 [ 1420.986938] 

     可以看到,第2行中得到物理地址0x32000000對應的虛擬地址是0xC2000000。在模塊卸載時打印出了我們之前寫入的內容。

    這種方法使用與物理內存在Normal區域的情況。

    方法二

    修改mach-tq2440.c

     1 diff --git a/arch/arm/mach-s3c24xx/mach-tq2440.c b/arch/arm/mach-s3c24xx/mach-tq2440.c
     2 index f9679fb..345a868 100644
     3 --- a/arch/arm/mach-s3c24xx/mach-tq2440.c
     4 +++ b/arch/arm/mach-s3c24xx/mach-tq2440.c
     5 @@ -23,6 +23,7 @@
     6  #include <linux/platform_device.h>
     7  #include <linux/io.h>
     8  #include <linux/dm9000.h>
     9 +#include <linux/memblock.h>
    10  
    11  #include <asm/mach/arch.h>
    12  #include <asm/mach/map.h>
    13 @@ -503,11 +504,31 @@ static void __init tq2440_machine_init(void)
    14         s3c_pm_init();
    15  }
    16  
    17 +static void __init tq2440_reserve(void) {
    18 +    u32 paddr,size;
    19 +
    20 +    printk("%s enter.
    ", __func__);
    21 +
    22 +    paddr = 0x32000000;
    23 +    size = 0x200000;
    24 +
    25 +    if (memblock_reserve(paddr, size) < 0) {
    26 +        pr_err("failed to reserve DRAM - no memory
    ");
    27 +               return;
    28 +    }
    29 +
    30 +    memblock_free(paddr, size);
    31 +    memblock_remove(paddr, size);
    32 +
    33 +    printk("reserve : reserve %dM mem
    ", size>>20);
    34 +}
    35 +
    36  MACHINE_START(TQ2440, "TQ2440")
    37         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
    38         .atag_offset    = 0x100,
    39  
    40         .init_irq       = s3c2440_init_irq,
    41 +    .reserve    = tq2440_reserve,
    42         .map_io         = tq2440_map_io,
    43         .init_machine   = tq2440_machine_init,
    44         .init_time      = samsung_timer_init,

     用新kernel啓動,在啓動信息中可以看到:

     1 [    0.000000] Machine: TQ2440
     2 [    0.000000] memblock_reserve: [0x00000030008200-0x0000003057fc03] flags 0x0 arm_memblock_init+0x4c/0x1bc
     3 [    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x158/0x1bc
     4 [    0.000000] tq2440_reserve enter.
     5 [    0.000000] memblock_reserve: [0x00000032000000-0x000000321fffff] flags 0x0 tq2440_reserve+0x1c/0x68
     6 [    0.000000]    memblock_free: [0x00000032000000-0x000000321fffff] tq2440_reserve+0x3c/0x68
     7 [    0.000000] reserve : reserve 2M mem
     8 [    0.000000] MEMBLOCK configuration:
     9 [    0.000000]  memory size = 0x3e00000 reserved size = 0x57ba04
    10 [    0.000000]  memory.cnt  = 0x2
    11 [    0.000000]  memory[0x0]    [0x00000030000000-0x00000031ffffff], 0x2000000 bytes flags: 0x0
    12 [    0.000000]  memory[0x1]    [0x00000032200000-0x00000033ffffff], 0x1e00000 bytes flags: 0x0
    13 [    0.000000]  reserved.cnt  = 0x2
    14 [    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
    15 [    0.000000]  reserved[0x1]    [0x00000030008200-0x0000003057fc03], 0x577a04 bytes flags: 0x0

     在kernel啓動後,在memory和reserved節點中:

    [root@TQ2440 /]# cat /sys/kernel/debug/memblock/memory 
       0: 0x30000000..0x31ffffff
       1: 0x32200000..0x33ffffff

     可以看到,此時系統中有兩塊物理內存,但是這兩塊物理內存的地址之間不連續,中間有一個大小爲2MB的“洞”。此時在reserved節點看不到這部分內存。

    [root@TQ2440 /]# cat /sys/kernel/debug/memblock/reserved 
       0: 0x30004000..0x30007fff
       1: 0x30008200..0x3057fc03
       2: 0x33f60000..0x33ffbfff
       3: 0x33ffc520..0x33ffc94b
       4: 0x33ffc960..0x33ffc9d7
       5: 0x33ffc9e0..0x33ffc9e3
       6: 0x33ffca00..0x33ffca03
       7: 0x33ffca20..0x33ffca23
       8: 0x33ffca40..0x33ffca43
       9: 0x33ffca60..0x33ffcab2
      10: 0x33ffcac0..0x33ffcb12
      11: 0x33ffcb20..0x33ffcb72
      12: 0x33ffcb80..0x33ffcb9b
      13: 0x33ffcba0..0x33ffcbbb
      14: 0x33ffcbc0..0x33ffcdc7
      15: 0x33ffcdd0..0x33ffffff

    此時在使用這部分保留內存時就不能直接使用了,需要申請,然後映射後才能使用,如下:

     1 #include <linux/module.h>
     2 #include <linux/ioport.h>
     3 #include <linux/io.h>
     4 
     5 #define RESERVE_PHY  0x32000000
     6 #define RESERVE_SIZE 0x200000
     7 
     8 static char str[] = "pengdonglin137@163.com
    ";
     9 static void __iomem *addr;
    10 
    11 static __init int reserve_demo_init(void)
    12 {
    13 
    14     if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
    15         return  -EBUSY;
    16 
    17     addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
    18     memcpy(addr, str, sizeof(str));
    19 
    20     printk("%s: virt: %p
    ", __func__, addr);
    21 
    22     return 0;
    23 }
    24 
    25 static __exit void reserve_demo_exit(void)
    26 {
    27     printk("%s: %s
    ", __func__, (char *)addr);
    28 
    29     iounmap(addr);
    30     release_mem_region(RESERVE_PHY, RESERVE_SIZE);
    31 }
    32 
    33 module_init(reserve_demo_init);
    34 module_exit(reserve_demo_exit);
    35 MODULE_LICENSE("GPL");

     然後加載這個模塊,此時會向addr中寫入字符串,在卸載時再打印出來。log如下:

    1 [root@TQ2440 /]# insmod nfs/demo.ko 
    2 [   43.853848] reserve_demo_init: virt: c5000000
    3 [root@TQ2440 /]# 
    4 [root@TQ2440 /]# rmmod demo
    5 [   49.247204] reserve_demo_exit: pengdonglin137@163.com
    6 [   49.247204] 
    7 [root@TQ2440 /]# 

    方法三

    在uboot可以通過在bootargs中設置mem參數,告訴kernel可用的物理內存。

    修改uboot傳給kernel的bootargs,如下:

    setenv bootargs "memblock=debug noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200n8 mem=32M"

     此時啓動Linux,啓動信息中可以看到:

    [    0.000000] memblock_reserve: [0x00000030008200-0x00000030589c83] flags 0x0 arm_memblock_init+0x5c/0x1d8
    [    0.000000] memblock_reserve: [0x00000030004000-0x00000030007fff] flags 0x0 arm_memblock_init+0x8c/0x1d8
    [    0.000000] MEMBLOCK configuration:
    [    0.000000]  memory size = 0x2000000 reserved size = 0x585a84
    [    0.000000]  memory.cnt  = 0x1
    [    0.000000]  memory[0x0]    [0x00000030000000-0x00000031ffffff], 0x2000000 bytes flags: 0x0
    [    0.000000]  reserved.cnt  = 0x2
    [    0.000000]  reserved[0x0]    [0x00000030004000-0x00000030007fff], 0x4000 bytes flags: 0x0
    [    0.000000]  reserved[0x1]    [0x00000030008200-0x00000030589c83], 0x581a84 bytes flags: 0x0
    ... ...
    [    0.000000] Memory: 26752K/32768K available (3912K kernel code, 214K rwdata, 1176K rodata, 155K init, 176K bss, 6016K reserved)

     可以看到,此時Linux只知道有32MB的物理內存,而實際上板子上有64MB的物理內存。

    可以看到memory和reserved節點的內容如下:

     1 [root@TQ2440 /]# cat /d/memblock/memory 
     2    0: 0x30000000..0x31ffffff
     3 [root@TQ2440 /]# 
     4 [root@TQ2440 /]# cat /d/memblock/reserved 
     5    0: 0x30004000..0x30007fff
     6    1: 0x30008200..0x30589c83
     7    2: 0x31fa6000..0x31ffbfff
     8    3: 0x31ffc840..0x31ffca6b
     9    4: 0x31ffca80..0x31ffcaf7
    10    5: 0x31ffcb00..0x31ffcb03
    11    6: 0x31ffcb20..0x31ffcb23
    12    7: 0x31ffcb40..0x31ffcb43
    13    8: 0x31ffcb60..0x31ffcb63
    14    9: 0x31ffcb80..0x31ffcbda
    15   10: 0x31ffcbe0..0x31ffcc3a
    16   11: 0x31ffcc40..0x31ffcc9a
    17   12: 0x31ffcca0..0x31ffccbb
    18   13: 0x31ffccc0..0x31ffcdc3
    19   14: 0x31ffcdd0..0x31ffffff

     使用剩餘的物理內存,同樣需要先申請,再映射,然後再使用:

     1 #include <linux/module.h>
     2 #include <linux/ioport.h>
     3 #include <linux/io.h>
     4 
     5 #define RESERVE_PHY  0x32000000
     6 #define RESERVE_SIZE 0x200000
     7 
     8 static char str[] = "pengdonglin137@163.com
    ";
     9 static void __iomem *addr;
    10 
    11 static __init int reserve_demo_init(void)
    12 {
    13 
    14     if (!request_mem_region(RESERVE_PHY, RESERVE_SIZE, "my reserve"))
    15         return  -EBUSY;
    16 
    17     addr = ioremap_nocache(RESERVE_PHY, RESERVE_SIZE);
    18     memcpy(addr, str, sizeof(str));
    19 
    20     printk("%s: virt: %p
    ", __func__, addr);
    21 
    22     return 0;
    23 }
    24 
    25 static __exit void reserve_demo_exit(void)
    26 {
    27     printk("%s: %s
    ", __func__, (char *)addr);
    28 
    29     iounmap(addr);
    30     release_mem_region(RESERVE_PHY, RESERVE_SIZE);
    31 }
    32 
    33 module_init(reserve_demo_init);
    34 module_exit(reserve_demo_exit);
    35 MODULE_LICENSE("GPL");

     加載模塊,然後卸載,可以看到log:

    1 root@TQ2440 /]# insmod /nfs/demo.ko 
    2 [  266.998897] reserve_demo_init: virt: c3000000
    3 [root@TQ2440 /]# 
    4 [root@TQ2440 /]# rmmod demo
    5 [  271.735731] reserve_demo_exit: pengdonglin137@163.com
    6 [  271.735731] 

  • 相关阅读:
    tomcat的HTTPS
    java websocket开发的客户端程序
    Java后端WebSocket的Tomcat实现
    java 注解
    工作队列
    中断共享(硬件角度)
    电平触发与边沿触发中断的理解
    kuser_cmpxchg_check 原子操作
    新建普通进程
    fork 至 “sys_clone" SyS_clone
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/5837115.html
Copyright © 2011-2022 走看看