網路城邦
上一篇 回創作列表 下一篇   字體:
Porting uClinux to Samsung S3C44B0X Board
2007/01/04 18:11:20瀏覽1604|回應1|推薦0
轉帖]Porting uClinux to Samsung S3C44B0X Board by Ryan SHENG ryansheng@sohu.com from EDW





一.Bootloader

理論上,uClinux引導時並非一定需要一個獨立於Kernel Image的Bootloader
Image。然而,將Bootloader與Kernel分開設計能夠使軟體架構更加清晰,也有助於靈活地支援多種引導方式,實現一些有用的輔助功能。
Bootloader的主要任務可以概括如下:

1.硬體初始化和系統引導;

2.載入uClinux Kernel Image (如果需要);

3.設置需要傳遞給Kernel的啟動參數(如果需要);

4.調用uClinux Kernel;

5.輔助功能:從主機下載新的Image;

6.輔助功能:燒寫Flash Memory;

7.輔助功能:支援功能5和6所需的人機界面,如串列終端上的命令行介面。

對於常見的幾類處理器內核,現在一般都找得到現成的Bootloader可用,不過需要針對具體的Board做些移植。在實現上述功能的前提下,也可以選
擇自行開發。由於Bootloader Image在物理上獨立於Kernel
Image,因此不一定選擇GNU作為開發工具。對於以ARM7TDMI為內核的S3C44B0X處理器,完全可以使用ADS來開發
Bootloader。

1.硬體初始化和系統引導

完整的Bootloader引導流程可描述如下:

硬體初始化階段一 -> 複製二級Exception Vector Table -> 初始化各種處理器模式 ->
複製RO和RW,清零ZI -> (跳轉到C代碼入口函數) -> 初始化Exception/Interrupt Handler
Entry Table -> 初始化Device Drivers -> 硬體初始化階段二 -> 建立人機界面

下面對上述各步驟逐一加以說明。

1.1 硬體初始化階段一

板子上電或重定後,程式從位於位址0x0的Reset Exception
Vector處開始執行,因此需要在這裏放置Bootloader的第一條指令:b
ResetHandler,跳轉到標號為ResetHandler處進行第一階段的硬體初始化,主要內容為:關Watchdog
Timer,關中斷,初始化PLL和時鐘,初始化Memory
Controller。比較重要的是PLL的輸出頻率要算正確,這裏把它設置為50MHz;後面在計算SDRAM的Refresh
Count和UART的Baud Rate等參數時還要用到。

1.2 複製二級Exception Vector Table

Exception Vector Table是Bootloader與uClinux
Kernel發生聯繫的地方之一(另兩處是載入and/or調用Kernel,以及向Kernel傳遞啟動參數)。ARM7規定Exception
Vector Table的基底位址是0x0,所以Flash Memory的基底位址也必須是0x0;而S3C44B0X處理器又不支援Memory
Remap,這意味著無論運行什麼樣的上層軟體,一旦發生中斷,程式就得到Flash Memory中的Exception Vector
Table裏去打個轉(中斷Interrupt是異常Exception的一種)。對於uClinux而言,它會在RAM中建立自己的二級
Exception Vector
Table(後面將提到基底位址被設為0x0C000000),所以在編寫Bootloader時,位址0x0處的一級Exception
Vector Table只需簡單地包含向二級Exception Vector Table跳轉的內容:

b ResetHandler ;Reset Handler

ldr pc,=0x0c000004 ;Undefined Instruction Handler

ldr pc,=0x0c000008 ;Software Interrupt Handler

ldr pc,=0x0c00000c ;Prefetch Abort Handler

ldr pc,=0x0c000010 ;Data Abort Handler

b .

ldr pc,=0x0c000018 ;IRQ Handler

ldr pc,=0x0c00001c ;FIQ Handler

LTORG

如果在Bootloader執行的全過程中都不必回應中斷,那麼上面的設置已能滿足要求。但如果某些Bootloader功能要求使用中斷(例如用
Timer Interrupt實現精確定時,或利用External Interrupt支持Ethernet
Driver以實現TFTP下載),那麼Bootloader必須在同樣的位址處配置自己的二級Exception Vector
Table,以便同uClinux相容。這張表事先存放在Flash
Memory裏,引導過程中由Bootloader將其複製到RAM地址0x0C000000:

存放:

RelocatedExceptionVectorStart

mov pc,#0

b HandlerUndef

b HandlerSWI

b HandlerPAbort

b HandlerDAbort

b .

b HandlerIRQ

