IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【linux 系统移植⑦】内存分配相关问题 -> 正文阅读

[系统运维]【linux 系统移植⑦】内存分配相关问题

. 内存分配相关问题

  • 为什么要 relocate?
  • 使用 uboot 启动树莓派
makefile
    uboot 版本信息:U_BOOT_VERSION
	uboot 入口地址:CONFIG_SYS_TEXT_BASE
	.lds 的选择:CONIFG_STS_LDSCRIPT
mkconfig
    config.h
    创建符号链接
.lds
    指定了uboot的入口:ENTRY(_start)
第 1 阶段 - arch/arm/cpu/armv7/start.S
    1. 填充 16 字节校验位
    2. 设置异常向量表(4B/item)
            实际内容为空,除 reset 异常
            0. 复位异常:复位电平有效时,程序跳转到复位处理程序处执行
            1. 未定义指令异常:遇到不能处理的指令时,产生未定义指令异常
            2. 软件中断异常:执行SWI指令产生的异常
            3. 预存指令异常:处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取终止异常
            4. 数据操作异常:处理器数据访问指令的地址不存在时,或该地址不允许当前指令访问时,产生数据中止异常
            5. 未使用
            6. IRQ:外部中断请求有效,且 CPSR 中的 I 位为 0 时,产生 IRQ 异常
            7. FIQ:快速中断请求引脚有效,且 CPSR 中的 F 位为 0 时,产生 FIQ 异常
    3. 定义uboot中断处理函数栈
   			如果 uboot 使用中断
    4. 【reset段】禁能 IRQ(普通中断) 和 FIQ(快速中断),使能 SVC 模式(超级用户模式)
            禁能目的:异常向量表未初始化,无法执行中断
            SVM 模式主要用于 SWI 和 os kernel
    5. 初始化相关全局变量
            TEXT_BASE:uboot代码的链接地址
            CFG_PHY_UBOOT_BASE:
    6. 【cpu_init_cp15段】设置 MMU、cache 以及 TLB
            关闭 cache:DDR未初始化,从cache中取数据时,会产生异常
            关闭 mmu:开启会导致uboot引导过程变得复杂
    7. 判断启动介质——PRO_ID_BASE+OMR_OFFSET地址处的寄存器读取启动信息
    8. 第一次初始化栈 —— SRAM(cache)
            只有一个 lr 寄存器,无法使用嵌套跳转,后续过程需要双层函数调用
    9. 【cpu_init_crit段】跳转到 lowlevel_init.S(ddr、clk、pll 初始化)
            1. 判断复位类型
                    冷启动需要初始化DDR,休眠唤醒不需要初始化DDR
            2. 关闭看门狗
                    看门狗就是定期的查看芯片内部的情况,一旦发生错误就向芯片发出重启信号的电路。看门狗命令在程序的中断中拥有最高的优先级。防止程序跑飞。也可以防止程序在线运行时候出现死循环。
            3. 开发板供电锁存
            4. 检测程序当前运行位置 cache?DDR?
            5. 初始化时钟、串口、DDR
            6. 跳回到 start.S
    10. 再次锁存供电
            可能为代码的冗余
    11. 第二次初始化栈 —— DDR(内存)
            为了即将执行的C程序做准备,位置_TEXT_PHY_BASE(uboot在DDR中的真正物理地址)
    12. 再次检测当前程序执行地址(SRAM:DDR?12:14)
    13. 通过引脚来判断启动介质
    14. 进行uboot重定位
            将SD/MMC中的内容移动到DDR
    15. 建立虚拟地址映射表并开启MMU
    16. 第三次初始化栈 —— DDR
            之前栈位置紧挨着uboot,不太合理
    17. 清空 bss
    18. 跳转至第二阶段
第 2 阶段 —— crt0.S/_main
	1. 初始化 board_init_f() 环境并调用
			该环境提供了一个栈和存储 GD 数据结构的地方
			r9寄存器保存gd结构体的首地址
	2. 初始化 recloate_code() 环境并调用
			    gd 更新
                    r9 = gd->bd
                    new GD is below bd
                为 relocate 做准备
                	...
                r0 = gd->relocaddr
                调用 relocate_code
	3. 初始化 board_init_r() 环境并调用
			BSS:初始化为0
    		上电指示灯亮
    		r0 赋值 gd 指针,r1 赋值 GD_RELOCADDR
    		一些CPU需要调用 c_runtime_cpu_setup()
    			无效 icache
    			更新异常向量表首地址
    		
