Migration from T-Kernel to T-Kernel/Standard Extension

Shuichi Takayama and Hiroki Imai

YRP Ubiquitous Networking Laboratory


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.

System Calls

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.

Table 1 Modifiable System Calls: Process/Task Management Functions
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.

Table 2 Modifiable System Calls: Task Synchronization Communication Functions
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.

Table 3 Modifiable System Calls: Device Management
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.

Table 4 Modifiable System Calls: Time Management Functions
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.

Figure 1. Memory space in the T-Kernel/SE environment

Memory Management

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).

Figure 2. Subsystem internal module composition

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

Table 5 Memory Acquisition/Release System Calls

Target Memory

Function

Call Name
Resident Shared
Memory
Memory allocation Kmalloc
Kcalloc
Memory reallocation Krealloc
Memory release Kfree
Non-Resident
Shared Memory
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.

Table 6 Library Calls Prepared in T-Kernel/SE

Target Memory

Function

Call Name
Non-Resident
Local Memory
Memory allocation malloc
calloc
Memory reallocation realloc
Memory release free
Non-Resident
Shared Memory
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.

Table 7 Memory Attributes in Drivers and Middleware
 

SVC Handler

Process Task
Time Event Handler
Task Space
Same as request source task
Specified as the time of creation
(Can be modified with SetTaskSpace)
Undecided
Protection Level
Special privilege level
Specified at the time of task creation
Special privilege level
Access Privilege
Information
Same as request source task
Specified at the time of task creation
(Can be modified with SetTaskSpace)
Undecided
Resource ID
Same as request source task
Specified at the time of task creation
(Can be modified with SetTaskSpace)
Undecided

 

Figure 3. Local memory switching

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).

Table 8 Local Memory-Related System Calls

Function

Call Name
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

Table 9 Address Space Management Function System Calls (1)

Function

Call Name
Checking of read-in access privilege
ChkSpaceR
ChkSpaceBstrR
ChkSpaceTstrR
Checking of read-in/write-an access privilege
ChkSpaceRW
ChkSpaceBstrRW
ChkSpaceTstrRW
Checking of read-in access privilege and execution privilege
ChkSpaceRE

 

Figure 4. Access privilege check

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).

Table 10 Address Space Management Function System Calls (2)

Function

Call Name
Logical address to physical address conversion
(also possible to simultaneously acquire the physically continuous size)
CnvPhysicalAddr
Mapping to the logical space of physical memory
(also possible to secure memory that is newly physically continuous)
MapMemory
Release of memory that has been mapped to logical space
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).

Application Startup

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).

Compile/Link

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.

____________________

Lists

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