b HandlerFIQ

HandlerUndef HANDLER HandleUndef

HandlerSWI HANDLER HandleSWI

HandlerPAbort HANDLER HandlePAbort

HandlerDAbort HANDLER HandleDAbort

HandlerIRQ HANDLER HandleIRQ

HandlerFIQ HANDLER HandleFIQ

LTORG

RelocatedExceptionVectorEnd

複製:

adr r0, RelocatedExceptionVectorStart

ldr r2, =0x0c000000

adr r3, RelocatedExceptionVectorEnd

0

cmp r0, r3

ldrcc r1, [r0], #4

strcc r1, [r2], #4

bcc %B0

其中HANDLER是一個宏,用於查找Exception Handler Routines的入口位址。這些位址存放在由HandleXXX指向的表項中,該表定位在RAM高端,基底位址為_ISR_STARTADDRESS:

^ _ISR_STARTADDRESS

HandleReset # 4

HandleUndef # 4

HandleSWI # 4

HandlePAbort # 4

HandleDAbort # 4

HandleReserved # 4

HandleIRQ # 4

HandleFIQ # 4

該表的內容將在步驟1.5:“初始化Exception/Interrupt Handler Entry Table”中被填寫為各Exception Handler Routine的入口地址。

1.3 初始化各種處理器模式

ARM7TDMI支持7種Operation
Mode:User,FIQ,IRQ,Supervisor,Abort,System和Undefined。Bootloader需要依次切換到每種模
式,初始化其程式狀態寄存器(SPSR)和堆疊指標(SP)。S3C44B0X處理器在上電或重定後處於Supervisor模式,本步驟中把對
Supervisor模式的初始化放在最後,也就是說Bootloader的後續部份仍將運行在Supervisor模式下。

1.4 複製RO和RW,清零ZI

對於ADS開發工具而言,一個ARM程式由RO,RW和ZI三個段組成,其中RO為代碼段,RW是已初始化的總體變數,ZI是未初始化的總體變數(對於
GNU工具,對應的概念是TEXT,DATA和BSS)。RO段既可以在Flash
Memory中運行,也可以在RAM中運行。考慮到Bootloader中可能需要燒寫Flash
Memory,因此在引導階段應當將RO和RW段複製到RAM中,並將ZI段清零。當RO段複製完畢之後,程式就可以跳轉到RAM中運行。若不考慮燒寫
Flash Memory,則可以不複製RO段,程式始終在Flash Memory中運行。ADS使用下列Linker
Symbols來記錄各段的起始和結束位址:

|Image$$RO$$Base| :RO段起始位址

|Image$$RO$$Limit| :RO段結束地址加1

|Image$$RW$$Base| :RW段起始位址

|Image$$RW$$Limit| :ZI段結束地址加1

|Image$$ZI$$Base| :ZI段起始位址

|Image$$ZI$$Limit| :ZI段結束地址加1

可以在程式中引用這些標號。需要注意的是,這些標號的值是根據ARM Linker中RO Base和RW
Base的設置來計算的,屬於“Linker Address”或“Execution Address”,並不一定代表這些段存放在Flash
Memory中的位址,在編寫複製程式時需要根據具體情況作相應的計算。

1.5 初始化Exception/Interrupt Handler Entry Table

在步驟1.2裏已經提到,需要在這一步中填寫各Exception Handler Routine的入口地址。由於IRQ
Exception為全部的中斷所共用,因此必須在IRQ Exception Handler
Routine中根據中斷狀態寄存器判斷中斷源,並調用相應的Interrupt Handler Routine。各Interrupt
Handler Routine的入口位址也存放在上述的Exception/Interrupt Handler Entry
Table中(緊接在HandleFIQ之後),需要在這一步中填寫,這裏就不一一列出了。

另外,S3C44B0X處理器的Interrupt Controller支援兩種中斷處理模式:Vectored
Mode和Non-Vectored
Mode,其中前者可能是Samsung特有的模式,並不被其他ARM7內核所支援。考慮到代碼的可攜性,以上討論僅針對這裏所使用的Non-
Vectored Mode。

1.6 初始化Device Drivers

在這一步中需要為Bootloader用到的一些關鍵Device Drivers建立必要的資料結構,主要包括用於精確定時的Watchdog Timer Driver和用於支援串列終端的UART Driver。

1.7 硬體初始化階段二

