• Embeded linux之地址映射


    一、板级文件

    通常会由MACHINE_START到板级文件

    MACHINE_START(Chipname, "Chipname")
      .atag_offset    = 0x100,
      .map_io      = Chipname_map_io,
      .init_early    = Chipname_init_early,
      .init_irq     = Chipname_gic_init_irq,
      .handle_irq   = gic_handle_irq,
      .timer       = &Chipname_sys_timer,
      .init_machine  = Chipname_init,
      .reserve     = Chipname_reserve,
      .restart      = Chipname_restart,
    MACHINE_END

    .map_io是一个函数指针,这里指定了Chipname_map_io。函数实体为:

    void __init Chipname_map_io(void)

    {

       ... 

    }

    二、内存映射

    芯片IO口操作分为两类:

    1.寄存器与内存统一编址,又称IO内存

    2.寄存器与内存不统一编址,又称IO端口

    ARM芯片基本上是统一编址,访问寄存器(包括系统寄存器、外设寄存器、IO口寄存器等)直接访问该地址即可。

    其中linux支持的寄存器地址寻址方式为内存映射,即将内核内存1G的某一些地址映射给寄存器,这样操作内核虚拟内存地址就是操作寄存器。

    三、映射方式

    linux内核提供的映射方式有两种:

    1.静态映射

    使用map_desc结构体进行映射,其中map_desc结构体为:

    struct map_desc {
      unsigned long virtual;  //虚拟地址
      unsigned long pfn;    //__phys_to_pfn(物理地址) , 就是物理页框号
      unsigned long length;  //长度
      unsigned int type;    //类型
    };

    举例,填充一个映射信息结构体,映射两块连续的地址空间,出现保留字就再映射一个,或者遇到不需要控制的地址,否则一直连续映射:

    static struct map_desc Chipname_io_desc[] __initdata = {
      {
        .virtual = 0xFE000000,
        .pfn = __phys_to_pfn(0x10000000),
        .length = 0xD0000,
        .type = MT_DEVICE
      },
      {
        .virtual = 0xFE100000,
        .pfn = __phys_to_pfn(0x20000000),
        .length = 0x700000,
        .type = MT_DEVICE
      }
    };

    然后调用iotable_init(Chipname_io_desc, ARRAY_SIZE(Chipname_io_desc));建立映射的函数

    以后直接*(volatile unsigned int *)0xFE000000就能使用了。

    2.动态映射

    驱动中手动映射某一个地址

    #define ioremap(cookie,size)         __arm_ioremap((cookie), (size), MT_DEVICE)
    #define ioremap_nocache(cookie,size)   __arm_ioremap((cookie), (size), MT_DEVICE)
    #define ioremap_cached(cookie,size)     __arm_ioremap((cookie), (size), MT_DEVICE_CACHED)
    #define ioremap_wc(cookie,size)       __arm_ioremap((cookie), (size), MT_DEVICE_WC)
    #define iounmap               __arm_iounmap

    cookie:物理地址
    size:要映射的空间的大小;
    返回虚拟地址
    头文件io.h
    以后直接*(volatile unsigned int *)虚拟地址就能使用了。
     
    扩展:
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) //include/linux/kernel.h,ARRAY_SIZE为计算数组的一维维度,计算方法为数组大小和数组单成员大小之商

    #ifdef __CHECKER__
      #define __must_be_array(arr) 0
    #else
      #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) //include/linux/compiler-gcc.h

    #endif

    #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) //include/linux/bug.h,BUILD_BUG_ON_ZERO的作用在于将返回值转化为编译错误信息。显然当内嵌函数返回值为0时,也即类型相同时,由于BUILD_BUG_ON_ZERO参数为非0而导致char[-1]而发出编译器警告。

    #ifndef __same_type
    # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))  //include/linux/compiler.h,gcc编译器内嵌的函数,判断一个变量的类型是否为某指定的类型,假如是就返回1,否则返回0。这里通过判断指针和指针指向的第一个元素的指针是否是相同类型来判断是否为数组。
    #endif

    arch/arm/mm/mmu.c

    void __init iotable_init(struct map_desc *io_desc, int nr)
    {
      struct map_desc *md;
      struct vm_struct *vm;

      if (!nr) return;

      /*early_alloc_aligned

      *

      */

      vm = early_alloc_aligned(sizeof(*vm) * nr, __alignof__(*vm));

      for (md = io_desc; nr; md++, nr--)

      {

        create_mapping(md, false);
        vm->addr = (void *)(md->virtual & PAGE_MASK);
        vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
        vm->phys_addr = __pfn_to_phys(md->pfn);
        vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
        vm->flags |= VM_ARM_MTYPE(md->type);
        vm->caller = iotable_init;
        vm_area_add_early(vm++);
      }
    }

     
  • 相关阅读:
    Linux下openSSL安装并颁发证书
    js控制自动上传文件
    Swoole 4.5v安装使用
    Flask-SQLAlchemy 的基本使用
    Flask入门之二
    微信消息订阅
    Flask入门之一
    MySQL主从库快速搭建
    FastAPI连接mysql傻瓜式
    UOJ Round总结
  • 原文地址:https://www.cnblogs.com/pokerface/p/6777581.html
Copyright © 2020-2023  润新知