高通平台memory信息的获取

         我们知道每个嵌入式系统的内存分布都不同,这既与平台有关,同时也与硬件连线有关,那么这些具体的内存布局信息是怎么传递到内核并供内核分配使用的呢?我们以高通平台为例进行说明。

        总的来说高通平台是在SBL1中完成对系统内存信息的检测从而收集了系统存在的各类RAM信息,包括地址,大小等。并且把这些信息添加到SMEM中,通过SMEM传递给lk,lk又将其加入到device tree的memory节点之中,这样kernel就可以根据device tree中的memory节点信息来获得系统的内存发布情况。

      一、SBL中RAM partition table的建立

      高通平台采用ram partition table的数据结构来保存系统的内存信息。我们先来看看它的定义:

struct usable_ram_partition_table 
{
  uint32 magic1;          /**< Magic number to identify valid RAM partition table */
  uint32 magic2;          /**< Magic number to identify valid RAM partition table */
  uint32 version;         /**< Version number to track structure definition changes
                               and maintain backward compatibilities */
  uint32 reserved1;       /**< Reserved for future use */

  uint32 num_partitions;  /**< Number of RAM partition table entries */
  
  uint32 reserved2;       /** < Added for 8 bytes alignment of header */
  
  /** RAM partition table entries */ 
  struct ram_partition_entry ram_part_entry[RAM_NUM_PART_ENTRIES];
};
RAM_NUM_PART_ENTRIES=32,也就是最多支持32个RAM partition。
 
struct ram_partition_entry 
{
  char name[RAM_PART_NAME_LENGTH];  /**< Partition name, unused for now */
  uint64 start_address;             /**< Partition start address in RAM */
  uint64 length;                    /**< Partition length in RAM in Bytes */
  uint32 partition_attribute;       /**< Partition attribute */
  uint32 partition_category;        /**< Partition category */
  uint32 partition_domain;          /**< Partition domain */
  uint32 partition_type;            /**< Partition type */
  uint32 num_partitions;            /**< Number of partitions on device */
  uint32 hw_info;                   /**< hw information such as type and frequency */
  uint32 reserved4;                 /**< Reserved for future use */
  uint32 reserved5;                 /**< Reserved for future use */
};
usable_ram_partition_table 是总表,ram_partition_entry是一个个partition entry数据。结构体里的各数据项的含义后面会介绍。

 

         代码实现中函数boot_populate_ram_partition_table中:

void boot_populate_ram_partition_table 
( 
  bl_shared_data_type *bl_shared_data 
)
{
  usable_ram_part_table_t usable_ram_part_tbl_ptr = NULL;
  ram_partition_return_type result = RAM_PART_OTHER_ERR;
  
  /* allocate memory for ram partition table in shared memory region */
  usable_ram_part_tbl_ptr = 
      load_ram_partition_table((uint32*)&result);

  BL_VERIFY(result == RAM_PART_SUCCESS && usable_ram_part_tbl_ptr != NULL, 
            BL_ERR_FAIL_ALLOCATE_SMEM);

  /* add all partitions to the table*/
  result = add_ram_partitions(usable_ram_part_tbl_ptr);
          
  BL_VERIFY(result == RAM_PART_SUCCESS, BL_ERR_FAIL_SAVE_TO_SMEM);

  usable_ram_part_tbl_ptr->magic1  = RAM_PART_MAGIC1;
  usable_ram_part_tbl_ptr->magic2  = RAM_PART_MAGIC2;
  usable_ram_part_tbl_ptr->version = RAM_PARTITION_VERSION;
}
这里面主要就两个函数:

load_ram_partition_table函数中smem中分配SMEM_USABLE_RAM_PARTITION_TABLE空间,用来保存ram_partition_table。

而函数add_ram_partitions把所有的ram partition加入usable_ram_part_tbl_ptr。

我们重点来看这个函数:

static ram_partition_return_type add_ram_partitions 
(
  usable_ram_part_table_t usable_ram_part_tbl_ptr
)
{
  
  。。。。
  do
  {
    result = add_system_ram_partitions(usable_ram_part_tbl_ptr);
    
    。。。。
    result = add_iram_ram_partition(usable_ram_part_tbl_ptr);
   
    。。。。
    result = add_imem_ram_partition(usable_ram_part_tbl_ptr);
    。。。。
    
    /* only after system ram partitions are added should we call add appsbl 
    and add boot region, because add_boot_region_ram_partition and
    add_appsbl_ram_partition will use the system ram partitions 
    entries in the table to figure out which ram categories appsbl/boot 
    region memory belong to*/
    result = add_boot_region_ram_partition(usable_ram_part_tbl_ptr); 
    if (result != RAM_PART_SUCCESS) 
    {
      break;
    }

  }while(0);
  
  return result;
}