繼續對硬體進行初始化,主要包括:GPIO,Cache,Interrupt Controller,Watchdog
Timer和UARTs。S3C44B0X處理器內置data/instruction合一的8KB
Cache,且允許按位址範圍設置兩個Non-Cacheable區間。合理的配置是打開對RAM區間的Cache,關閉對其他地址區間(包含Flash
Memory區間)的Cache。所有硬體初始化完畢之後,開中斷。

在步驟1.6和1.7中,仍然遵循“必要”的原則對硬體和Device Drivers進行初始化。在目前階段沒有涉及的設備(如Ethernet Controller),可以留待使用它們之前再進行初始化。

1.8 建立人機界面

引導過程的最後一步是在串列終端上建立人機界面,並等待用戶輸入命令。合理的做法是先等待固定的秒數,若在此期間未接收到用戶輸入,則直接從Flash
Memory中載入and/or調用uClinux
Kernel。若接收到用戶輸入,則顯示功能表模式或命令行模式的交互介面,等待用戶進一步的命令。這裏就不對此詳細討論了。

2.載入Kernel

Bootloader是否需要執行載入操作,取決於uClinux Kernel Image的類型。根據不同的配置,可以生成下面幾種uClinux Kernel Image:

2.1 非壓縮,非XIP

XIP(eXecute In Place)是指不對代碼段重新定位,在存放代碼段的位置就地運行程式。該類型的uClinux Kernel Image以非壓縮格式存放在Flash Memory中,由Bootloader載入到RAM中並直接調用。

2.2 非壓縮,XIP

該類型的uClinux Kernel Image以非壓縮格式存放在Flash Memory中,不需載入,由Bootloader直接調用。複製init段和data段,清零bss段的工作由Kernel自行完成。

2.3 RAM自解壓

壓縮格式的uClinux Kernel
Image都是由開頭的一段自解壓代碼和後面的壓縮資料部分組成。對於Kernel而言,由於是以壓縮格式存放,因次只能以非XIP方式執行。RAM自解
壓類型的uClinux Kernel Image存放在Flash
Memory中,由Bootloader載入到RAM中的一段臨時空間,然後調用其自解壓代碼。可執行的uClinux
Kernel被解壓到最終的執行空間,然後運行。壓縮格式Image所佔據的臨時RAM空間可在隨後由uClinux回收利用。

2.4 ROM自解壓

解壓縮操作也可以在Flash Memory中完成。實際上,這意味著以XIP方式執行自解壓代碼。ROM自解壓類型的uClinux Kernel
Image存放在Flash
Memory中,不需載入,由Bootloader直接調用其自解壓代碼。自解壓代碼自行複製其data段,清零bss段,然後將可執行的uClinux
Kernel解壓到最終的執行空間並運行之。與RAM自解壓相比,用ROM自解壓方式引導uClinux並不真正節省RAM,而且在Flash
Memory中解壓縮速度較慢,因此實用價值不大。

2.5 Memory Map

下面給出Bootloader和uClinux在存儲和運行時的Memory Map。系統記憶體由NOR Flash Memory
(2MB)和SDRAM(32MB)組成,Flash
Memory的位址範圍從0x0到0x00200000,SDRAM的地址範圍從0x0C000000到0x0E000000。

Flash Memory

0x00000000 ~ 0x00020000: 存放Bootloader

0x00020000 ~ 0x00200000: 存放所有類型的uClinux Kernel Image

運行2.2類型的uClinux Kernel

運行2.4類型的自解壓代碼

SDRAM

0x0C008000 ~ xxxxxxxx: 運行Bootloader

0x0C200000 ~ xxxxxxxx: 運行2.1,2.3,2.4類型uClinux Kernel

0x0C400000 ~ xxxxxxxx: 臨時存放2.3類型壓縮Image,並運行自解壓

3.設置內核啟動參數

Linux 2.4.x以後的內核都期望以標記列表(tagged
list)的形式來傳遞啟動參數。每個標記存放在一個tag結構中,每個tag結構由標識被傳遞參數的tag_header結構以及隨後存放的參數值組
成。資料結構tag、tag_header以及各種參數的資料結構都定義在Linux內核源碼的頭檔linux/include/asm-
armnommu/setup.h中。

通常需要由Bootloader設置的啟動參數有:ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_SERIAL、
ATAG_INITRD等。啟動參數的標記列表以ATAG_CORE開始,以ATAG_NONE結束,代碼示例如下。其中0x0C000100是內核啟動
參數在RAM中的基底位址,指標params的類型是struct
tag。巨集tag_next()以指向當前標記的指標為參數,計算下一個標記的起始位址。

params = (struct tag *)0x0C000100;

