OpenHarmony(2)
代码仓:https://codechina.csdn.net/fu851523125/rtos
从头看起,看有没有什么改变。
harmonyos/kernel/liteos_a/arch/arm/arm/src/startup/下有 reset_vector_mp.S 、reset_vector_up.S两个文件,一下子看不出来啥区别,但在harmonyos/kernel/liteos_a/platform/main.c中有
#if (LOSCFG_KERNEL_SMP == YES)
" * %d\n"
"Run Mode : SMP\n"
#else
"\n"
"Run Mode : UP\n"
#endif
所以reset_vector_up.S可以理解为单核的vectors代码, reset_vector_mp.S为多核SMP方式的vectors代码。
为了解SMP,选择HI3516DV300,选择看reset_vector_mp.S。
先了解LDS链接脚本在哪,搜索vectors,在harmonyos/kernel/liteos_a/platform/下的board.ld.S
#include "include/board.h"
#define TEXT_BASE KERNEL_VADDR_BASE
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
MEMORY
{
ram : ORIGIN = KERNEL_VADDR_BASE, LENGTH = KERNEL_VADDR_SIZE
sram : ORIGIN = 0x40000000, LENGTH = 0x1000
user_ram : ORIGIN = 0x1000000, LENGTH = 0x100000
}
SECTIONS
{
/DISCARD/ : { *(.comment .note) }
.ram_vectors TEXT_BASE : {
__ram_vectors_vma = .;
KEEP (*(.vectors))
} > ram
__ram_vectors_lma = LOADADDR(.ram_vectors);
}
USER_INIT_VM_START = 0x1000000;
内存有ram、sram、user_ram三段
KERNEL_VADDR_BASE 为 0x40000000,KERNEL_VADDR_SIZE为0x20000000(512MB)
没看到.data .bss相关的,说明还有,board.ld.S预编译为board.ld,搜索board.ld,在kernel/liteos_a/tools/build/下,有liteos.ld、liteos_llvm.ld,使用SDK的gcc,选择liteos.ld
ENTRY(reset_vector)
INCLUDE board.ld
SECTIONS
{
_start = .;
.set_sysinit_set : {
__start_set_sysinit_set = ABSOLUTE(.);
KEEP (*(.set_sysinit_set))
__stop_set_sysinit_set = ABSOLUTE(.);
} > ram
.got ALIGN(0x4) : { *(.got.plt) *(.got) } > ram
.gcc_except_table ALIGN (0x8) : { . = .; } > ram .gcc_except_table : { KEEP(*(.gcc_except_table*)) }
.exception_ranges ALIGN (0x8) : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } > ram
.ARM.extab ALIGN(0x4) : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > ram
/* .ARM.exidx is sorted, so has to go in its own output section. */
.ARM.exidx ALIGN(0x8) : { __exidx_start = .; *(.ARM.exidx* .gnu.linkonce.armexidx.*) ;__exidx_end = .;} > ram
/* text/read-only data */
.text ALIGN(0x1000) : {
__text_start = .;
*(.text* .sram.text.glue_7* .gnu.linkonce.t.*)
} > ram
.rel.text : { *(.rel.text) *(.rel.text.*) *(.rel.gnu.linkonce.t*) } > ram
.rela.text : { *(.rela.text) *(.rela.text.*) *(.rela.gnu.linkonce.t*) } > ram
.rel.data : { *(.rel.data) *(.rel.data.*) *(.rel.gnu.linkonce.d*) } > ram
.rela.data : { *(.rela.data) *(.rela.data.*) *(.rela.gnu.linkonce.d*) } > ram
.rel.rodata : { *(.rel.rodata) *(.rel.rodata.*) *(.rel.gnu.linkonce.r*) } > ram
.rela.rodata : { *(.rela.rodata) *(.rela.rodata.*) *(.rela.gnu.linkonce.r*) } > ram
.rel.got : { *(.rel.got) } > ram
.rela.got : { *(.rela.got) } > ram
.rel.ctors : { *(.rel.ctors) } > ram
.rela.ctors : { *(.rela.ctors) } > ram
.rel.dtors : { *(.rel.dtors) } > ram
.rela.dtors : { *(.rela.dtors) } > ram
.rel.init : { *(.rel.init) } > ram
.rela.init : { *(.rela.init) } > ram
.rel.fini : { *(.rel.fini) } > ram
.rela.fini : { *(.rela.fini) } > ram
.rel.bss : { *(.rel.bss) } > ram
.rela.bss : { *(.rela.bss) } > ram
.rel.plt : { *(.rel.plt) } > ram
.rela.plt : { *(.rela.plt) } > ram
.rel.dyn : { *(.rel.dyn) } > ram
.dummy_post_text : {
__text_end = .;
} > ram
.rodata ALIGN(0x1000) : {
__rodata_start = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
__exc_table_start = .;
KEEP(*(__exc_table))
__exc_table_end = .;
} > ram
/*
* extra linker scripts tend to insert sections just after .rodata,
* so we want to make sure this symbol comes after anything inserted above,
* but not aligned to the next section necessarily.
*/
.dummy_post_rodata : {
_hdf_drivers_start = .;
KEEP(*(.hdf.driver))
_hdf_drivers_end = .;
__rodata_end = .;
} > ram
.data ALIGN(0x1000) : {
/* writable data */
__ram_data_start = .;
__vdso_data_start = LOADADDR(.data);
KEEP(*(.data.vdso.datapage))
. = ALIGN(0x1000);
KEEP(*(.data.vdso.text))
. = ALIGN(0x1000);
__vdso_text_end = .;
*(.data .data.* .gnu.linkonce.d.*)
. = ALIGN(0x4);
KEEP(*( SORT (.liteos.table.*)));
} > ram
.ctors : ALIGN(0x4) {
__ctor_list__ = .;
KEEP (*(.ctors .init_array))
__ctor_end__ = .;
} > ram
.dtors : ALIGN(0x4) {
__dtor_list__ = .;
KEEP (*(.dtors .fini_array))
__dtor_end__ = .;
} > ram
/*
* extra linker scripts tend to insert sections just after .data,
* so we want to make sure this symbol comes after anything inserted above,
* but not aligned to the next section necessarily.
*/
.dummy_post_data : {
__ram_data_end = .;
} > ram
.user_init USER_INIT_VM_START : ALIGN(0x1000) {
. = ALIGN(0x4);
__user_init_load_addr = LOADADDR(.user_init);
__user_init_entry = .;
KEEP(libuserinit.O (.user.entry))
KEEP(libuserinit.O (.user.text))
KEEP(libuserinit.O (.user.rodata))
. = ALIGN(0X4);
__user_init_data = .;
KEEP(libuserinit.O (.user.data))
. = ALIGN(0X4);
__user_init_bss = .;
KEEP(libuserinit.O (.user.bss))
. = ALIGN(0x1000);
__user_init_end = .;
} > user_ram AT > ram
__user_init_size = __user_init_end - __user_init_entry;
/* unintialized data (in same segment as writable data) */
.bss : {
. = ALIGN(0x800);
__int_stack_start = .;
*(.int_stack);
. = ALIGN(0x4);
KEEP(*(.bss.prebss.*))
. = ALIGN(0x8);
__bss_start = .;
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(0x8);
__bss_end = .;
} > ram
. = ALIGN(0x1000);
_end = .;
/* mmu temp page table(sys aviliable mem is start with __bss_end) */
. = ALIGN(0x4000);
__mmu_ttlb_begin = .;
/* Strip unnecessary stuff */
/DISCARD/ 0 : { *(.comment .note) } > ram
}
OK,了解了ELF段分布,再看代码。
向量表
.code 32
.section ".vectors","ax"
.global __exception_handlers
__exception_handlers:
/*
*Assumption: ROM code has these vectors at the hardware reset address.
*A simple jump removes any address-space dependencies [i.e. safer]
*/
b reset_vector
b _osExceptUndefInstrHdl
b _osExceptSwiHdl
b _osExceptPrefetchAbortHdl
b _osExceptDataAbortHdl
b _osExceptAddrAbortHdl
b OsIrqHandler
b _osExceptFiqHdl
启动代码,注释都挺详细的。
/* Startup code which will get the machine into supervisor mode */
.global reset_vector
.type reset_vector,function
reset_vector:
/* clear register TPIDRPRW */
mov r0, #0
mcr p15, 0, r0, c13, c0, 4
/* do some early cpu setup: i/d cache disable, mmu disabled */
mrc p15, 0, r0, c1, c0, 0
bic r0, #(1<<12)
bic r0, #(1<<2 | 1<<0)
mcr p15, 0, r0, c1, c0, 0
/* r11: delta of physical address and virtual address */
adr r11, pa_va_offset
ldr r0, [r11]
sub r11, r11, r0
mrc p15, 0, r12, c0, c0, 5 /* r12: get cpuid */
and r12, r12, #MPIDR_CPUID_MASK
cmp r12, #0
bne secondary_cpu_init
(启动,多核运行,如果不是主核/启动核【一般为CPU#0】就跳转到secondary_cpu_init执行)
......
reloc_img_to_bottom_done:
(MMU页表设置)
ldr r4, =g_firstPageTable /* r4: physical address of translation table and clear it */
add r4, r4, r11
bl page_table_clear
PAGE_TABLE_SET SYS_MEM_BASE, KERNEL_VMM_BASE, KERNEL_VMM_SIZE, MMU_DESCRIPTOR_KERNEL_L1_PTE_FLAGS
PAGE_TABLE_SET SYS_MEM_BASE, UNCACHED_VMM_BASE, UNCACHED_VMM_SIZE, MMU_INITIAL_MAP_STRONGLY_ORDERED
PAGE_TABLE_SET PERIPH_PMM_BASE, PERIPH_DEVICE_BASE, PERIPH_DEVICE_SIZE, MMU_INITIAL_MAP_DEVICE
PAGE_TABLE_SET PERIPH_PMM_BASE, PERIPH_CACHED_BASE, PERIPH_CACHED_SIZE, MMU_DESCRIPTOR_KERNEL_L1_PTE_FLAGS
PAGE_TABLE_SET PERIPH_PMM_BASE, PERIPH_UNCACHED_BASE, PERIPH_UNCACHED_SIZE, MMU_INITIAL_MAP_STRONGLY_ORDERED
orr r8, r4, #MMU_TTBRx_FLAGS /* r8 = r4 and set cacheable attributes on translation walk */
ldr r4, =g_mmuJumpPageTable /* r4: jump pagetable vaddr */
add r4, r4, r11
ldr r4, [r4]
add r4, r4, r11 /* r4: jump pagetable paddr */
bl page_table_clear
/* build 1M section mapping, in order to jump va during turing on mmu:pa == pa, va == pa */
mov r6, pc
mov r7, r6 /* r7: pa (MB aligned)*/
lsr r6, r6, #20 /* r6: va l1 index */
ldr r10, =MMU_DESCRIPTOR_KERNEL_L1_PTE_FLAGS
add r12, r10, r6, lsl #20 /* r12: pa |flags */
str r12, [r4, r7, lsr #(20 - 2)] /* jumpTable[paIndex] = pt entry */
rsb r7, r11, r6, lsl #20 /* r7: va */
str r12, [r4, r7, lsr #(20 - 2)] /* jumpTable[vaIndex] = pt entry */
bl _bootaddr_setup
bl mmu_setup /* set up the mmu */
/* clear out the interrupt and exception stack and set magic num to check the overflow */
ldr r0, =__undef_stack
ldr r1, =__exc_stack_top
bl stack_init
STACK_MAGIC_SET __undef_stack, #OS_EXC_UNDEF_STACK_SIZE, OS_STACK_MAGIC_WORD
STACK_MAGIC_SET __abt_stack, #OS_EXC_ABT_STACK_SIZE, OS_STACK_MAGIC_WORD
STACK_MAGIC_SET __irq_stack, #OS_EXC_IRQ_STACK_SIZE, OS_STACK_MAGIC_WORD
STACK_MAGIC_SET __fiq_stack, #OS_EXC_FIQ_STACK_SIZE, OS_STACK_MAGIC_WORD
STACK_MAGIC_SET __svc_stack, #OS_EXC_SVC_STACK_SIZE, OS_STACK_MAGIC_WORD
STACK_MAGIC_SET __exc_stack, #OS_EXC_STACK_SIZE, OS_STACK_MAGIC_WORD
(热启动)
warm_reset:
/* initialize interrupt/exception environments */
mov r0, #(CPSR_IRQ_DISABLE |CPSR_FIQ_DISABLE|CPSR_IRQ_MODE)
msr cpsr, r0
EXC_SP_SET __irq_stack_top, #OS_EXC_IRQ_STACK_SIZE
mov r0, #(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_UNDEF_MODE)
msr cpsr, r0
EXC_SP_SET __undef_stack_top, #OS_EXC_UNDEF_STACK_SIZE
mov r0, #(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_ABT_MODE)
msr cpsr, r0
EXC_SP_SET __abt_stack_top, #OS_EXC_ABT_STACK_SIZE
mov r0, #(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_FIQ_MODE)
msr cpsr, r0
EXC_SP_SET __fiq_stack_top, #OS_EXC_FIQ_STACK_SIZE
/* initialize CPSR (machine state register) */
mov r0, #(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_SVC_MODE)
msr cpsr, r0
/* Note: some functions in LIBGCC1 will cause a "restore from SPSR"!! */
msr spsr, r0
/* get cpuid and keep it in r12 */
mrc p15, 0, r12, c0, c0, 5
and r12, r12, #MPIDR_CPUID_MASK
/* set svc stack, every cpu has OS_EXC_SVC_STACK_SIZE stack */
ldr r0, =__svc_stack_top
mov r2, #OS_EXC_SVC_STACK_SIZE
mul r2, r2, r12
sub r0, r0, r2
mov sp, r0
/* enable fpu+neon */
#ifndef LOSCFG_TEE_ENABLE
MRC p15, 0, r0, c1, c1, 2
ORR r0, r0, #0xC00
BIC r0, r0, #0xC000
MCR p15, 0, r0, c1, c1, 2
LDR r0, =(0xF << 20)
MCR p15, 0, r0, c1, c0, 2
#endif
MOV r3, #0x40000000
VMSR FPEXC, r3
LDR r0, =__exception_handlers
MCR p15, 0, r0, c12, c0, 0
cmp r12, #0
bne cpu_start
clear_bss:
ldr r1, =__bss_start
ldr r2, =__bss_end
mov r0, #0
bss_loop:
cmp r1, r2
strlo r0, [r1], #4
blo bss_loop
#if defined(LOSCFG_CC_STACKPROTECTOR_ALL) || \
defined(LOSCFG_CC_STACKPROTECTOR_STRONG) || \
defined(LOSCFG_CC_STACKPROTECTOR)
bl __stack_chk_guard_setup
#endif
#ifdef LOSCFG_GDB_DEBUG
/* GDB_START - generate a compiled_breadk,This function will get GDB stubs started, with a proper environment */
bl GDB_START
.word 0xe7ffdeff
#endif
bl main
_start_hang:
b _start_hang
由汇编时代步入C时代,kernel/liteos_a/platform/main.c
LITE_OS_SEC_TEXT_INIT INT32 main(VOID)
{
UINT32 uwRet = LOS_OK;
OsSetMainTask();
OsCurrTaskSet(OsGetMainTask());
/* set smp system counter freq */
#if (LOSCFG_KERNEL_SMP == YES)
#ifndef LOSCFG_TEE_ENABLE
HalClockFreqWrite(OS_SYS_CLOCK);
#endif
#endif
/* system and chip info */
OsSystemInfo();
PRINT_RELEASE("\nmain core booting up...\n");
uwRet = OsMain();
if (uwRet != LOS_OK) {
return LOS_NOK;
}
#if (LOSCFG_KERNEL_SMP == YES)
PRINT_RELEASE("releasing %u secondary cores\n", LOSCFG_KERNEL_SMP_CORE_NUM - 1);
release_secondary_cores();
#endif
CPU_MAP_SET(0, OsHwIDGet());
OsStart();
while (1) {
__asm volatile("wfi");
}
}
如果一切正常,OsStart(); 开启调度,然后执行着代码,不会执行后面的while(1)
简单的startup.S
.equ MPIDR_CPUID_MASK, 0xffU
.fpu vfpv4
.arm
.code 32
.section ".vectors","ax"
.global __exception_handlers
__exception_handlers:
b reset_vector
b . //_osExceptUndefInstrHdl
b . //_osExceptSwiHdl
b . //_osExceptPrefetchAbortHdl
b . //_osExceptDataAbortHdl
b . //_osExceptAddrAbortHdl
b . //OsIrqHandler
b . //_osExceptFiqHdl
/* Startup code which will get the machine into supervisor mode */
.global reset_vector
.type reset_vector,function
reset_vector:
/* clear register TPIDRPRW */
mov r0, #0
mcr p15, 0, r0, c13, c0, 4
/* do some early cpu setup: i/d cache disable, mmu disabled */
mrc p15, 0, r0, c1, c0, 0
bic r0, #(1<<12)
bic r0, #(1<<2 | 1<<0)
mcr p15, 0, r0, c1, c0, 0
/* r11: delta of physical address and virtual address */
adr r11, pa_va_offset
ldr r0, [r11]
sub r11, r11, r0
mrc p15, 0, r12, c0, c0, 5 /* r12: get cpuid */
and r12, r12, #MPIDR_CPUID_MASK
cmp r12, #0
bne secondary_cpu_init
secondary_cpu_init:
bl warm_reset
warm_reset:
/* get cpuid and keep it in r12 */
mrc p15, 0, r12, c0, c0, 5
and r12, r12, #MPIDR_CPUID_MASK
ldr sp, =__svc_stack_top
/* enable fpu+neon */
MRC p15, 0, r0, c1, c1, 2
ORR r0, r0, #0xC00
BIC r0, r0, #0xC000
MCR p15, 0, r0, c1, c1, 2
LDR r0, =(0xF << 20)
MCR p15, 0, r0, c1, c0, 2
clear_bss:
ldr r1, =__bss_start
ldr r2, =__bss_end
mov r0, #0
bss_loop:
cmp r1, r2
strlo r0, [r1], #4
blo bss_loop
mov r0, r12
bl OsMain
_start_hang:
b _start_hang
pa_va_offset:
.word .
__svc_stack:
.space 1024
__svc_stack_top:
测试的rtos_main.c
#include
#define read32(addr) (*(volatile u32 *)(addr))
#define write32(addr, val) ((*(volatile u32 *)(addr)) = (val))
/* test */
void uart_putc(const s8 c)
{
u32 val = 0;
#define UART0_REG_BASE 0x120A0000
do {
val = read32(UART0_REG_BASE + 24);
} while(val & 0x20);
write32(UART0_REG_BASE + 0, c);
}
char hex2char(u8 hex)
{
if(hex < 10) {
return '0' + hex;
}
if(hex < 16) {
return 'a' + hex - 10;
}
return '?';
}
void OsMain(u32 cpuid)
{
uart_putc('C');uart_putc('P');uart_putc('U');
uart_putc('#');uart_putc('0');uart_putc('x');
uart_putc(hex2char(cpuid>>28));uart_putc(hex2char(cpuid>>24));
uart_putc(hex2char(cpuid>>20));uart_putc(hex2char(cpuid>>16));
uart_putc(hex2char(cpuid>>12));uart_putc(hex2char(cpuid>>8));
uart_putc(hex2char(cpuid>>4));uart_putc(hex2char(cpuid>>0));
}
由于u-boot已经设置了,所以
go 0x80000000是显示 CPU#0x00000000
go_cpu1 0x80000000是显示 CPU#0x00000001
看怎么启动其他核的kernel/liteos_a/platform/main.c
#if (LOSCFG_KERNEL_SMP == YES)
PRINT_RELEASE("releasing %u secondary cores\n", LOSCFG_KERNEL_SMP_CORE_NUM - 1);
release_secondary_cores();
#endif
#define CLEAR_RESET_REG_STATUS(regval) (regval) &= ~(1U << 2)
LITE_OS_SEC_TEXT_INIT VOID release_secondary_cores(VOID)
{
UINT32 regval;
/* clear the slave cpu reset */
READ_UINT32(regval, PERI_CRG30_BASE);
CLEAR_RESET_REG_STATUS(regval);
WRITE_UINT32(regval, PERI_CRG30_BASE);
/* wait until all APs are ready */
while (LOS_AtomicRead(&g_ncpu) < LOSCFG_KERNEL_CORE_NUM) {
asm volatile("wfe");
}
}
OK,添加
/* clear the slave cpu reset */
READ_UINT32(regval, PERI_CRG30_BASE);
CLEAR_RESET_REG_STATUS(regval);
WRITE_UINT32(regval, PERI_CRG30_BASE);
测试,不行,
看u-boot的go_cpu1
asm("str %0, [%1]"::"r" (cmd), "r" (cmd_address): "cc");
cmd = simple_strtoul(argv[1], NULL, 16);
printf("starting cpu1 liteos address 0x%x\n", cmd);
asm("str %0, [%1, #4]"::"r" (cmd), "r" (cmd_address): "cc");
/* clear the slave cpu reset */
regval = readl(0x12010000 + 0x0078);
regval &= ~(1 << 2);
writel(regval, (0x12010000 + 0x0078));
再修改startup.S
#define MPIDR_CPUID_MASK 0xffU
.fpu vfpv4
.arm
.code 32
.section ".vectors","ax"
.global __exception_handlers
__exception_handlers:
b reset_vector
b . //_osExceptUndefInstrHdl
b . //_osExceptSwiHdl
b . //_osExceptPrefetchAbortHdl
b . //_osExceptDataAbortHdl
b . //_osExceptAddrAbortHdl
b . //OsIrqHandler
b . //_osExceptFiqHdl
/* Startup code which will get the machine into supervisor mode */
.global reset_vector
.type reset_vector,function
reset_vector:
/* clear register TPIDRPRW */
mov r0, #0
mcr p15, 0, r0, c13, c0, 4
/* do some early cpu setup: i/d cache disable, mmu disabled */
mrc p15, 0, r0, c1, c0, 0
bic r0, #(1<<12)
bic r0, #(1<<2 | 1<<0)
mcr p15, 0, r0, c1, c0, 0
mrc p15, 0, r12, c0, c0, 5 /* r12: get cpuid */
and r12, r12, #MPIDR_CPUID_MASK
mov r0, r12
bl OsMain
b .
rtos_main.c
#include
#include
#define read32(addr) (*(volatile u32 *)(addr))
#define write32(addr, val) ((*(volatile u32 *)(addr)) = (val))
/* test */
void uart_putc(const s8 c)
{
u32 val = 0;
do {
val = read32(UART0_REG_BASE + 24);
} while(val & 0x20);
write32(UART0_REG_BASE + 0, c);
}
char hex2char(u8 hex)
{
if(hex < 10) {
return '0' + hex;
}
if(hex < 16) {
return 'a' + hex - 10;
}
return '?';
}
void OsMain(u32 cpuid)
{
u32 val = 0;
volatile int cmd_address = 0;
volatile int cmd = 0xe51ff004;
uart_putc('C');uart_putc('P');uart_putc('U');
uart_putc('#');uart_putc('0');uart_putc('x');
uart_putc(hex2char(cpuid>>28));uart_putc(hex2char(cpuid>>24));
uart_putc(hex2char(cpuid>>20));uart_putc(hex2char(cpuid>>16));
uart_putc(hex2char(cpuid>>12));uart_putc(hex2char(cpuid>>8));
uart_putc(hex2char(cpuid>>4));uart_putc(hex2char(cpuid>>0));
uart_putc('\r');uart_putc('\n');
if(0 == cpuid) {
uart_putc('w');uart_putc('a');uart_putc('k');uart_putc('e');
uart_putc('u');uart_putc('p');uart_putc(' ');
uart_putc(hex2char(CORE_NUM - 1));uart_putc(' ');
uart_putc('s');uart_putc('l');uart_putc('a');uart_putc('v');uart_putc('e');uart_putc(' ');
uart_putc('c');uart_putc('p');uart_putc('u');uart_putc('s');
uart_putc('\r');uart_putc('\n');
asm("str %0, [%1]"::"r" (cmd), "r" (cmd_address): "cc");
cmd = KERNEL_VADDR_BASE;
asm("str %0, [%1, #4]"::"r" (cmd), "r" (cmd_address): "cc");
#define CLEAR_RESET_REG_STATUS(regval) ((regval) &= ~(1U << 2))
/* clear the slave cpu reset */
val = read32(PERI_CRG30_BASE);
CLEAR_RESET_REG_STATUS(val);
write32(PERI_CRG30_BASE, val);
}
}
测试,OK
hisilicon # go 0x80000000
## Starting application at 0x80000000 ...
CPU#0x00000000
wakeup 1 slave cpus
CPU#0x00000001