DA145x软件平台利用了由Riviera Waves许可的小型高效实时内核,内核提供以下功能:
本节主要分析Riviera Waves系统中常用的一些数据结构
co_list就是一个单向链表,DA145x代码里面会使用co_list来存放各种数据,比如消息数据.
核心的数据结构如下
//链表中的节点
struct co_list_hdr
{
/// 指向下一个链表节点
struct co_list_hdr *next;
};
/// 链表头的结构
struct co_list
{
// 链表头节点
struct co_list_hdr *first;
// 链表尾节点
struct co_list_hdr *last;
// 链表中的节点个数
uint32_t cnt;
// 链表中最多节点数
uint32_t maxcnt;
// 链表中最少节点数
uint32_t mincnt;
};
co_list表示链表头,存放了整个链表的一些元数据,链表节点为co_list_hdr,应用程序使用co_list时会在其特定结构体内部嵌入co_list_hdr和co_list。
co_list_init用于初始化一个链表
void __fastcall co_list_init(struct co_list *list)
{
list->first = 0;
list->last = 0;
list->cnt = 0;
list->maxcnt = 0;
list->mincnt = -1;
}
co_list_push_back
用于将节点list_hdr
插入到链表list
的尾部
void __fastcall co_list_push_back(struct co_list *list, struct co_list_hdr *list_hdr)
{
uint32_t cnt; // r1
if ( list->first )
{
list->last->next = list_hdr;
}
else
{
list->first = list_hdr;
}
list->last = list_hdr;
list_hdr->next = 0;
cnt = list->cnt + 1;
list->cnt = cnt;
if ( list->maxcnt < cnt )
{
list->maxcnt = cnt;
}
}
list->first
为空,就把list_hdr
放到链表头list->first
,否则就把该list_hdr放到最后一个节点的末尾list->last
和list->cnt
co_list_push_front
用于将节点list_hdr
插入到链表list
的头部
void __fastcall co_list_push_front(struct co_list *list, struct co_list_hdr *list_hdr)
{
co_list_hdr *v2; // r2
uint32_t v3; // r1
v2 = list->first;
if ( !list->first )
{
list->last = list_hdr;
}
list_hdr->next = v2;
list->first = list_hdr;
v3 = list->cnt + 1;
list->cnt = v3;
if ( list->maxcnt < v3 )
{
list->maxcnt = v3;
}
}
逻辑类似,就是把list_hdr
放到链表list->first
,然后修正list
中相关的字段
节点出链表
co_list_pop_front将头节点出链表
struct co_list_hdr *__fastcall co_list_pop_front(struct co_list *list)
{
struct co_list_hdr *item; // r1
co_list_hdr *v2; // r2
uint32_t v3; // r2
item = list->first;
if ( list->first )
{
v2 = item->next;
list->first = item->next;
if ( !v2 )
{
list->last = 0;
}
v3 = list->cnt - 1;
list->cnt = v3;
if ( list->mincnt > v3 )
{
list->mincnt = v3;
}
}
return item;
}
就是把list->first
的元素取出,然后修改list的相关信息。
co_list_extract
函数用于在list
中取出从list_hdr
开始的nb_following
个节点。
bool __fastcall co_list_extract(struct co_list *list, struct co_list_hdr *list_hdr, int nb_following)
{
bool has_found; // r5
co_list_hdr *pre; // r4
co_list_hdr *cur; // r3
co_list_hdr *new_next; // r1
uint32_t v7; // r1
has_found = 0;
pre = 0;
for ( cur = list->first; cur; cur = cur->next )
{
if ( cur == list_hdr ) // 首先找到 list_hdr 节点
{
has_found = 1;
while ( nb_following > 0 ) // 从list_hdr开始取出nb_following个节点
{
cur = cur->next;
--nb_following; // 如果 nb_following 超过链表长度,就会空指针...
--list->cnt;
}
new_next = cur->next;
if ( pre ) // list_hdr开始的nb_following个节点出链表
{
pre->next = new_next;
}
else
{
list->first = new_next;
}
if ( list->last == cur )
{
list->last = pre;
}
v7 = list->cnt - 1;
list->cnt = v7;
if ( list->mincnt > v7 )
{
list->mincnt = v7;
}
return has_found;
}
pre = cur;
}
return has_found;
}
主要逻辑就是找到list_hdr节点cur,然后从cur开始取出nb_following个节点。
co_list_find就是遍历链表找到list_hdr节点
bool __fastcall co_list_find(struct co_list *list, struct co_list_hdr *list_hdr)
{
do
{
list = list->first;
}
while ( list != list_hdr && list );
return list == list_hdr;
}
co_list_merge把两个链表合并为一个链表,实际就是把list2
的元素挂在list1
的末尾
void __fastcall co_list_merge(struct co_list *list1, struct co_list *list2)
{
list1->last->next = list2->first;
list1->last = list2->last;
list2->first = 0;
list1->cnt += list2->cnt;
list2->cnt = 0;
}
Riviera Waves中实现了事件调度机制,一个任务可以在处理完事情后,通知特定的事件处理函数去进行具体的事物处理。
本节主要分析事件调度相关函数的实现
该函数主要就是初始化了一个全局变量
void ke_event_init()
{
memset(p_ke_event_table, 0, sizeof(ke_event_table_struct));
}
p_ke_event_table指向一个全局的事件调度管理结构,经过逆向分析其结构体定义如下
struct ke_event_table_struct
{
int pending_event_bits;
int callback_list[6];
};
其中pending_event_bits其中的一些bit用于表示特定的事件是否已经处于pending状态等待系统处理。
callback_list表示每个事件的处理函数的地址
该函数实际就是向系统注册 event_type 事件对应的处理函数, event_type最大为5,及系统共支持6个事件。
uint8_t __fastcall ke_event_callback_set(uint8_t event_type, void (*p_callback)(void))
{
unsigned int idx; // r2
uint8_t result; // r0
idx = event_type;
result = 3;
if ( idx < 6 )
{
p_ke_event_table->callback_list[idx] = p_callback;
result = 0;
}
return result;
}
ke_event_schedule
会检查p_ke_event_table->pending_event_bits
中所有事件的状态,如果事件对应的bit
为1,就调用对应的回调函数,关键代码如下
unsigned int ke_event_schedule()
{
v0 = p_ke_event_table;
while ( 1 ) // 检查所有事件的状态
{
result = v0->pending_event_bits;
// 根据pending_event_bits找到对应的回调函数
event_callback = *(v0->callback_list + ((4 * (31 - v3)) & 0x3FF));
if ( event_callback )
{
event_callback(); // 调用事件的回调函数
}
ke_event_schedule
会在系统运行的特定时机被调用,比如定时器或者某些任务主动调用来让系统处理事件。
此外该函数不会设置事件对应的bit
,所以在事件的处理函数,如果事件得到处理要调用ke_event_clear设置对应事件的bit为0.
设置某个事件的状态位为0.
bool __fastcall ke_event_clear(unsigned int event)
{
unsigned int v1; // r1
_BOOL4 result; // r0
v1 = __get_CPSR();
_R2 = 1;
__asm { MSR.W PRIMASK, R2 }
if ( event < 6 )
{
p_ke_event_table->pending_event_bits &= ~(1 << event);
}
result = v1 != 0;
__asm { MSR.W PRIMASK, R0 }
return result;
}
设置某个事件的状态位为1,即通知系统该事件需要处理,ke_event_schedule函数中会调用事件对应的回调函数来处理事件。
bool __fastcall ke_event_set(unsigned int a1)
{
unsigned int v1; // r1
_BOOL4 result; // r0
v1 = __get_CPSR();
_R2 = 1;
__asm { MSR.W PRIMASK, R2 }
if ( a1 < 6 )
{
p_ke_event_table->pending_event_bits |= 1 << a1;
}
result = v1 != 0;
__asm { MSR.W PRIMASK, R0 }
return result;
}
通过查看ke_event_callback_set的交叉引用和参数可以知道系统中注册的事件号及其回调函数的信息如下
https://github.com/hac425xxx/BLE-DA145XX/blob/main/argument_tracker.py#L556
addr: 0x7F08BB2, event: 0x5, callback: lld_evt_deffered_elt_handler @ 0x7F08A6E
addr: 0x7F09CCE, event: 0x0, callback: llm_encryption_done @ 0x7F02744
addr: 0x7F0E5C2, event: 0x3, callback: event_3_callback_func @ 0x7F0E58E
addr: 0x7F0E956, event: 0x4, callback: event_4_callback_func @ 0x7F0E87C
addr: 0x7F1CDEC, event: 0x1, callback: event_1_callback_func @ 0x7F1CCDE
addr: 0x7F1D06C, event: 0x2, callback: event_2_callback_func @ 0x7F1CFFA
Riviera Waves中实现了任务管理机制,用户可以创建自己的任务来处理特定的事件
任务ID由两个部分组成,高8字节为任务的IDX,低8字节为任务的类型,定义如下
/// Task Identifier. Composed by the task type and the task index.
typedef uint16_t ke_task_id_t;
/// Builds the task identifier from the type and the index of that task.
#define KE_BUILD_ID(type, index) ( (ke_task_id_t)(((index) << 8)|(type)) )
/// Retrieves task type from task id.
#define KE_TYPE_GET(ke_task_id) ((ke_task_id) & 0xFF)
/// Retrieves task index number from task id.
#define KE_IDX_GET(ke_task_id) (((ke_task_id) >> 8) & 0xFF)
ke_task_create用于创建一个任务,实际就是把任务描述符放到全局任务数组的特定位置
uint8_t __fastcall ke_task_create(uint8_t task_type, const struct ke_task_desc *p_task_desc)
{
idx = task_type;
if ( idx < 26 )
{
if ( p_task_desc_table_0[idx] )
{
result = 4;
}
else
{
p_task_desc_table_0[idx] = p_task_desc;
}
任务描述符的结构如下
/// Task descriptor grouping all information required by the kernel for the scheduling.
struct ke_task_desc
{
/// Pointer to the state handler table (one element for each state).
const struct ke_state_handler* state_handler;
/// Pointer to the default state handler (element parsed after the current state).
const struct ke_state_handler* default_handler;
/// Pointer to the state table (one element for each instance).
ke_state_t* state;
/// Maximum number of states in the task.
uint16_t state_max;
/// Maximum index of supported instances of the task.
uint16_t idx_max;
};
state是一个数组,用于表示当前task处于哪些状态,state_max为state数组的大小
开发者可以使用ke_state_set
设置task->state
的值
void __fastcall ke_state_set(const ke_task_id_t id, const ke_state_t state_id)
{
int state_idx; // r4
ke_task_desc *task; // r2
ke_state_t *v4; // r2
state_idx = HIBYTE(id);
task = 0;
if ( id < 0x1Au )
{
task = p_task_desc_table_0[id];
}
if ( task->idx_max > state_idx )
{
v4 = &task->state[state_idx];
if ( *v4 != state_id )
{
*v4 = state_id;
notify_handle_saved_msg(id); // 通知内核去处理queue_saved中的消息
}
}
}
这个表主要在get_msg_handler
函数中被使用,用于任务的状态机。
系统中的任务列表
call ke_task_create on llc_init 0x7F02CBE, task_struct: 0x7F1F1E8
call ke_task_create on lld_init 0x7F06E1E, task_struct: 0x7F1F540
call ke_task_create on llm_init 0x7F09CC6, task_struct: 0x7F1F578
call ke_task_create on gtl_init_func 0x7F0E322, task_struct: 0x7F1F7F0
call ke_task_create on gattc_init 0x7F125BE, task_struct: 0x7F1FE44
call ke_task_create on gattm_init 0x7F13824, task_struct: 0x7F1FF40
call ke_task_create on l2cc_init 0x7F13B7A, task_struct: 0x7F1FFE0
call ke_task_create on gapc_init 0x7F1567C, task_struct: 0x7F2004C
call ke_task_create on gapm_init 0x7F176D4, task_struct: 0x7F201B4
函数通过ke_msg_alloc
申请消息,入参分别为消息ID,目的task_id, 源task_id以及消息参数的长度。
void *__fastcall ke_msg_alloc(const ke_msg_id_t id, const ke_task_id_t dest_id, const ke_task_id_t src_id, const uint16_t param_len)
{
size_t v6; // r4
ke_msg *msg; // r0
uint32_t *v9; // r5
v6 = param_len;
msg = ke_malloc(param_len + 16, 2); // 申请内存
msg->hdr.next = -1;
msg->saved = 0;
msg->id = id;
msg->dest_id = dest_id;
msg->src_id = src_id;
msg->param_len = v6;
v9 = msg->param;
memset(msg->param, 0, v6);
return v9;
}
返回值是一个ke_msg
结构体的param
部分
struct ke_msg
{
struct co_list_hdr hdr; // 链表头,用于后面把消息挂载到co_list链表中
uint32_t saved;
ke_msg_id_t id;
ke_task_id_t dest_id;
ke_task_id_t src_id;
uint16_t param_len; // param 的长度
uint32_t param[1];
};
ke_msg_free直接使用 ke_free 释放内存。
int __fastcall ke_msg_free(int a1)
{
return ke_free(a1);
}
ke_msg_free的入参是 ke_msg*
,但是ke_msg_alloc
返回是ke_msg
的param
,所以在使用ke_msg_free
很有可能出现指针没有减0x10(ke_msg头部的大小)
的情况。
ke_msg_send用于将特定消息发送到目标任务去处理
bool __fastcall ke_msg_send(int param)
{
ke_msg *msg_hdr; // r1
unsigned int v2; // r4
msg_hdr = (param - 16);
v2 = __get_CPSR();
_R0 = 1;
__asm { MSR.W PRIMASK, R0 } // 关闭中断
co_list_push_back(&p_ke_env->queue_sent, &msg_hdr->hdr);
_R0 = v2 != 0;
__asm { MSR.W PRIMASK, R0 } // 恢复中断
return ke_event_set(1u);
}
主要逻辑就是把msg_hdr
放到p_ke_env->queue_sent
链表的末尾,p_ke_env
指向ke_env
,ke_env
是一个全局变量,其结构如下
/// Kernel environment definition
struct ke_env_tag
{
/// Queue of sent messages but not yet delivered to receiver
struct co_list queue_sent;
/// Queue of messages delivered but not consumed by receiver
struct co_list queue_saved;
/// Queue of timers
struct co_list queue_timer;
#if (KE_MEM_RW)
/// Root pointer = pointer to first element of heap linked lists
struct mblock_free * heap[KE_MEM_BLOCK_MAX];
/// Size of heaps
uint16_t heap_size[KE_MEM_BLOCK_MAX];
#if (KE_PROFILING)
/// Size of heap used
uint16_t heap_used[KE_MEM_BLOCK_MAX];
/// Maximum heap memory used
uint32_t max_heap_used;
#endif //KE_PROFILING
#endif //KE_MEM_RW
};
可以看的结构体头部是queue_sent
,类型为co_list
,这个队列用于存放发送的的消息,queue_sent
中消息会在后面消息调度时,找到对应的消息处理函数进行处理。
故ke_msg_send
就是把要发送的消息放到ke_env
的queue_sent
发送队列中。
消息挂载到queue_sent
链表后会调用ke_event_set
通知内核,1号事件触发,然后在事件处理函数中会去调用消息对应的处理函数去处理消息。
在ke_task_init_func
函数里面注册了1号事件的处理函数
int ke_task_init_func()
{
memset(p_task_desc_table_0, 0, 0x68u);
return ke_event_callback_set(1u, 0x07F1CCDF);
}
0x07F1CCDF
处的函数的关键代码为
int event_1_callback_func()
{
// 从发送队列中取出一个消息
msg = co_list_pop_front(&p_ke_env_->queue_sent);
if ( msg && !ke_is_free(msg) )
{
custom_msg_handler = *custom_msg_handlers_1;
if ( *custom_msg_handlers_1 )
{
// 首先在 custom_msg_handlers 里面搜索消息处理函数
for ( i = 0; ; ++i )
{
handler = &custom_msg_handler[i];
if ( !handler->func )
{
break;
}
if ( msg->dest_id == custom_msg_handler[i].task_id )
{
msg_id = msg->id;
if ( msg_id == handler->id || msg_id == dv_0xFFFF )
{
msg_handle_func = custom_msg_handler[i].func;
if ( !msg_handle_func )
{
break; // 如果匹配就调用回调函数处理
}
goto trigger_callback_func;
}
}
}
}
msg_handle_func = get_msg_handler(msg->id, msg->dest_id);
if ( msg_handle_func )
{
trigger_callback_func:
msg_handle_result = msg_handle_func(msg->id, msg->param, msg->dest_id, msg->src_id);
if ( msg_handle_result )
{
if ( msg_handle_result != 1 && msg_handle_result == 2 )
{
// 处理结果为2,msg保存到queue_saved链表
msg->saved = 1;
co_list_push_back(&p_ke_env_->queue_saved, &msg->hdr);
}
goto out;
}
}
ke_msg_free(msg); // 如果消息处理成功就把msg释放
}
out:
if ( !p_ke_env_->queue_sent.first ) // 如果queue_sent链表为空,清除 event #1 事件
{
ke_event_clear(1u);
}
return result;
}
代码逻辑为
p_ke_env_->queue_sent
取出一个消息msg
。msg->id
在 custom_msg_handlers
里面搜索消息处理函数,如果能找到就调用消息处理函数。get_msg_handler
根据msg->id
和msg->dest_id
去目标任务描述符里面搜索处理函数msg_handle_func
后,调用msg_handle_func
对消息进行处理msg_handle_func
返回值为0表示消息处理完毕,后面会使用ke_msg_free
释放消息的内存,如果返回值为2,就会把消息放到p_ke_env_->queue_saved
链表中本文主要对Riviera Waves系统中的一些关键API、工作机制进行介绍。