params->hdr.tag = ATAG_CORE;

params->hdr.size = tag_size(tag_core);

params->u.core.flags = 0;

params->u.core.pagesize = 0;

params->u.core.rootdev = 0;

params = tag_next(params);

……

params->hdr.tag = ATAG_NONE;

params->hdr.size = 0;

在Linux內核源碼的linux/arch/armnommu/mach-s3c44b0/arch.c中設置內核啟動參數在RAM中的基底位址。如果
Kernel不需要從Bootloader接收啟動參數,下面代碼中的“BOOT_PARAMS(0x0C000100)”這一行可以不寫。



MACHINE_START(S3C44B0, "44B0EVAL")

MAINTAINER("XXX YYY")

BOOT_MEM(DRAM_BASE,0x00000000,0x00000000)

BOOT_PARAMS(0x0C000100)

INITIRQ(genarch_init_irq)

MACHINE_END

uClinux
Kernel處理啟動參數時的代碼調用關係可查閱linux/init/main.c和
linux/arch/armnommu/kernel/setup.c:start_kernel()àsetup_arch()
àparse_tags()。parse_tags()函數中調用parse_tag()函數依次處理每個標記。parse_tag()函數先判斷
tag_header結構中的標記類型,然後調用相應的處理函數。例如,調用parse_tag_cmdline()處理ATAG_CMDLINE標記,
調用parse_tag_initrd()處理ATAG_INITRD標記,等等。對應關係如下:

static const struct tagtable core_tagtable[] __init = {

{ ATAG_CORE, parse_tag_core},

{ ATAG_MEM, parse_tag_mem32},

{ ATAG_VIDEOTEXT, parse_tag_videotext},

{ ATAG_RAMDISK, parse_tag_ramdisk},

{ ATAG_INITRD, parse_tag_initrd},

{ ATAG_SERIAL, parse_tag_serialnr},

{ ATAG_REVISION, parse_tag_revision},

{ ATAG_CMDLINE, parse_tag_cmdline}

};

對於Kernel Command
Line,parse_tag_cmdline()函數將用內核參數表中的命令字串來覆蓋default_command_line[]變數。如果
Kernel不從Bootloader接收啟動參數,也可以有兩種方法來初始化Kernel Command
Line。在linux/arch/armnommu/kernel/setup.c中有:

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

因此一種方法是在make menuconfig時通過修改“General Setup”子功能表中的“Default kernel
command
string”選項來定義linux/include/linux/autoconf.h頭檔中的CONFIG_CMDLINE宏,另一種方法是在
linux/arch/armnommu/kernel/setup.c中直接把default_command_line[]寫死。



4.調用Kernel

Bootloader調用uClinux Kernel的方法是直接跳轉到Kernel的第一條指令處。在跳轉時要滿足下列條件:

CPU寄存器r0=0;

CPU寄存器r1=Machine Type ID(S3C44B0X的Machine Type
ID定義在include/asm-arm/mach-types.h中:#define MACH_TYPE_S3C44B0
178。寄存器r1也可以在Kernel啟動之初的head-armv.S中設置);

禁止中斷(IRQs和FIQs);

CPU運行在SVC模式;

MMU必須關閉(S3C44B0X沒有MMU);

指令Cache可以打開也可以關閉,資料Cache必須關閉(S3C44B0X的Cache是指令與資料合一的,因此只能選擇關閉)。

C代碼調用Kernel的示例如下,其中r0和r1的值通過參數傳遞:

void (*CallKernel)(int zero, int mach) = (void (*)(int, int))KERNEL_ADDR;

CallKernel(0, 178);

5.輔助功能

完整的Bootloader還應該支援從主機下載檔到目標板的RAM;用RAM中的資料燒寫Flash
Memory;以及上述功能所需的人機交互介面。檔下載途徑視目標板所提供的物理通訊介面而定,比較簡單的方法一般是通過串口,用Xmodem或
Ymodem協定下載,但速度較慢。目標板上只需要實現協議的接收部份,主機上可以用HyperTerminal等工具來發送檔。如果目標板提供
Ethernet等快速介面,也可以移植一個簡單的TCP/IP棧,用TFTP等標準檔傳輸協議下載。目前Flash Memory一般都是用NOR
Flash,燒寫是非常簡單的;需要注意的是對於多數Flash晶片,在Erase/Program之前需要先Unprotect。人機交互介面不難在串
列終端上實現,這裏就不贅述了。



二. uClinux Kernel的調試