一共先后add四种(category)的ram partition:system_ram,iram_ram,imem_ram,boot_region_ram。我们分别进行介绍:

1. system_ram partition就是DDR所代表的RAM空间,在之前ddr初始化的那篇文章里我们提到过boot_ddr_size_info中有个变量noofddr,这个代表了连接的所有DDR总数,一个CS代表一个,所以如果两个EBI上的四个CS都连了DDR的话noofddr等于4,如果只有EBI0的两个CS连了DDR,则noofddr = 2,这里add system_ram依据的就是这个值以及每个CS所覆盖的地址范围来创建partition,因此有一个CS连了DDR就会对应一个system_ram partition。我们看下

   ram_partition_entry里面几个重要的变量的值的设定:

     ram_part_entry_ptr->partition_category = RAM_PARTITION_SDRAM;  ------------本partition的种类(category)
     ram_part_entry_ptr->start_address = ddr_info->cs_addr;---------------------partition的起始地址,也就是这个CS的地址。 
     ram_part_entry_ptr->length = ddr_info->ramsize << CONVERT_TO_BYTE_SHIFT;------------------partition的大小。
     ram_part_entry_ptr->partition_attribute = RAM_PARTITION_READWRITE; --------------------------partition的属性
     ram_part_entry_ptr->partition_domain = RAM_PARTITION_DEFAULT_DOMAIN; -----------------------------partition属于哪个域
     ram_part_entry_ptr->partition_type = RAM_PARTITION_SYS_MEMORY;----------------------------partiton的类型(type)


typedef enum 
{
  RAM_PARTITION_DEFAULT_CATEGORY = ~0,  /**< No specific category defintion */
  RAM_PARTITION_IRAM=4,                   /**< IRAM RAM partition */
  RAM_PARTITION_IMEM=5,                   /**< IMEM RAM partition */
  RAM_PARTITION_SDRAM=14,                  /**< SDRAM type without specific bus information**/
  RAM_PARTITION_CATEGORY_MAX_SIZE = 0x7FFFFFFF
} ram_partition_category_t;

typedef enum 
{
  RAM_PARTITION_SYS_MEMORY = 1,        /**< system memory */
  RAM_PARTITION_BOOT_REGION_MEMORY1,   /**< boot loader memory 1 */
  RAM_PARTITION_BOOT_REGION_MEMORY2,   /**< boot loader memory 2, reserved */
  RAM_PARTITION_APPSBL_MEMORY,         /**< apps boot loader memory */
  RAM_PARTITION_APPS_MEMORY,           /**< apps  usage memory */  
   RAM_PARTITION_TYPE_MAX_SIZE = 0x7FFFFFFF
} ram_partition_type_t;

typedef enum 
{
  RAM_PARTITION_DEFAULT_DOMAIN = 0,  /**< 0b00: No specific domain defintion */
  RAM_PARTITION_APPS_DOMAIN = 1,     /**< 0b01: APPS RAM partition */
  RAM_PARTITION_MODEM_DOMAIN = 2,    /**< 0b10: MODEM RAM partition */
   RAM_PARTITION_DOMAIN_MAX_SIZE = 0x7FFFFFFF
} ram_partition_domain_t


2. iram_partition:调用add_single_ram_partition增加一个partition,参数如下:

result = add_single_ram_partition(usable_ram_part_tbl_ptr,
                                      SCL_IRAM_BASE,
                                      SCL_IRAM_SIZE,
                                      RAM_PARTITION_READWRITE,
                                      RAM_PARTITION_IRAM,
                                      RAM_PARTITION_DEFAULT_DOMAIN,
                                      RAM_PARTITION_SYS_MEMORY);

#define SCL_IRAM_BASE SCL_RPM_CODE_RAM_BASE
#define SCL_IRAM_SIZE SCL_RPM_CODE_RAM_SIZE

