【系統移植】uboot詳細分析
uboot使用
uboot控制檯,倒計時
命令: 調試,操作一些硬件
nand erase
nand write
tftp 20008000 zImage
help: uboot可以提供哪些命令
setenv == set == sete == seten
環境變量: 爲命令提供參數
serverip : tftp命令提供tftp服務器的地址
ipaddr : tftp命令提供tftp客戶端(開發板)的地址
兩個環境變量
uboot: 下載內核,並啓動內核
bootcmd: 倒計時結束後,uboot應該自動做什麼事情
set serverip 192.168.7.2
set ipaddr 192.168.7.6
set ethaddr 00:22:23:24:25:ee
倒計時結束的時候,uboot會執行bootcmd中的內容:
tftp 20008000 zImage ; bootm 20008000
從tftp服務器(serverip)中將zImage文件(/tftpboot/)下載到開發板(ipaddr)中內存的20008000
set bootcmd tftp 20008000 zImage ; bootm 20008000
bootargs: 負責告訴內核文件系統在哪裏(uboot傳遞給內核, 內核要用)
root=xxxx : 根文件系統目錄在哪裏
/dev/nfs : 根文件系統目錄在網絡的遠端
nfsroot=xxxx: 根文件系統目錄在哪臺機器的哪個文件路徑
nfsroot=192.168.7.2:/opt/filesystem
ip=192.168.7.6: 系統登錄的時候,靜態分配一個ip
如果root=/dev/nfs
root=/dev/nfs + nfsroot=xxxx +ip=xx
如果root=/dev/mtdblock2(文件系統製作的時候會講)
root=/dev/mtdblock2 + rootfstype=cramfs
console=ttySAC0,115200 : 內核啓動過程中,調試信息往哪裏輸出,printk
init=/linuxrc : 指定第一個init進程的可執行代碼文件
/opt/filesystem==> host: /etc/exports
sudo vim /etc/exports
/opt/filesystem *(subtree_check,rw,no_root_squash,async)
/opt/fs100/rootfs *(subtree_check,rw,no_root_squash,async)
啓動內核:go/bootm
官方的uboot
: go
set bootcmd tftp 20008000 zImage \; go 20008000
uImage
: bootm(下載地址,不能是20008000)
set bootcmd tftp 20800000 uImage \; bootm 20800000
下載地址的選用:
go==> 可以是任何地址
bootm==> 20008000+zImage的大小以上==>20800000
綜合用法:
set bootcmd tftp 20800000 zImage \; go 20800000
set bootcmd tftp 20800000 uImage \; bootm 20800000
uboot1.3.4:
zImage/uImage ==>bootm
set bootcmd tftp 20800000 uImage \; bootm 20800000
uboot的連接腳本
所在路徑:cpu/arm_cortexa8/u-boot.lds
OUTPUT_ARCH(arm)
ENTRY(_start) // 入口函數
SECTIONS
{
. = 0x00000000; // 當前的起始位置0x0
. = ALIGN(4);
.text(目標文件) :
{
cpu/arm_cortexa8/start.o (.text) // 第一個文件的.text
*(.text)
}
. = ALIGN(4); // 當前位置四字節對齊
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .; // 用__u_boot_cmd_start記錄當前的位置, 代碼會用到,全局的
.u_boot_cmd : { *(.u_boot_cmd) } // 段數據
__u_boot_cmd_end = .; // 結束位置
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
連接的基地址:
-Ttext 0x34800000==>board/samsung/smdkc100/config.mk
TEXT_BASE = 0x34800000
1,TEXT_BASE指定uboot的連接的起始位置
2,指定uboot重定位的位置(可以改成0x2ff00000)
uboot配置的詳細說明
vim Makefile
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep
MKCONFIG := $(SRCTREE)/mkconfig == ./mkconfig shell腳本(可執行程序)
smdkc100_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm_cortexa8 smdkc100 samsung s5pc1xx
./mkconfig smdkc100 arm arm_cortexa8 smdkc100 samsung s5pc1xx
執行一個腳本: 傳遞了6個參數(控制源碼的編譯)
arm : 架構==> lib_arm
smdkc100 : include/configs/smdkc100.h // 開發板所有的宏的配置
arm_cortexa8 : arm名 ==> cpu/arm_cortexa8
smdkc100 samsung : 開發板名==> board/samsung/smdkc100
s5pc1xx :cpu ==>cpu/arm_cortexa8/s5pc1xx
$(@:_config=): $@:_config= ==>smdkc100_config:_config= // _config替換成空,去掉
$(@:_config=xxx) ===>smdkc100xxx
uboot第一階段啓動流程
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
2,
reset:
/*
* set the cpu to SVC32 mode, disable F, I
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
bl cpu_init_crit
|
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
bl lowlevel_init //lowlevel_init.S (board\samsung\smdkc100):lowlevel_init:
|
/* Disable Watchdog */
ldr r0, =S5PC100_WATCHDOG_BASE @0xEA200000
orr r0, r0, #0x0
str r5, [r0]
/* setting SRAM */
ldr r0, =S5PC100_SROMC_BASE
ldr r1, =0x9
str r1, [r0]
/* S5PC100 has 3 groups of interrupt sources */
ldr r0, =S5PC100_VIC0_BASE @0xE4000000
ldr r1, =S5PC100_VIC1_BASE @0xE4000000
ldr r2, =S5PC100_VIC2_BASE @0xE4000000
/* Disable all interrupts (VIC0, VIC1 and VIC2) */
mvn r3, #0x0
str r3, [r0, #0x14] @INTENCLEAR
str r3, [r1, #0x14] @INTENCLEAR
str r3, [r2, #0x14] @INTENCLEAR
/* Set all interrupts as IRQ */
str r5, [r0, #0xc] @INTSELECT
str r5, [r1, #0xc] @INTSELECT
str r5, [r2, #0xc] @INTSELECT
/* Pending Interrupt Clear */
str r5, [r0, #0xf00] @INTADDRESS
str r5, [r1, #0xf00] @INTADDRESS
str r5, [r2, #0xf00] @INTADDRESS
bl uart_asm_init // 只是設置了gpio的功能,波特率的設置在第二階段
#if 1 // 改動的部分
/* init system clock */
bl system_clock_init // 基本上沒太大問題
bl mem_ctrl_asm_init
//mem_setup.S board\samsung\Smdkc100
// 內存的初始化比較複雜, 原廠會提供(1.3.4)
// 向FAE要
// 這部分代碼運行有問題
1,mem_ctrl_asm_init
2,mem_setup.S需要被編譯<===board\samsung\Smdkc100\Makefile
3,內存初始化代碼應該在前16k (反彙編)
修改cpu/arm_cotexa8/u-boot.lds
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE @ upper 128 KiB: relocated uboot
sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 @ leave 3 words for abort-stack
and sp, sp, #~7 @ 8 byte alinged for (ldr/str)d
/* nand src offset : 0x0*/
mov r0, #0x0
/* ddr dst addr : 0x2ff00000*/
ldr r1,=0x2ff00000
/*size*/
ldr r2, =0x40000
bl copy2ddr
ddr的地址(重定位的目標地址): 和uboot的鏈接的基地址要一樣
board/samsung/smdkc100/config.mk
TEXT_BASE=xxxx
clear_bss:
ldr r0, _bss_start @ find start of bss segment
ldr r1, _bss_end @ stop here
mov r2, #0x00000000 @ clear value
clbss_l:
str r2, [r0] @ clear BSS location
cmp r0, r1 @ are we at the end yet
add r0, r0, #4 @ increment clear index pointer
bne clbss_l @ keep clearing till at end
ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot
//start_armboot它的值是在編譯的時候就已經確定:0x2ff00000+offset==> 0x2ff00980
arm: 基本所有的指令都是位置無關(指令在哪裏執行都可以)
有些代碼是位置有關: ldr pc, _start_armboot (pc跳轉的目標地址_start_armboot(0x2ff00980),和特定的位置相關)
ldr本身這條指令是位置無關,整個ldr pc, _start_armboot==>成爲一個位置相關的指令
鏈接地址: 鏈接器爲所有的指令做的排序, 肯定有有個基地址: 基地址+該指令的偏移量
運行地址: 指令實際加載的地址,運行時,指令存放地址
物理地址: 和硬件相關,數據手冊中的地址都是物理地址, 硬件工程師爲設備設定的值
虛擬地址: 一般和mmu相關
思路:
1,支持一種啓動模式nand啓動
a, 時鐘和內存的初始化
1,mem_setup.S 被編譯
b, 完成自拷貝的實現
nand_ops.c(讀操作)
nand(0x0) --> ddr(TEXT_BASE)
board/samsung/smdkc100/config.mk
c,第一階段的代碼必須全部在前16k
u-boot.lds
d, 熟悉一下第一階段的啓動流程代碼
uboot第二階段代碼
lib_arm/Board.c
void start_armboot (void)
爲什麼總是去看smkdc100.h
|
#include <config.h>
|
#define CONFIG_BOARDDIR board/samsung/smdkc100
#include <config_defaults.h>
#include <configs/smdkc100.h>
#include <asm/config.h>
先看主線流程
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
// gd指針指向的空間,清零
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
// 初始化序列
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
// 堆的初始化
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
CONFIG_SYS_MALLOC_LEN);
#if defined(CONFIG_CMD_NAND) // CONFIG_CMD_NAND沒有定義
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
// 環境變量的重定位
env_relocate ();
// 串口的初始化
serial_initialize();
// 無需關心
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
stdio_init (); /* get the devices list going. */
jumptable_init ();
console_init_r ();
// 中斷的使能
/* enable exceptions */
enable_interrupts ();
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
// 網卡的初始化
eth_initialize(gd->bd);
//死循環
for (;;) {
main_loop ();
}
模塊的方式
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
arch_cpu_init,
board_init, // smdkc100開發板的整體的初始化
timer_init, // 定時器的初始化, timer4==>倒計時的間隔時間,產生一個10ms的間隔
env_init, // 環境變量的初步初始化 /* initialize environment */
init_baudrate, // 波特率的設置 /* initialze baudrate settings */
serial_init, // 串口的初始化 /* serial communications setup */
// 分水嶺, 才能夠使用printf去打印調試信息
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
print_cpuinfo, /* display cpu info (and speed) */
checkboard, /* display board info */
dram_init, /* configure available RAM banks */
display_dram_config
環境變量處理:
環境變量的保存到哪個地方: CONFIG_ENV_IS_IN_NAND
env_init
|
gd->env_addr = (ulong)&default_environment[0]; //gd->env_addr指向默認的環境變量
gd->env_valid = 1;
env_relocate ();
|//分配空間,128k
env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
env_relocate_spec ();
|// 從nand中0x40000讀取數據到malloc區域
ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
if (ret)//如果讀取失敗,就使用默認的環境變量
return use_default();
//讀取數據成功,此時還要crc校驗一下,
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
//如果校驗失敗,仍然使用默認的環境變量
return use_default();
|
puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
memset(env_ptr, 0, sizeof(env_t));
// 使用默認的環境變量
memcpy(env_ptr->data, default_environment, sizeof(default_environment));
//CONFIG_ENV_OFFSET是可以控制env保存到nand中特定的位置
// 0x40000
readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
|// 0x40000 塊大小 , malloc區域
char_ptr = &buf[amount_loaded];
nand_read(&nand_info[0], offset, &len, char_ptr)
|// mtd的架構
info->read(info, ofs, *len, (size_t *)len, buf);
自己設定環境變量: smdkc100.h
#define CONFIG_SERVERIP 192.168.7.2
#define CONFIG_IPADDR 192.168.7.6
#define CONFIG_ETHADDR 00:23:24:25:26:27
#define CONFIG_BOOTCOMMAND "tftp 20800000 zImage35 \; go 20800000"
#define CONFIG_BOOTARGS "root=/de/nfs nfsroot=192.168.7.2:/opt/filesystem ip=192.168.7.6 console=ttySAC0,115200 init=/linuxrc"
插曲:函數指針
int* fun(int a, int b);
int (*fun)(int a, int b);
2,初始化
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
//fun = add;
fun = sub;
3,調用
fun(3, 4);
4,作用
a,產生api
b,用於抽象分層
struct stud{
int age;
int (*func)(int a, int b);
}
app
查詢: a的名字
printf("age = %d\n", p->age);
p->func(3,4);
===============================================
(核心層)鏈表:
struct stud *p; (全局)
p = 鏈表頭;
=====================================================================
(特定數據層)struct stud a;
a.age = 30000;
a.func = add;
struct stud b;
b.age = 3;
b.func = sub;
nandflash的初始化;
|
nand_init();
|Nand.c drivers\mtd\Nand 2677 2010-4-1
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
|(struct mtd_info *mtd, struct nand_chip *nand,ulong base_addr)
mtd->priv = nand;
board_nand_init(nand)// 初始化nand_chip對象
|
nand->IO_ADDR_R = (void __iomem *)NFDATA;
nand->IO_ADDR_W = (void __iomem *)NFDATA;
nand->cmd_ctrl = s3c_nand_hwcontrol;
nand->dev_ready = s3c_nand_device_ready;
nand->select_chip = s3c_nand_select_chip;
nand->options = 0;
nand_scan(mtd, maxchips) // 初始化struct nand_info
readenv
|
nand_read(
info->read() // 誰給這個read函數指針初始化
->read = xxx
初始化部分:
nand_scan
|
nand_scan_tail(mtd);
|//在這個地方給初始化了
mtd->read = nand_read; // Nand_base.c drivers\mtd\Nand 81953 2010-4-1
|
struct nand_chip *chip = mtd->priv;
nand_do_read_ops(mtd, from, &chip->ops);
|
chip->select_chip(mtd, chipnr); //select_chip在哪裏初始化
nand_scan
|
nand_scan_ident(mtd, maxchips);
|// 片選在這個初始化了
chip->select_chip = nand_select_chip;
|//chip實際就是struct nand_chip,
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
s3c_nand_hwcontrol;
board_nand_init(nand)// 初始化nand_chip對象,
|
nand->IO_ADDR_R = (void __iomem *)NFDATA;
nand->IO_ADDR_W = (void __iomem *)NFDATA;
nand->cmd_ctrl = s3c_nand_hwcontrol;
nand->dev_ready = s3c_nand_device_ready;
nand->select_chip = s3c_nand_select_chip;
nand->options = 0;
uboot命令處理的邏輯過程
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; /* Usage message (short) */
char *help; /* Help message (long) */
};
typedef struct cmd_tbl_s cmd_tbl_t;
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
U_BOOT_CMD(
tftpboot, 3, 1, do_tftpb,
"boot image via network using TFTP protocol",
"[loadAddress] [[hostIPaddr:]bootfilename]"
);
===>展開:
類型 變量名 設置屬性
struct cmd_tbl_s __u_boot_cmd_tftpboot __attribute__ ((unused,section (".u_boot_cmd"))) ={
.name = "tftpboot",
.maxargs = 3,
.repeatable = 1,
.cmd = do_tftpb,
.usage = "boot image via network using TFTP protocol",
.help = "[loadAddress] [[hostIPaddr:]bootfilename]"
}
===> 在uboot中新增一條命令:
common/cmd_hello.c
#include <common.h>
#include <command.h>
int do_mycmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf("in do_mycmd for uboot cmd test\n");
return 0;
}
U_BOOT_CMD(
mycmd, 3, 1, do_mycmd,
"this is a uboot cmd test",
"mycmd : no args"
);
common/Makefile
COBJS-$(CONFIG_CMD_HELLO) += cmd_hello.o
smdkc100.h
#define CONFIG_CMD_HELLO 1
uboot命令解析過程
mainloop:(一般不需要去修改)
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
s = getenv ("bootcmd"); //獲取bootcmd中值==>"tftp 20008000 zImage ; go 20008000"
if (bootdelay >= 0 && s && !abortboot (bootdelay)) // 倒計時
run_command (s, 0);
|
分析字符串中命令: 命令名 參數tftp 20008000 zImage
argc=>3
argv==argv[0]=="tftp"
argv[1] =="20008000"
argv[2] == "zImage"
argc = parse_line (finaltoken, argv);
cmd_tbl_t *cmdtp = find_cmd(argv[0]);
|
find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
|// cmdtp爲指針,執行.u_boot_cmd段的起始位置
for (cmdtp = table; cmdtp != table + table_len;cmdtp++)
if (strncmp (cmd, cmdtp->name, len)
return cmdtp;
(cmdtp->cmd) (cmdtp, flag, argc, argv) // 執行命令的處理函數
倒計時被打斷的時候:
for (;;) {
len = readline (CONFIG_SYS_PROMPT);
rc = run_command (lastcommand, flag);
掌握:
1, 在uboot添加命令
2, 已知的命令對應的處理函數
nand 命令==> cmd_nand.c
do_nand
dm9000網卡:
|
eth_initialize(gd->bd); // 沒有調用所有網卡的init方法
|
dm9000_initialize(bis) // 自己添加,將dm9000的對象初始化,並且設置mac地址,加入鏈表
eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);
//從環境變量中獲取,某個網卡的mac地址, mac從軟件上設定的
// mac保存在env_enetaddr
eth_init(gd->bd); // Eth.c Net 11013 2013-9-10
// 意味着鏈表中多有的節點都會執行其中的init方法
|
while循環:
eth_current->init(eth_current,bis) // 執行當前的節點的init方法
eth_try_another(0);
|
eth_current = eth_current->next;
1,dm9000==>鏈表中
dm9000_initialize(bis) ;
2,執行dm9000的init方法
eth_init(gd->bd);
COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o
struct eth_device {
char name[NAMESIZE]; // 網卡名字
unsigned char enetaddr[6]; //網卡mac地址
int iobase; //網卡的物理地址
int state; //網卡狀態
int (*init) (struct eth_device*, bd_t*); // 初始化方法
int (*send) (struct eth_device*, volatile void* packet, int length); // 發送
int (*recv) (struct eth_device*); //接收
void (*halt) (struct eth_device*); // 終止
struct eth_device *next;
void *priv;
};
int dm9000_initialize(bd_t *bis)
{
struct eth_device *dev = &(dm9000_info.netdev);
/* Load MAC address from EEPROM */
dm9000_get_enetaddr(dev);
dev->init = dm9000_init;
dev->halt = dm9000_halt;
dev->send = dm9000_send;
dev->recv = dm9000_rx;
sprintf(dev->name, "dm9000");
eth_register(dev); // 將dm9000節點放到鏈表中
return 0;
}
uboot是如何啓動內核
1, 在0x20000100去存放內存的信息和bootargs的內容
2, 將r1=1826,告訴內核
tftp 20800000 uImage \; bootm 20800000
gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; // 1826
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000+0x100
bootm==>do_bootm
|
將zImage拷貝到0x20008000
boot_fn = boot_os[images.os.os];
|
do_bootm_linux //Bootm.c (lib_arm):int do_bootm_linux
|//gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; 1826
bd_t *bd = gd->bd;
int machid = bd->bi_arch_number;
void (*theKernel)(int zero, int arch, uint params);
char *commandline = getenv ("bootargs");
theKernel = (void (*)(int, int, uint))images->ep; // 0x20008000
setup_start_tag (bd);t
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
theKernel (0, machid, bd->bi_boot_params);
全局的數據:gd
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
某個.c中想使用gd變量: 加上這句話
DECLARE_GLOBAL_DATA_PTR;
gd->flags |= GD_FLG_RELOC;
board_init
|
gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; // 1826
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000 + 0x100
env_init
|
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
init_baudrate
|
gd->bd->bi_baudrate = gd->baudrate = 115200;
serial_init ==>drivers/serial/serial_s5pc1xx.c // 一般uboot對於串口的部分,都基本上是ok
int serial_init_dev(const int dev_index)
{
struct s5pc1xx_uart *const uart = s5pc1xx_get_base_uart(dev_index);
/* reset and enable FIFOs, set triggers to the maximum */
writel(0, &uart->ufcon);
writel(0, &uart->umcon);
/* 8N1 */
writel(0x3, &uart->ulcon);
/* No interrupts, no DMA, pure polling */
writel(0x245, &uart->ucon);
serial_setbrg_dev(dev_index);
return 0;
}
gd->have_console = 1;
dram_init:
gd->bd->bi_dram[0].start = PHYS_SDRAM_1; //起始位置
gd->bd->bi_dram[0].size = get_ram_size((long *)PHYS_SDRAM_1,PHYS_SDRAM_1_SIZE); // 計算內存大小
= 256 *1024 *1024
彙總:
gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; // 1826, 機器id, uboot和內核達成的一個協議
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000 + 0x100
gd->bd->bi_baudrate = gd->baudrate = 115200;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size =256 *1024 *1024;
gd->have_console = 1;
gd->flags |= GD_FLG_RELOC;
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
串口的初始化
serial_initialize();
//將所有的外圍設備全部做成對象 struct stdio_dev, 如果想要研究uboot中有lcd,研究這塊
stdio_init (); /* get the devices list going. */
// 跳轉表
jumptable_init ();
// 將stdin, out, err==> serial
console_init_r (); /* fully init console as a device */
/* enable exceptions */
enable_interrupts ();
smdkc100所有平臺數據的註冊流程
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);
smdkc100_machine_init
|
platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices));
|
platform_device_register(devs[i]);
init/main.c
start_kernel
|
printk(KERN_NOTICE "%s", linux_banner);
setup_arch(&command_line); // 建立平臺相關的數據,會到0x200000100去uboot存放數據
|
mdesc = setup_machine(machine_arch_type); // 獲取machine描述
//struct machine_desc *mdesc===>mach-smdkc100.c==>MACHINE_START
mdesc->boot_params; //獲取0x20000100
tags = phys_to_virt(mdesc->boot_params);
parse_tags(tags); // 獲取bootargs, from就在這裏初始化
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
內核是如何去處理bootargs中的所有參數:
&unknown_bootoption);
用途:
uboot想傳遞一個自定義的值給我們內核,內核如何處理:
set bootargs myval=56 init=/linuxrc console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.7.2:/opt/filesystem ip=192.168.7.6
在內核的任何地方:
static int __init parse_myval(char *str)
{
int val = simple_strtoul(str, NULL, 10);
printk("myval = %d\n", val);
return 0;
}
__setup("myval=", parse_myval);
uboot上電完整內存使用
文章轉自:https://www.cnblogs.com/lcw/p/3802644.html