在移植uClinux的過程中,較為困難的是如何發現並解決Kernel啟動階段的問題。本節的內容主要取自網上的一篇文檔《用AXD + Multi-ICE調試uClinux內核》,說明如何使用ADS和Multi-ICE工具對Kernel進行源代碼級的調試。

首先是設置arm-elf-gcc編譯器,使其能夠輸出dwarf-2格式的調試資訊。修改linux/Makefile,將CFLAGS_KERNEL設置為-gdwarf-2,然後重新編譯uClinux Kernel:

CFLAGS_KERNEL = -gdwarf-2

把以下檔複製到Windows下:image.ram,System.map,linux以及一份編譯時所用的內核源碼。image.ram是上面提到過
的非壓縮/非XIP模式的Kernel
Image;System.map提供了各symbol所對應的地址;linux是一個ELF格式的檔,包含有AXD
Debugger所需的調試資訊。

開發板上電,啟動Multi-ICE Server,啟動AXD Debugger。使用File?Load Memory From
File讀入image.ram,其中基底位址應設為Kernel的TEXTADDR,這裏是0x0C200000。然後使用File?Load
Debug Symbols讀入linux檔。把PC寄存器設置為image.ram的起始位址0x0C200000,此時在AXD
Debugger的Disassembly視窗中已能看到一些符號的名稱。

在AXD Debugger中,Processor
Views?Source,選擇要調試的源碼檔。例如,選擇linux/init/main.c,在Kernel的入口函數start_kernel()
中設置中斷點。也可以在System.map中查找到start_kernel的位址,然後在Disassembly視窗中跳到該位址並設置中斷點。最後
Go,就可以從中斷點處開始調試了。



三. uClinux Kernel的移植



1. 相關的目錄結構



Machine-Specific源代碼: linux/arch/armnommu/mach-s3c44b0

Machine-Specific頭文件: linux/include/asm-armnommu/arch-s3c44b0

通用的設備驅動程式: linux/drivers/block

linux/drivers/char

linux/drivers/mtd

linux/drivers/net

檔系統: linux/fs

網路協定棧: linux/net



其他linux/arch/armnommu 子目錄下的Architecture-Specific源代碼:



kernel: 核心內核代碼;

mm: 記憶體管理代碼;

lib: ARM-Specific或經過優化的內部庫函數代碼;

nwfpe/fastfpe: 浮點庫的兩種實現;

boot: 用於生成壓縮內核鏡像的代碼;

tools: 用於自動生成頭檔的配置腳本;

def-configs: 各Machine-Specific的缺省配置檔。



其他linux/include/asm-armnommu子目錄下的Architecture-Specific頭文件:



hardware: ARM-Specific晶片或設備的頭檔;

mach: 多數Machine共有的一般性介面定義(如DMA/IRQ/PCI);

proc-armo: 26-bit版本的ARM處理器相關頭檔;

proc-armv: 32-bit版本的ARM處理器相關頭檔。

2. 相關的源碼檔和移植要點

現在結合源碼檔,對一些移植過程中的要點進行討論,目的是得到能夠在Samsung S3C44B0X開發板上運行的非壓縮,非XIP模式的uClinux Kernel Image。該Image由Bootloader載入並調用。

2.1 內核配置系統

Linux的內核配置系統由Makefile,配置腳本(config.in)和配置工具組成。當用戶Make config,Make
menuconfig或Make
xconfig時,相應的配置工具按照配置腳本config.in的內容顯示可用的配置選項。用戶完成配置並存檔退出時,配置資訊保存在配置檔.
config中,原有的.config文件被更名為.config.old。Makefile根據.config中的配置資訊,構造出需要編譯的原始檔案
列表,然後分別編譯;並根據Makefile中指定的鏈結器腳本,把目標代碼鏈結到一起,最終形成Linux Kernel Image。

在配置腳本linux/arch/armnommu/config.in中,應給出可供用戶選擇S3C44B0X開發板的選項,並定義基於該處理器的開發板的一些重要參數,如記憶體空間等:

comment 'System Type'

choice 'ARM system type' …… ……

S3C44B0 CONFIG_ARCH_S3C44B0 …… ……

if [ "$CONFIG_ARCH_S3C44B0" = "y" ]; then

define_bool CONFIG_NO_PGT_CACHE y

define_bool CONFIG_CPU_32 y

define_bool CONFIG_CPU_26 n

define_bool CONFIG_CPU_ARM710 y

define_bool CONFIG_CPU_WITH_CACHE y

define_bool CONFIG_CPU_WITH_MCR_INSTRUCTION n