因此iram分区代表的是RPM_CODE所占的区域,category为RAM_PARTITION_IRAM, type也是SYS_MEMORY。


3. imem_partition:同样也是调用add_single_ram_partition增加一个partition,参数如下:

result = add_single_ram_partition(usable_ram_part_tbl_ptr,
                                      SCL_IMEM_BASE,
                                      SCL_IMEM_SIZE,
                                      RAM_PARTITION_READWRITE,
                                      RAM_PARTITION_IMEM,
                                      RAM_PARTITION_DEFAULT_DOMAIN,
                                      RAM_PARTITION_SYS_MEMORY);

这个分区所代表的区域就是系统IMEM的区域,category为RAM_PARTITION_IMEM,type也是SYS_MEMORY。


4. boot_region ram partition,这个分区必须要中之前的分区添加完成之后再添加,原因看注释:

/* only after system ram partitions are added should we call add appsbl 
    and add boot region, because add_boot_region_ram_partition and
    add_appsbl_ram_partition will use the system ram partitions 
    entries in the table to figure out which ram categories appsbl/boot 
    region memory belong to*/

意思就是必须要等到system ram partition增加完之后,appsbl和boot region分区才能增加,这是因为appsbl和boot region分区要用到system ram分区的信息来计算出appsbl/boot region分区属于哪个category,说白了就是要根据appsbl和boot region的地址范围看落中DDR分区还是RPM CODE分区还是IMEM分区来判断appsbl/boot分区属于哪个的范围之内。

 get_partition_category(usable_ram_part_tbl_ptr,
                         SCL_SBL1_DDR_DATA_BASE,
                         &category);

    result = add_single_ram_partition(usable_ram_part_tbl_ptr,
                                      SCL_SBL1_DDR_DATA_BASE,
                                      SCL_SBL1_DDR_DATA_SIZE,
                                      RAM_PARTITION_READWRITE,
                                      category,
                                      RAM_PARTITION_DEFAULT_DOMAIN,
                                      RAM_PARTITION_BOOT_REGION_MEMORY1);
 所以先通过get_partition_category函数把boot reagion分区的起始地址 SCL_SBL1_DDR_DATA_BASE传进去看这个地址落中哪个范围之内。

#define SCL_SBL1_DDR_DATA_BASE 0x86700000

#define SCL_SBL1_DDR_DATA_SIZE 0x00E0000

这个地址在DDR的范围之内(0x8000_0000 -- 0x9FFF_FFFF),因此boot region的category是RAM_PARTITION_SDRAM,size为0xE0000(896KB)

这896KB的空间里存放了如下的东西:

SBL1 DDR Region
#   896KB, 0x8670_0000 -- 0x867D_FFFF
#   Contains:
#     SBL1 DDR ZI Region
#     416KB, 0x8670_0000 -- 0x8676_7FFF
#
#     SBL1 DDR Page Tables
#     32KB, 0x8676_8000 -- 0x8676_FFFF
#
#     SBL1 DLOAD Buffers
#     446KB, 0x8677_0000 -- 0x867D_F7FF
#
#     SBL1 DDR Boot Logger Buffers
#     2KB, 0x867D_F800 -- 0x867D_FFFF


appsbl ram 分区没看到有添加。这样ram partition就添加完成了。


在lk中会把这里添加的ram partition信息读取出来加到device tree的memory节点中去,然后再传到Kernel。

代码如下,代码很清晰就不细说了:

uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset)
{
ram_partition ptn_entry;
unsigned int index;
int ret = 0;
uint32_t len = 0;


/* Make sure RAM partition table is initialized */
ASSERT(smem_ram_ptable_init_v1());


len = smem_get_ram_ptable_len();


/* Calculating the size of the mem_info_ptr */
for (index = 0 ; index < len; index++)
{
smem_get_ram_ptable_entry(&ptn_entry, index);


if((ptn_entry.category == SDRAM) &&
(ptn_entry.type == SYS_MEMORY))
{


/* Pass along all other usable memory regions to Linux */
ret = dev_tree_add_mem_info(fdt,
memory_node_offset,
ptn_entry.start,
ptn_entry.size);


if (ret)
{
dprintf(CRITICAL, "Failed to add secondary banks memory addresses\n");
goto target_dev_tree_mem_err;
}
}
}
target_dev_tree_mem_err:


return ret;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值