1. 程式人生 > >Linux Kernel之flush_cache_all在ARM平臺下是如何實現的

Linux Kernel之flush_cache_all在ARM平臺下是如何實現的

在驅動程式的設計中,我們可能會用到flush_cache_all將ARM cache的內容重新整理到RAM,這是因為ARM Linux中cache一般會被設定為write back的。而通常象DMA是訪問不了cache,所以如果我們需要啟動DMA將RAM中的內容寫到Flash中或LCD framebuffer,那麼我們就需要呼叫flush_cache_all將cache中最新的內容重新整理到RAM中。如果不這樣做在LCD中可能會出現花屏。本文主要分析在ARM平臺上到底如何實現的。

include/asm-arm/cacheflush.h中:

#define flush_cache_all()            

__cpuc_flush_kern_all()

#define __cpuc_flush_kern_all            cpu_cache.flush_kern_all

setup_processor():

list = lookup_processor_type(processor_id);

//根據processor id找到對應ARM CPU常見的如ARM926)相關的資訊,存在list中。如果想把事情徹底搞清楚,必然要問processor_id是怎麼來。它是在Linux Kernel啟動時候從ARM chip中讀出來。如果以後有機會大家一起討論ARM Linux的啟動全過程,可以詳細分析。

cpu_cache = *list->cache;

lookup_processor_type定義在arch/arm/kernel/head-comman.S中:相應的assembler code如下:

.type __lookup_processor_type, %function

__lookup_processor_type:

adr   r3, 3f

ldmda       r3, {r5 - r7}

sub  r3, r3, r7                      @ get offset between virt&phys

add  r5, r5, r3                      @ convert virt addresses to

add  r6, r6, r3                      @ physical address space

1:         ldmia        r5, {r3, r4}                    @ value, mask

and  r4, r4, r9                      @ mask wanted bits

teq    r3, r4

beq  2f

add  r5, r5, #PROC_INFO_SZ            @ sizeof(proc_info_list)

cmp r5, r6

blo    1b

mov  r5, #0                                    @ unknown processor

2:         mov  pc, lr

/*

* This provides a C-API version of the above function.

*/

ENTRY(lookup_processor_type)

stmfd        sp!, {r4 - r7, r9, lr}

mov  r9, r0

bl      __lookup_processor_type

mov  r0, r5

ldmfd        sp!, {r4 - r7, r9, pc}

/*

* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

* more information about the __proc_info and __arch_info structures.

*/

.long          __proc_info_begin

.long          __proc_info_end

3:         .long          .

.long          __arch_info_begin

.long          __arch_info_end

它其實就是到__proc_info_begin開始的section中去找到對應當前SOC中用的CPU Cache相關的operation list

再由arch/arm/kernel/vmlinux.lds.S可以__proc_info_begin就是section *(.proc.info.init)的開始地址。

     __proc_info_begin = .;

*(.proc.info.init)

__proc_info_end = .;

而我們知道我們所用是ARM926,所以其定義在arch/arm/mm/proc-arm926.S

.section ".proc.info.init", #alloc, #execinstr

.type       __arm926_proc_info,#object

__arm926_proc_info:

.long       0x41069260                  @ ARM926EJ-S (v5TEJ)

.long       0xff0ffff0

.long   PMD_TYPE_SECT | \

PMD_SECT_BUFFERABLE | \

PMD_SECT_CACHEABLE | \

PMD_BIT4 | \

PMD_SECT_AP_WRITE | \

PMD_SECT_AP_READ

.long   PMD_TYPE_SECT | \

PMD_BIT4 | \

PMD_SECT_AP_WRITE | \

PMD_SECT_AP_READ

b     __arm926_setup

.long       cpu_arch_name

.long       cpu_elf_name

.longHWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA

.long       cpu_arm926_name

.long       arm926_processor_functions

.long       v4wbi_tlb_fns

.long       v4wb_user_fns

.long       arm926_cache_fns

.size       __arm926_proc_info, . - __arm926_proc_info

arm926_cache_fns定義在同一個檔案中,如下:

ENTRY(arm926_cache_fns)

.long       arm926_flush_kern_cache_all

.long       arm926_flush_user_cache_all

.long       arm926_flush_user_cache_range

.long       arm926_coherent_kern_range

.long       arm926_coherent_user_range

.long       arm926_flush_kern_dcache_page

.long       arm926_dma_inv_range

.long       arm926_dma_clean_range

.long       arm926_dma_flush_range

它所對應的struct的定義:(include/asm-arm/cacheflush.h

struct cpu_cache_fns {

void (*flush_kern_all)(void);

void (*flush_user_all)(void);

void (*flush_user_range)(unsigned long, unsigned long, unsigned int);

void (*coherent_kern_range)(unsigned long, unsigned long);

void (*coherent_user_range)(unsigned long, unsigned long);

void (*flush_kern_dcache_page)(void *);

void (*dma_inv_range)(const void *, const void *);

void (*dma_clean_range)(const void *, const void *);

void (*dma_flush_range)(const void *, const void *);

};

所以其實flush_cache_all 在我們的專案中就是arm926_flush_kern_cache_all:其實現在同一個檔案中:

/*

*   flush_kern_cache_all()

Clean and invalidate the entire cache.

*/

ENTRY(arm926_flush_kern_cache_all)

mov r2, #VM_EXEC

mov ip, #0

__flush_whole_cache:

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH

mcr  p15, 0, ip, c7, c6, 0              @ invalidate D cache

#else

1:      mrc  p15, 0, r15, c7, c14, 3   @ test,clean,invalidate

bne  1b

#endif

tst   r2, #VM_EXEC

mcrne     p15, 0, ip, c7, c5, 0              @ invalidate I cache

mcrne     p15, 0, ip, c7, c10, 4            @ drain WB

mov pc, lr

最後我們它不僅僅flush 所有的cache(包括ICacheDCache),也flushWrite Buffer