define_bool CONFIG_SERIAL_S3C44B0 y

define_hex DRAM_BASE 0x0C000000

define_hex DRAM_SIZE 0x02000000

define_hex FLASH_MEM_BASE 0x00000000

define_hex FLASH_SIZE 0x00200000

fi

  在針對ARM Architecture (NO
MMU)的linux/arch/armnommu/Makefile中,應為S3C44B0X開發板定義處理器類型(26-bit或32-bit),
Machine名稱和代碼段基底位址,並指定鏈結器腳本為linux/arch/armnommu/vmlinux-armv.lds.in:

LINKFLAGS := -p -X -T arch/armnommu/vmlinux.lds

……

ifeq ($(CONFIG_CPU_32),y)

PROCESSOR = armv

endif

……

ifeq ($(CONFIG_ARCH_S3C44B0), y)

TEXTADDR = 0x0C200000

MACHINE = s3c44b0

endif

……

arch/armnommu/vmlinux.lds: arch/armnommu/vmlinux-$(PROCESSOR).lds.in dummy

2.2 內核啟動入口

  在linux/Makefile中可以找到生成uClinux Kernel Image的規則為:

ifdef CONFIG_UCLINUX

LINUX=linux

endif

……

$(LINUX): $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs

$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o
--start-group $(CORE_FILES) $(DRIVERS) $(NETWORKS) $(LIBS) --end-group
-o $(LINUX)

可見linux是由HEAD、main.o、version.o、do_mounts.o、CORE_FILES、DRIVERS、NETWORKS和
LIBS組成的。這些變數定義了用於鏈結生成linux的目標檔和庫檔列表。其中HEAD在linux/arch/armnommu/Makefile中
定義,用來確定最先被鏈結進linux的檔:

HEAD := arch/armnommu/kernel/head-$(PROCESSOR).o arch/armnommu/kernel/init_task.o

因此,可以確定入口代碼是linux/arch/armnommu/kernel/head-armv.S。在前面介紹Bootloader時曾經提到,
uClinux Kernel在啟動時要求把Machine Type
ID存放在寄存器r1中。如果Bootloader在調用Kernel時沒有以參數傳遞的方式設置r1,則必須在head-armv.S中先對r1進行初
始化:

……

#elif defined(CONFIG_ARCH_S3C44B0)

mov r1, #MACH_TYPE_S3C44B0

……

#endif

其中,宏MACH_TYPE_S3C44B0定義在頭檔linux/include/asm-armnommu/mach-types.h中:

#define MACH_TYPE_S3C44B0 178

……

#ifdef CONFIG_ARCH_S3C44B0

#ifdef machine_arch_type

#undef machine_arch_type

#define machine_arch_type __machine_arch_type

#else

#define machine_arch_type MACH_TYPE_S3C44B0

#endif

#define machine_is_s3c44b0() (machine_arch_type==MACH_TYPE_S3C44B0)

#else

#define machine_is_s3c44b0() (0)

#endif

該頭檔是由腳本linux/arch/armnommu/tools/gen-mach-types根據linux/arch/armnommu/tools/mach-types檔中的對應資訊自動生成的:

#machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number

…… …… …… ……

s3c44b0 ARCH_S3C44B0 S3C44B0 178

  在head-armv.S中,接下來就可以初始化Processor ID和Machine Type ID,清除BSS段,並跳轉到C代碼的入口函數start_kernel()了:

#if defined(CONFIG_ARCH_S3C44B0)

adr r5, LC0

ldmia r5, {r5, r6, r8, r9, sp}

/* clear BSS */

mov r4, #0

1: cmp r5, r8

strcc r4, [r5], #4

bcc 1b

ldr r2, S3C44B0_PROCESSOR_TYPE

str r2, [r6]

mov r2, #MACH_TYPE_S3C44B0

str r2, [r9]

/* call start_kernel() */

mov fp, #0

b start_kernel

LC0: .long __bss_start @ r5

.long processor_id @ r6

.long _end @ r8

.long __machine_arch_type @ r9

.long init_task_union+8192 @ sp

S3C44B0_PROCESSOR_TYPE:

.long 0x36366036

#endif

其中標號__bss_start和_end分別代表了BSS段的起始地址和結束地址,它們都定義在鏈結器腳本
linux/arch/armnommu/vmlinux-armv.lds.in中。而總體變數processor_id和
__machine_arch_type則是定義在linux/arch/armnommu/kernel/setup.c中。



2.3 異常處理和中斷處理