第 2.5 阶段 —— arch/arm/lib/board.c
    1. board_init_f()
    		1. 初始化全局变量 - gd.monlen、init_sequence 数组,数组元素为函数的指针,这些函数用来初始化各个功能
    			arch_cpu_init: 可以先写一个空函数启动uboot
            	timer_init 在lib/time.c中有实现,是空函数,但是有__WEAK关键字,如果自己实现,则会调用自己实现的这个函数
            	serial_init
            		gd 中的 have_console 字段就是该函数设置的
            	console_init_f
            	dram_init
            		设置 gd->ram_size
            	...
        	2. 初始化 sdram(由顶部向下进行)
      			(参考内存分配相关问题)
      			gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
                gd->irq_sp = addr_sp;
                gd->bd->bi_baudrate = gd->baudrate;
                gd->relocaddr = addr;
                gd->start_addr_sp = addr_sp;
                gd->reloc_off = addr - _TEXT_BASE;
                gd->fdt_blob = new_fdt;
                ...
    2. relocate_code()
			环境变量的重定位
	3. board_init_r()
			gd->flags |= GD_FLG_RELOC; // 标志已经relocate
			使能cache
			调用板级支持函数—— board_init()
			serial_initialize();
			对malloc预留的空间初始化,起始地址,结束地址,清空。
			接下来的代码是做一些外设的初始化
				比如 mmc flash eth,环境变量的设置,还有中断的使能
				...
			进入死循环
                for (;;) {
                    main_loop();
                }
第 3 阶段 —— common/main.c/main_loop()
    	第一阶段:do_bootm_states() --> bootm_start() 
    		清空 images,标志状态 bootstage_mark_name为bootm_start
		第二阶段:do_bootm_states() --> bootm_find_os() --> boot_get_kernel()
			boot_get_kernel(): CRC校验image头部,获取image的type
			bootm_find_os():获取一些基本的os信息到images结构体中(获取os的 type、comp、end、load addr)
		第三阶段:do_bootm_states() --> bootm_find_other()
			查询是否有ramdisk
		第四阶段:do_boom_states() --> bootm_load_os()
				do_bootm_states() --> boot_fn()
	 			获取相应的 boot_fn()即do_bootm_linux()
		第五阶段:do_bootm_states() --> do_bootm_linux() --> boot_prep_linux()
			为kernel准备参数
		第六阶段:do_bootm_states() --> boot_selected_os() --> do_bootm_linux() -c-> boot_jump_linux()
			调用kernel_entry()进入内核,将内核参数放在r2(bi_boot_params)

. board_init_f() 中关于sdram的划分 —— 预留

https://blog.csdn.net/skyflying2012/article/details/25804209?spm=1001.2014.3001.5501

1. 将 sdram 顶部一段空间隐藏
	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
2. 预留出 tlb 空间
	gd->arch.tlb_addr = addr; // tlb 空间存放首地址
	gd->arch.tlb_size = 4096 * 4; // 如果打开了 icache 以及 dcache,则预留出 16KB 的 TLB 空间 
3. 确定 framebuffer
    gd->fb_base = CONFIG_FB_ADDR; // 获取framebuffer大小
    gd->fb_base = addr; // frambuffer 首地址
4.  为uboot的code留出空间
	addr -= gd->mon_len;addr &= ~(4096 - 1);
5. 预留 malloc space 空间
	addr_sp = addr - TOTAL_MALLOC_LEN;
6. bd struct
	bd = (bd_t *) addr_sp;
	gd->bd = bd; // 全局信息 bd_t 结构体空间的首地址存在 gd->bd
7. gd struct
	addr_sp -= sizeof (gd_t);
8. 留出 12 bytes
	addr_sp -= 12; // for abort stack
9. 预留 uboot stack space,确定了 addr_sp 位置(之下的空间为栈空间)
	interrupt_init();

重定位后,

https://blog.csdn.net/nicholas_duan/article/details/106529673

在这里插入图片描述

在这里插入图片描述

