In order to run programs that run on T-Kernel on top of an environment that includes T-Kernel/Standard Extension (hereafter T-Kernel/SE), there are several things to be careful about. Here, with software created on top of T-Kernel as the target, we will explain techniques to serve as a reference for the purpose of migrating to the T-Kernel/SE environment.
The system calls that have been prepared in T-Kernel basically cannot be directly called up from an application running in user mode. Because system calls with the same functions as the T-Kernel system calls have been prepared in T-Kernel/SE, it is necessary to carry out modifications of the places of those calls in regard to T-Kernel system calls that are used inside application source code. Furthermore, in regard to drivers and middleware that run in privileged mode, because utilizing T-Kernel system calls is possible, it is not necessary to carry out modifications of these system calls.
In tables 1~4, we give lists of the system calls that can be modified.
|
||
Function | T-Kernel | T-Kernel/SE |
Create task | tk_cre_tsk | tkse_cre_tsk, tkse_crs_tsk |
Start Task | tk_sta_tsk | tkse_sta_tsk |
Self task terminate | tk_ext_tsk | tkse_ext_tsk |
Other task forced termination | tk_ter_tsk | tkse_ter_tsk |
Move to wait self task wake up state | tk_slp_tsk | tkse_slp_tsk |
Other task wake up | tk_wup_tsk | tkse_wup_tsk |
Invalidate task wake up request | tk_can_wup | tkse_can_wup |
Delay task | tk_dly_tsk | tkse_dly_tsk |
Because there are also ones where the argument settings are modified too, the manual should be checked in actual replacement work. |
|
||
Function | T-Kernel | T-Kernel/SE |
Create semaphore | tk_cre_sem | tkse_cre_sem |
Delete semaphore | tk_del_sem | tkse_del_sem |
Return semaphore resources | tk_sig_sem | tkse_sig_sem |
Acquire semaphore resources | tk_wai_sem | tkse_wai_sem |
Reference semaphore state | tk_ref_sem | tkse_ref_sem |
Create event flag | tk_cre_flg | tkse_cre_flg |
Delete event flag | tk_del_flg | tkse_del_flg |
Set event flag | tk_set_flg | tkse_set_flg |
Clear event flag | tk_clr_flg | tkse_clr_flg |
Wait event flag | tk_wai_flg | tkse_wai_flg |
Reference event flag state | tk_ref_flg | tkse_ref_flg |
Create mailbox | tk_cre_mbx | tkse_cre_mbx |
Delete mailbox | tk_del_mbx | tkse_del_mbx |
Send to mailbox | tk_snd_mbx | tkse_snd_mbx |
Receive from mailbox | tk_rcv_mbx | tkse_rcv_mbx |
Reference mailbox state | tk_ref_mbx | tkse_ref_mbx |
Create mutex | tk_cre_mtx | tkse_cre_mtx |
Delete mutex | tk_del_mtx | tkse_del_mtx |
Lock mutex | tk_loc_mtx | tkse_loc_mtx |
Unlock mutex | tk_unl_mtx | tkse_unl_mtx |
Reference mutex state | tk_ref_mtx | tkse_ref_mtx |
Create message buffer | tk_cre_mbf | tkse_cre_mbf |
Delete message buffer | tk_del_mbf | tkse_del_mbf |
Send to message buffer | tk_snd_mbf | tkse_snd_mbf |
Receive from message buffer | tk_rcv_mbf | tkse_rcv_mbf |
Reference message buffer state | tk_ref_mbf | tkse_ref_mbf |
Create rendezvous port | tk_cre_por | tkse_cre_por |
Delete rendezvous port | tk_del_por | tkse_del_por |
Call rendezvous | tk_cal_por | tkse_cal_por |
Accept rendezvous | tk_acp_por | tkse_acp_por |
Forward rendezvous | tk_fwd_por | tkse_fwd_por |
Reply to rendezvous | tk_rpl_rdv | tkse_rpl_rdv |
Reference rendezvous port state | tk_ref_por | tkse_ref_por |
Because there are also ones where the argument settings are modified too, the manual should be checked in actual replacement work. |
|
||
Function | T-Kernel | T-Kernel/SE |
Open device | tk_opn_dev | tkse_opn_dev |
Close device | tk_cls_dev | tkse_cls_dev |
Read in data | tk_rea_dev | tkse_rea_dev |
Read in data synchronously | tk_srea_dev | tkse_srea_dev |
Write in data | tk_wri_dev | tkse_wri_dev |
Write in data synchronously | tk_swri_dev | tkse_swri_dev |
Wait for termination of processing | tk_wai_dev | tkse_wai_dev |
Request suspend | tk_sus_dev | tkse_sus_dev |
Obtain device name | tk_get_dev | tkse_get_dev |
Obtain device information | tk_ref_dev | tkse_ref_dev |
tk_oref_dev | tkse_oref_dev | |
Obtain registered device information | tk_lst_dev | tkse_lst_dev |
Because there are also ones where the argument settings are modified too, the manual should be checked in actual replacement work. |
|
||
Function | T-Kernel | T-Kernel/SE |
Set system time | tk_set_tim | tkse_set_tim |
Reference system time | tk_get_tim | tkse_get_tim |
Because there are also ones where the argument settings are modified too, the manual should be checked in actual replacement work. |
Moreover, there are also several functions for which an equivalent system call has not been prepared on T-Kernel/SE side. In regard to those types of things, it is necessary to carry out a review of the software processes themselves.
|
|
We can mention being compatible with memory management where an MMU is used as one of the characteristics of T-Kernel/SE (figs.1 and 2).
|
|
A basic function that is necessary in carrying out memory management in T-Kernel Extension is provided in standalone T-Kernel, but on top of actual targets, in almost all cases it is made to run in a state where it does not utilize an MMU. When it comes about that this utilizes T-Kernel/SE, a memory management function that utilizes an MMU becomes indispensable.
It is desirable for original device drivers and middleware to be created so that they run without relation to an MMU being valid or not, but when that kind of compatibility is not assured, there will be cases in which device drivers and middleware that have run in a standalone T-Kernel environment in which an MMU has not been valid up to that point will become unable to access memory they have been able access up to that point and will come to not run as a result of functions such as memory protection and so on based on an MMU having become valid and address space differences occurring at the point in time of migration to the T-Kernel/SE environment.
Here, we will present things you ought to be careful of in the future when an MMU becomes valid.
Acquisition/Release of Memory
|
||
|
|
|
|
Memory allocation | Kmalloc |
Kcalloc | ||
Memory reallocation | Krealloc | |
Memory release | Kfree | |
|
Memory allocation | Vmalloc |
Vcalloc | ||
Memory reallocation | Vrealloc | |
Memory release | Vfree |
T-Kernel
In standalone T-Kernel, because we carry out memory management of task space, in a state in which an MMU has not become valid in the standalone T-Kernel environment, it is almost always the case that memory space consists only of shared memory, and that the task space region turns into a form that does not exist. The acquisition/release of the memory of that shared space is carried out using the system calls in Table 5.
|
||
|
|
|
|
Memory allocation | malloc |
calloc | ||
Memory reallocation | realloc | |
Memory release | free | |
|
Memory allocation | Smalloc |
Scalloc | ||
Memory reallocation | Srealloc | |
Memory release | Sfree |
T-Kernel/SE
In the same manner as the previously mentioned system calls, library calls such as Kmalloc that use T-Kernel system calls internally cannot be directly used from applications running in user mode. Because we have prepared the library calls of Table 6 in their place in T-Kernel/SE, it is necessary to carry out modification of the places of those calls.
In the T-Kernel/SE environment, memory space is comprised of local memory made up from address space that is different for each process, and shared memory. Local memory is namely task space, and it is a region for which access is possible only from processes in which tasks that belong to them are included; shared memory is a region for which access is possible from all processes.
In other words, we place data of the type that straddles and references processes in shared memory; we place data that we shut inside one process in local memory, and we make it so that they are protected in regard to other processes.
|
|
||||||||||||||||||||||||
|
The Switching of Local Memory (Task Space)
The local memory (task space) when running the processing parts called up via the SVC handler from applications such as device drivers middleware and the like becomes as in Table 7 (Fig. 3). In other words, because the call source application and the logical address space are different depending on the device driver or middleware processing part, there are cases where local memory cannot be referenced. Therefore, in order to handle the data in the memory specified by the call source application, it is necessary, in accordance with need, to use the system calls in Table 8 and carry out the following processing (lists 1 and 2).
|
|
|
|
Referencing of task space | tk_get_tsp |
Setting of task space | tk_get_tsp |
Referencing of resource group belonging to task | tk_get_rid |
Setting of resource group belonging to task | tk_set_rid |
Setting of task space and access privileges | SetTaskSpace |
|
|
||||||||||||||
|
Memory Protection Level/Access Privilege
The memory protection level and access privilege when running the processing parts that function after being called up via the SVC handler from applications such as device drivers, middleware, and the like become as in Table 8 (Fig. 4). In T-Kernel, it is recommended that you carry out a check of access privilege using the system calls in Table 9 in regard to memory that is passed via the SVC handler (Fig. 4, List 3). However, in a case where you are utilizing GDI/SDI in a driver, check processing is carried out in the interface part.
What you have to be careful about at this time are cases where you further call up a middleware function inside of middleware. As for the memory attributes of the SVC handler, as they are:
the memory protection level acquired in the middleware SVC handler called up from an application running in user mode becomes 0. When we end up calling a function that includes an access privilege check process such as ChkSpaceR by making that memory the argument, the access privilege itself is the same as the call source task, that is to say, because it is user mode, it ends up becoming an error (E_MACV) (lists 4~6).
Acquisition of a Resource Management Block
In T-Kernel/SE, a resource management block is automatically created for each process and used. For that reason, in a case where a device driver or middleware is referencing data inside a resource management block, we must make it so that it without fail references data of the resource management block that belongs to call source application (List 7).
|
|
|
|
|
CnvPhysicalAddr |
|
MapMemory |
|
UnmapMemory |
Making a memory region resident | LockSpace |
Making a memory region non-resident | UnlockSpace |
Device Access
When we carry out read-out/write-in of data in device drivers, because, depending on the device, there are also troublesome things when we carry out processing as is in logical space, it is necessary to carry out control that is also conscious of memory access from a device by using the system calls in Table 10. If we use DMA transfer as an example, it is necessary to be careful about the following things (List 8).
In the T-Kernel/SE environment, it is almost always the case that applications are created on a process base. In a case where an application has been created with processes, it is necessary to modify the places where we start up application tasks in the standalone T-Kernel environment so that they start up processes (lists 9 and 10).
Header Files
There are cases where a header file of the same name is included in tk/include and tkse/include, respectively. Basically, we make it so that we create the priority level of the header file search in the order tkse/include, tk/include.
Libraries
In T-Kernel/SE, there are the following types of things as libraries that are being newly provided.
____________________
List 1 User Parameter Access in a Device Driver (Not a Good Example) |
A Case Where There Is No Task Space (Undesirable Method of Writing): void sample_driver_accept_task(INT stacd, VP exinf) { /* * Device processing request acceptance task initialization * (Details omitted) */ /* Device processing request process loop */ while (TRUE) { /* Synchronous reception of request packet */ er = GDI_Accept(&req, DRP_NORMREQ, TMO_FEVR, gdi); if (er < E_OK) { /* Error processing */ continue; } /* * Processing for device processing request req: * Because always being able to reference req->buf that is * passed from the task of the call source is a premise, * this is not good. */ /* Processing result returned to the call source */ GDI_reply(req, gdi); } tk_exd_tsk(); } |
List 2 User Parameter Access in a Device Driver (Good Example) |
A Case Where Task Space Exists (Desirable Method of Writing): void sample_driver_accept_task(INT stacd, VP exinf) { ER er; T_DEVREQ *req; GDI gdi; /* * Device processing request acceptance task initialization * (Details omitted) */ /* Device processing request process loop */ while (TRUE) { /* Synchronous reception of request packet */ er = GDI_Accept(&req, DRP_NORMREQ, TMO_FEVR, gdi); if (er < E_OK) { /* Error processing */ continue; } /* Setting the task space of the system call call source */ er = tk_set_tsp(TSK_SELF, req->tskspc); if (er < E_OK) { /* Error processing */ continue; } /* * Processing for device processing request req: * When you don't set the task space, a memory access violation * occurs at the time req->buf referencing. */ /* Returning processing results to the call source */ GDI_reply(req, gdi); } tk_exd_tsk(); } |
List 3 Memory Access Privilege, Protection Level Relation (1) |
(1) Example of an Extended SVC the User Calls IMPORT INT sample_svc_func1(UB *buf_caller, INT len); IMPORT INT sample_svc_func2(UB *buf_caller, INT len); (2) SVC Handler a Subsystem Prepares typedef struct { UB *buf; INT len; } SAMPLE_SVC_PARA; INT sample_svc_handler(VP para, FN fncd) { INT ret; /* Branch based on fncd */ switch (fncd >> 8) { case SAMPLE_SVC_FUNC1: /* Calling up the substance of func1 */ ret = _sample_svc_func1(((SAMPLE_SVC_PARA *) para) ->buf, ((SAMPLE_SVC_PARA *) para) ->len); break; case SAMPLE_SVC_FUNC2: /* Calling up the substance of func2 */ ret = _sample_svc_func2(((SAMPLE_SVC_PARA *) para) ->buf, ((SAMPLE_SVC_PARA *) para) ->len; break; /* * In a case where there are other entries, line up here * . . . */ default: /* Undefined function code error */ ret = E_RSFN; break; } return ret; } (3) Substance of an Extended SVC a Subsystem Prepares INT _sample_svc_func1(UB *buf_caller, INT len) { INT ret; /* * Because buf is a region passed from a user application, it is * necessary to check whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Carrying out the processing of func1 */ return ret; } INT _sample_svc_func2(UB *buf_caller, INT len) { INT ret; /* * Because buf is a region passed from a user application, it is * necessary to check whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Carrying out the processing of func2 */ return ret; |
List 4 Memory Access Privilege, Protection Level Relation (2) |
A Case Where func1 Is Dependent on func 2 (Bad Example) : INT _sample_svc_func1(UB *buf_caller, INT len) { INT ret, result; UB *tmpbuf; /* * Because the buf_caller is a region that is passed from a user * application (protection level 3), it is necessary to check * whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Processing of func1 * . . . */ tmpbuf = Kmalloc(len); /* Error check omitted for example use*/ /* * Calling up func2 in the middle of this (example of failure) * * Because tmpbuf is a region created inside the SVC handler, the * protection level is 0. * On the other hand, because the access privilege of the call * source is protection level 3, the following call ends up * becoming E_MACV. */ result = sample_svc_func2(tmp_buf, len); /* . . . * Processing of func1 (continues) */ (void) Kfree(tmpbuf); return ret; } |
List 5 Memory Access Privilege, Protection Level Relation (3) |
A Case Where func1 Is Dependent on func 2 (Avoidance Scheme 1) : INT _sample_svc_func1(UB *buf_caller, INT len) { INT ret, result; UB *tmp; /* * Because the buf_caller is a region that is passed from a user * application (protection level 3), it is necessary to check * whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Processing of func1 * . . . */ tmpbuf = Kmalloc(len); /* Error check omitted for example use*/ /* * Calling up func2 in the middle of this (OK) * * We call up the actual thing not directly, but through the SVC * handler. When we further call up an extended SVC inside an * extended SVC, accessing the tmpbuf becomes possible, because * the access privilege is set at protection level 0 ( = extended * SVC protection level). */ result = _sample_svc_func2(tmpbuf, len); /* . . . * Processing of func1 (continues) */ (void) Kfree(tmpbuf); |
List 6 Memory Access Privilege, Protection Level Relation (4) |
A Case Where func1 Is Dependent on func 2 (Avoidance Scheme 2) : A Case Where You Want to Cut the Overhead of an Extended SVC Call /* * Defining with the respective different functions the processing * after the memory check of func1 and func2 */ LOCAL INT_sample_svc_func1_sub(UB *buf, INT len) { INT ret, result; UB *tmpbuf; /* * Processing of func1 * . . . */ tmpbuf = Kmalloc(len); /* Error check omitted for example use*/ /* * Calling up func2 in the middle of this (OK) * * Because we do not carry out a check of tmpbuf memory access * privileges, the following call normally terminates. */ result = _sample_SVC_func2_sub(tmpbuf, len); /* . . . * Processing of func1 (continues) */ (void) Kfree(tmpbuf); return ret; } LOCAL INT _sample_svc_func2_sub(UB *buf, INT len) { INT ret; /* * Processing of func2 */ return ret; } /* * Processing of func1 (called from SVC handler) */ INT _sample_svc_func1(UB *buf_caller, INT len) { INT ret; UB *tmp; /* * Because the buf_caller is a region that is passed from a user * application (protection level 3), it is necessary to check * whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Calling up instead a subroutine without a memory check */ ret = _sample_svc_func1_sub(buf_caller, len); return ret; } /* * Processing of func2 (to be called from the SVC handler) */ INT _sample_svc_func2(UB *buf_caller, INT len) { INT ret; /* * Because the buf is a region passed from a user application, it * is necessary to check whether memory access is possible or not. */ if (ChkSpaceRW(buf_caller, len) < 0) { /* Memory access privilege error */ return E_MACV; } /* * Calling up instead a subroutine without a memory check */ ret = _sample_svc_func2_sub(buf_caller, len); |
List 7 Utilization of a Resource Management Block in a Subsystem |
typedef struct { INT param#1; INT param#2; } SAMPLE_SSY_RBLK; INT sample_ssy_svc_handler(VP para, FN fncd) { INT ret; ID resid; SAMPLE_SSY_RBLK *r; /* * Obtain the resource management block of the call source: * The SVC handler is called with the task context of the * call source */ resid = tk_get_rid(TSK_SELF); er = tk_get_res(resid, SAMPLE_SSY_SVC, (VP *) &r); if (er < E_OK) { } /* * Subsystem handler processing: * For items where there is a need to carry out management * separately for each process, we utilize r */ return ret; } |
List 8 Securing of a Continuous Region and Physical Memory Conversion |
ER sample_driver_trans_data_with_DMAC(UB *data, INT len) { ER er; VP dmabuf_la, dmabuf_pa; /* Parameter check */ if (data == NULL || Len <= 0) { return E_PAR; } /* Securing the continuous region (automatic allotment) */ er = MapMemory(NULL, len, MM_SYSTEM | MM_READ | MM_WRITE, &dmabuf_la); if (er < E_OK) { /* Error return */ return er; } /* Logical-to-physical address conversion */ if (CnvPhysicalAddr(dmabuf_la, len, &dmabuf_pa) < len) { /* * len byte portion continuous region securing fails * error return after memory release */ (void) UnmapMemory(dmabuf_la); return er; } /* Copy call source transfer data to the continuous region */ (void) memmove(dmabuf_pa, data, len); /* * DMA transfer the dmabuf_pa * Contents abbreviated because of hardware-dependent processing */ /* Release the secured continuous region */ (void) UnmapMemory(dmabuf_la); return E_OK; } |
List 9 Example of Starting Up a Task in T-Kernel |
(1) Example of Starting Up a Task in T-Kernel void sample_tk_start_task(FP task_func, PRI pri) { ER er; T_CTSK ctsk; ID tid; /* Setting of task creation information */ ctsk.exinf = NULL; ctsk.tskatr = TA_HLNG | TA_RNG0; ctsk.task = task_func; ctsk.itskpri = pri; ctsk.stksz = 4 * 1024; ctsk.sstksz = 0; ctsk.stkptr = NULL; ctsk.uatb = NULL; ctsk.lsid = 0; ctsk.resid = 0; (void) memset(ctsk.dsname, 0, sizeof(ctsk.dsname)); /* Task creation */ tid = tk_cre_tsk(&ctsk); if (tid >= 0) { /* * Task startup * In this example, we pass self task ID to stacd. */ er = tk_sta_tsk(tid, tk_get_tid()); if (er == E_OK) { /* Task startup succeeds */ } else { /* Task startup fails */ } } else { /* Task creation fails */ } } (2) Example of a Task Entry Function in T-Kernel void sample_task(INT stacd, VP exinf) { ID ptid; /* Task ID of the parent task */ /* * Obtain the parent task ID * Not necessarily needed, but in communication with the parent task * there are a lot of cases where it can be utilized. */ ptid = stacd; /* * Carries out various types of user defined processing */ /* Special system call necessary in terminating the task. */ tk_exd_tsk(); |
List 10 Example of Starting Up a Process in T-Kernel/SE |
(1) Example of Starting Up a Process in T-Kernel/SE void sample_tkse_start_process(UB *path, PRI pri) { ER er; T_CPRC cprc; ID pid; MESSAGE msg; /* Specify path to be prescribed with standard input/output I/F. */ cprc.prcatr = TPA_USR | TPA_SEIO; cprc.prchdr = path; cprc.pri = pri; msg.msg_type = 0; /* Process creation and startup */ pid = tkse_cre_prc(&cprc, &msg); if (pid >= 0) { /* Process creation and startup succeeds */ } else { /* Process creation and startup fails */ } } (2) Process Entry Function Example in T-Kernel/SE W main(void) { /* * Carries out various types of user defined processing. */ /* Process termination is fine through normal function return.*/ return 0; } |
The above article on T-Kernel appeared on pages 34-42 in Vol. 99 of TRONWARE . It was translated and loaded onto this Web page with the permission of Personal Media Corporation.
Copyright © 2006 Personal Media Corporation
Copyright © 2006 Sakamura Laboratory, University Museum, University of Tokyo