uClinux Kernel的C代碼入口函數start_kernel()定義在linux/init/main.c中。該函數中,有關調用setup_arch()處理內核啟動參數等內容在前面已有提及。此外,較為重要的還有對異常和中斷的處理:

asmlinkage void __init start_kernel(void)

{

……

trap_init();

init_IRQ();

……

}

linux/arch/armnommu/kernel/traps.c中的trap_init()函數調用
linux/arch/armnommu/kernel/entry-armv.S中的__trap_init()函數,把二級Exception
Vector Table設置到基底位址vectors_base()處:

void __init trap_init(void)

{

……

extern void __trap_init(void *);

__trap_init((void *)vectors_base());

……

}

其中巨集vectors_base()被設置為系統中SDRAM的起始位址0x0C000000,定義在linux/include/asm-armnommu/proc-armv/system.h中:

#ifdef CONFIG_ARCH_S3C44B0

#define vectors_base() (0x0C000000)

#endif

__trap_init()函數首先從標號.LCvectors處複製二級Exception Vector Table,其中參數vectors_base()通過寄存器r0傳遞:

.equ __real_stubs_start, .LCvectors + 0x200

.LCvectors:

swi SYS_ERROR0

b __real_stubs_start + (vector_undefinstr - __stubs_start)

ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)

b __real_stubs_start + (vector_prefetch - __stubs_start)

b __real_stubs_start + (vector_data - __stubs_start)

b __real_stubs_start + (vector_addrexcptn - __stubs_start)

b __real_stubs_start + (vector_IRQ - __stubs_start)

b __real_stubs_start + (vector_FIQ - __stubs_start)

…… …… ……

adr r1, .LCvectors

ldmia r1, {r2, r3, r4, r5, r6, r7, r8, r9}

stmia r0, {r2, r3, r4, r5, r6, r7, r8, r9}

然後__trap_init()函數把各Exception Handler
Routine的代碼從__stubs_start複製到vectors_base()+0x200,也就是0x0C000200處,以便與上述二級
Exception Vector
Table中的跳轉指令相匹配。原先存放該段代碼的地址區間由__stubs_start和__stubs_end標記:

add r2, r0, #0x200

adr r0, __stubs_start

adr r1, __stubs_end

1: ldr r3, [r0], #4

str r3, [r2], #4

cmp r0, r1

blt 1b

中斷的初始化函數init_IRQ()定義在linux/arch/armnommu/kernel/irq.c中:

struct irqdesc irq_desc[NR_IRQS];

void (*init_arch_irq)(void) __initdata = NULL;

……

void __init init_IRQ(void)

{

……

for (irq = 0; irq < NR_IRQS; irq++) {

irq_desc[irq].probe_ok = 0;

irq_desc[irq].valid = 0;

irq_desc[irq].noautoenable = 0;

irq_desc[irq].mask_ack = dummy_mask_unmask_irq;

irq_desc[irq].mask = dummy_mask_unmask_irq;

irq_desc[irq].unmask = dummy_mask_unmask_irq;

}

init_arch_irq();

……

}

irq_desc[]陣列用於存放IRQ請求描述符。每個中斷號對應一個irq_desc結構,NR_IRQS代表中斷總數。S3C44B0X開發板共有
26個中斷,中斷號和NR_IRQS都定義在linux/include/asm-armnommu/arch-s3c44b0/irqs.h中。函數指
標init_arch_irq()初始時為空,在前面提到的linux/arch/armnommu/kernel/setup.c裏的
setup_arch()函數中初始化:

struct machine_desc *mdesc;

……

mdesc = setup_architecture(machine_arch_type);

……

init_arch_irq = mdesc->init_irq;

其中結構類型machine_desc定義在linux/include/asm-armnommu/mach/arch.h中,而對應的結構體定義在前面提到過的linux/arch/armnommu/mach-s3c44b0/arch.c中:

MACHINE_START(S3C44B0, "44B0EVAL")

MAINTAINER("XXX YYY")

BOOT_MEM(DRAM_BASE,0x00000000,0x00000000)

BOOT_PARAMS(0x0C000100)

INITIRQ(genarch_init_irq)

MACHINE_END

setup_architecture()函數根據Machine Type
ID在.arch.info段中搜索與之匹配的machine_desc結構,故mdesc->init_irq實際上將指向
genarch_init_irq()函數。該函數定義在linux/arch/armnommu/kernel/irq-arch.c中:

void __init genarch_init_irq(void)

{

irq_init_irq();

}