. /arch/arm/cpu/u-boot.lds 中关于 uboot 镜像划分

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") // 指定输出可执行文件是elf格式,32位ARM指令,小端格式
OUTPUT_ARCH(arm)	// 指定输出的可执行文件的平台为ARM
ENTRY(_start) // 链接完毕后的执行入口
SECTIONS
{
	. = 0x00000000; // 指定 image 文件的全局入口点,通常在 flash 0x0 处

	. = ALIGN(4); // 代码以4字节对齐
	.text :
	{
		*(.__image_copy_start) // __image_copy_start 全局变量
		CPUDIR/start.o (.text*) // 先放置 start.o
		*(.text*) // 其它文件的代码段
	}

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } // 只读数据段(排序)

	. = ALIGN(4);
	.data : {	// 全局变量段
		*(.data*)
	}

	. = ALIGN(4);

	. = .;

	. = ALIGN(4);
	.u_boot_list : {
		KEEP(*(SORT(.u_boot_list*)));
	}

	. = ALIGN(4);

	.image_copy_end :
	{
		*(.__image_copy_end)
	}

	.rel_dyn_start :	// 动态符号链接段
	{
		*(.__rel_dyn_start)
	}

	.rel.dyn : {
		*(.rel*)
	}

	.rel_dyn_end :
	{
		*(.__rel_dyn_end)
	}

	_end = .;

	. = ALIGN(4096);
	.mmutable : {	// 不再使用
		*(.mmutable)
	}

	.bss_start __rel_dyn_start (OVERLAY) : { // 可变化的局部数据
		KEEP(*(.__bss_start));
		__bss_base = .;
	}

	.bss __bss_base (OVERLAY) : {
		*(.bss*)
		 . = ALIGN(4);
		 __bss_limit = .;
	}

	.bss_end __bss_limit (OVERLAY) : {
		KEEP(*(.__bss_end));
	}
	// 下面不使用
	/DISCARD/ : { *(.dynsym) }
	/DISCARD/ : { *(.dynstr*) }
	/DISCARD/ : { *(.dynamic*) }
	/DISCARD/ : { *(.plt*) }
	/DISCARD/ : { *(.interp*) }
	/DISCARD/ : { *(.gnu*) }
	/DISCARD/ : { *(.ARM.exidx*) }
	/DISCARD/ : { *(.gnu.linkonce.armexidx.*) }
}

在这里插入图片描述

. kernel 镜像放在 DDR 什么位置?

do_bootm() 函数校验内核的头部信息,根据头部信息,判断内核格式和进行 CRC 校验。

https://blog.csdn.net/wowricky/article/details/83218356?spm=1001.2014.3001.5501

内核一定要放在链接地址处,链接地址去内核源代码的链接脚本或者Makefile中去查找。

.将 u-boot.bin 从 SD 卡搬运到 DDR 中(relocate.c)

真正的重定位是通过调用 movi_bl2_copy 函数完成的,在uboot/cpu/s5pc11x/movi.c中,在 lowlevel_init.s 完成 DDR 初始化后调用。

  1. copy_code_to_dram(void) 搬运代码。
  2. 修改 Makefile,添加搬运代码
  3. 修改 lowlevel_init.s 文件,添加跳转到 copy_code_to_dram 代码
  4. 修改 start.s 代码,从 sram 直接跳转到 sdram
typedef unsigned int u32;
typedef unsigned long           ulong;

typedef unsigned int (*copy_sd_mmc_to_mem)
(u32 channel, u32 start_block, unsigned char block_size, u32 *trg, u32 init);

void copy_code_to_dram(void)
{
        ulong ch;
        ulong dest = 0x34800000; 
        unsigned int uImage_dest = 30008000;
        unsigned int uImage_sec_nu = 1000
        u32 ret;

        ch = *(volatile u32 *)(0xD0037488);

        copy_sd_mmc_to_mem copy_bl2 =
            (copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));
	unsigned int uImage_dest = 0x30008000;
        unsigned int uImage_sec_nu = 1000;
	
	 ulong dest = 0x34800000;
        unsigned int sec_nu = 49;


        if (ch == 0xEB000000) {
                ret = copy_bl2(0, sec_nu,       128,(u32 *)dest , 0);
                ret = copy_bl2(0, sec_nu+128,   128,(u32 *)(dest+0x10000) , 0);
                ret = copy_bl2(0, sec_nu+256,   128,(u32 *)(dest+0x20000) , 0);
                //ret = copy_b12(0, sec_nu+384, 128,(u32 *)(dest+0x30000) , 0);

                for(i = 0;i < 5*1024;i++)
                {
                        copy_b12(0,uImage_sec_nu + i*2,2, (unsigned int *)(uImage_dest + i*1024),0);
                }
        }
}