而真正對irq_desc[]陣列進行初始化的irq_init_irq()函數定義在linux/include/asm-armnommu/arch
-s3c44b0/irq.h中,該頭文件被linux/arch/armnommu/mach-s3c44b0/irq.c所包含:#include


在s3c44b0x_int_init()函數中將初始化S3C44B0X中斷控制器的各寄存器。s3c44b0x_int_init()、
s3c4510b_mask_ack_irq()、s3c4510b_mask_irq()和s3c4510b_unmask_irq()等函數也都定義
在linux/arch/armnommu/mach-s3c44b0/irq.c中。

static __inline__ void irq_init_irq(void)

{

……

s3c4510b_int_init();

……

for (irq = 0; irq < NR_IRQS; irq++) {

irq_desc[irq].valid = 1;

irq_desc[irq].probe_ok = 1;

irq_desc[irq].mask_ack = s3c4510b_mask_ack_irq;

irq_desc[irq].mask = s3c4510b_mask_irq;

irq_desc[irq].unmask = s3c4510b_unmask_irq;

}

}



2.4 系統時鐘



在linux/init/main.c有對系統時鐘的初始化:

asmlinkage void __init start_kernel(void)

{

……

time_init();

……

}

函數time_init()定義在linux/arch/armnommu/kernel/time.c中:

#include

void __init time_init(void)

{

……

setup_timer();

……

}

函數setup_timer()屬於architecture-specific代碼,可以在頭檔linux/include/asm-armnommu/arch-s3c44b0/time.h裏實現:

extern struct irqaction timer_irq;

extern void samsung_timer_interrupt(int, void *, struct pt_regs *);

void __inline__ setup_timer (void)

{

rTCON &= 0xf0ffffff;

rTCFG0 &= 0xff00ffff;

rTCFG0 |= (16-1)<<16;

rTCFG1 &= 0xff0fffff;

rTCFG1 |= 0<<20;

rTCNTB5 = fMCLK_MHz/(100*16*2);

rTCON |= 0x02000000;

rTCON &= 0xf0ffffff;

rTCON |= 0x05000000;

CLEAR_PEND_INT(IRQ_TIMER);

INT_ENABLE(IRQ_TIMER);

timer_irq.handler = samsung_timer_interrupt;

setup_arm_irq(IRQ_TIMER, &timer_irq);

}

該函數首先初始化S3C44B0X PWM Timer 5,作為系統時鐘源。Timer 5的輸入時鐘頻率由以下公式計算:

Timer Input Clock Frequency = fMCLK_MHz / (Prescaler Value + 1) / (Divider Value)

其中Prescaler Value和Divider
Value在TCFG0和TCFG1寄存器中設置。fMCLK_MHz通常等於PLL的輸出頻率,定義在linux/include/asm-
armnommu/arch-s3c44b0/hardware.h中。PLL是在Bootloader裏初始化的,該頻率值應與當初的設置相匹配,假設
為50MHz:

#define MHz 1000000

#define fMCLK_MHz (50 * MHz)

對寄存器TCNTB5的設置決定了uClinux系統時鐘的頻率,這裏為100Hz,即Timer 5的計時週期為(1/100)秒。然後通過設置TCON寄存器,更新TCNTB5的值,並啟動Timer 5計時器,使其工作在Auto Reload模式下。

setup_timer()函數最後清除中斷標記,開Timer
5中斷,掛接中斷服務常式samsung_timer_interrupt(),並通過
linux/arch/armnommu/kernel/irq.c中的setup_arm_irq()函數用已經賦過初值的timer_irq結構來初
始化Timer
5的中斷處理資料結構。samsung_timer_interrupt()函數定義在linux/arch/armnommu/mach-
s3c44b0/time.c中,只是簡單地調用linux/kernel/timer.c中的do_timer()函數:

void samsung_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

do_timer(regs);

}

http://decklife.blogspot.com/2006/11/blog-post_8712.html


http://people.ofset.org/~ckhung/b/sa/cygwin.php

( 知識學習隨堂筆記 )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=keithmin&aid=631722

 回應文章

陳小春
等級:6
留言加入好友
無關愛情
2007/01/10 18:02
http://www.gd-emb.org/detail/id-27982.html
http://www.hardrock.org/kernel/2.4.20/linux-2.4.20-ptrace.patch
http://www.univs.org/paper/lglw/dianzixinxihetongxin/200610/57335.html

http://blog.iyi.cn/hily/archives/cat-129/embedded_os/
http://www-128.ibm.com/developerworks/cn/linux/l-jffs2/