. uboot 向内核传递启动参数

https://www.cnblogs.com/linfeng-learning/p/9284315.html

https://blog.csdn.net/qq_16933601/article/details/106244510

在跳转到内核以前,uboot 需要对 CPU 寄存器进行设置:

R0=0
R1=machine ID;==arch\arm\include\asm\mach-type.h==
R2=启动参数标记列表在RAM中起始基地址,块内存的基地址 bi_boot_params

重要的数据结构 —— global_data

uboot/include/asm-arm/Global_data.h

typedef struct global_data { 
    bd_t *bd; // asm-arm/u-boot.h
    unsigned long flags; 
    unsigned long baudrate; // 串口波特率   
    unsigned long have_console; // 标志位,是否使用控制台console   
    unsigned long reloc_off; // 重定位有关偏移量   
    unsigned long env_addr; // 环境变量结构体的偏移量   
    unsigned long env_valid; // 标志位,表示内存中的环境变量能否使用   
    unsigned long fb_base; // 帧缓存基地址,和显示有关
    #ifdef CONFIG_VFD 
    unsigned char vfd_type; /* display type */ 
    #endif 
    #if 0 
    unsigned long cpu_clk; /* CPU clock in Hz! */ 
    unsigned long bus_clk; 
    unsigned long ram_size; /* RAM size */ 
    unsigned long reset_status; /* reset status register at boot */ 
    #endif 
    void **jt; /* jump table */ // 基本无用
} gd_t;

// asm-arm/u-boot.h
typedef struct bd_info { 
    int bi_baudrate; /* serial console baudrate */ 
    unsigned long bi_ip_addr; /* IP Address */ 
    unsigned char bi_enetaddr[6]; /* Ethernet adress */ 
    struct environment_s *bi_env; 
    ulong bi_arch_number; /* unique id for this board */ 
    ulong bi_boot_params; /* where this board expects params */ // 传递给内核的参数的位置
    struct { /* RAM configuration */ 
            ulong start; 
            ulong size; 
    } 
    bi_dram[CONFIG_NR_DRAM_BANKS]; 
    #ifdef CONFIG_HAS_ETH1 
    /* second onboard ethernet port */ 
    unsigned char bi_enet1addr[6]; 
    #endif 
} bd_t;

gd 和 bd 初始化

// lib/arm/board.c
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); // ???
memset((void*)gd, 0, sizeof(gd_t)); 
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 
memset(gd->bd, 0, sizeof(bd_t));

// board/smdk2410/smdk2410.c
gd->bd->bi_boot_params = 0x30000100; // 说明参数位置在 0x30000100

gd和bd需要内存,内存当前没有被人管理(因为没有操作系统统一管理内存),大片的DDR内存散放着可以随意使用(只要使用内存地址直接去访问内存即可),在uboot中需要有一个整体规划. 如下:

在这里插入图片描述

参数链表必须以 ATAG_CORE 开始,以 ATAG_NONE 结束。(这两个标记本身是一个 32 位的值),其它的参数标记还包括:ATAG_MEM32,ATAG_INITRD,ATAG_RAMDISK,ATAG_COMDLINE 等。**每个参数标记就代表一个参数结构体(ATAG_CORE,ATAG_MEM,ATAG_RAMDISK,ATAG_INITRD32,ATAG_CMDLINE,ATAG_END),由各个参数结构体构成了参数链表。**在整个参数链表中除了参数结构体 ATAG_CORE 和 ATAG_NONE 的位置固定以外,其他参数结构体的顺序是任意的。所以我们在构建各种参数 tag 时,在开始时先要构建一个参数标记为 ATAG_CORE的 tag 结构,标示从这个 tag 结构开始接下来就是参数。参数链表的遍历是通过函数 tag_next(struct*tag) 来实现的。

在这里插入图片描述

.uboot 运行中如何得知 DDR 配置信息?

bdinfo 参数可以打印 gd->bd 记录的所有硬件相关的全局变量的值

DRAM bank   = 0x00000000
-> start    = 0x30000000
-> size     = 0x10000000
DRAM bank   = 0x00000001
-> start    = 0x40000000
-> size     = 0x10000000
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 12:50:23  更:2021-08-11 12:52:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 3:58:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计