This section explains the implementation of the SBC8260 reboot program.
"Reboot Program Initialization" describes how the reboot program is initialized during the system boot.
"HotRebootDesc Structure" describes the reboot program's global state.
"Rebooting the Board" describes how the reboot program proceeds to reboot the system.
Example 4-1is the reboot program initialization source code provided in src/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
#define MAX_CHUNKS 16 #define STR_LENGTH 256 PrstChunk chunks[MAX_CHUNKS]; char chunk_id_buf[STR_LENGTH]; RebootDesc rebootDesc; BootConfRebootEntry rebootEntry; DbgOps_reset resetOp; BootParams bootParams; void reboot_main(BootConf* conf, BootParams* bootp) { if (conf->rebootDesc == 0) { /* * This is a cold reboot. */ /* * Intialize printf()/scanf() support library */ _stdc_consInit(conf->dbgOps.consRead, conf->dbgOps.consWrite); /* * Register hot reboot descriptor */ rebootDesc.rebootOp = do_reboot; rebootDesc.hot.prstMem.numChunks = 0; rebootDesc.hot.prstMem.chunks = chunks; rebootDesc.hot.prstMem.maxChunks = MAX_CHUNKS; rebootDesc.hot.prstMem.curStrLen = 0; rebootDesc.hot.prstMem.maxStrLen = STR_LENGTH; rebootDesc.hot.prstMem.str = chunk_id_buf; conf->rebootDesc = &rebootdesc; /* * Save hot reboot entry point */ rebootEntry = conf->rebootEntry; /* * Save reset entry point */ resetOp = conf->dbgOps.reset; /* * Save bootparams */ if (bootp) { bootParams = *bootp; } } else { /* * This is a hot reboot. * * Nothing to do as the reboot program was intialized at the last cold * reboot. */ } }
The reboot program is installed only once during a cold boot. For subsequent hot reboots, the reboot program maintains the section of the system state that must be kept. reboot_main() is the reboot program entry point and is called by the bootstrap program each time the system boots.
For a cold boot, when the rebootDesc field of the conf argument is NULL, the reboot program intializes its static data. Whereas for a hot boot the reboot program re-intialization is empty.
One purpose of the program is to maintain a stable system state during hot reboots. This state is represented by a generic HotRebootDesc structure (see "HotRebootDesc Structure") and a board specific BootParams structure (see "Bootstrap Program Implementation").
The bootstrap program exports the address of the reboot service routine, do_reboot(), in the rebootOp field of the rebootDesc structure. The microkernel jumps to this routine to proceed with any kind of reboot. See "HotRebootDesc Structure" for details.
Example 4-2 is the HotRebootDesc structure definition provided in kernel/include/chorus/bki/reboot.h.
/* * PrstChunk::status bit values */ #define PRST_CHUNK_ALLOCATED 0x1 #define PRST_CHUNK_MAPPED 0x2 /* * Descriptor of a chunk of persistent memory */ typedef struct PrstChunk { char* id; /* name string */ int status; /* status bit string */ VmSize size; /* size in bytes */ PhAddr paddr; /* starting physical address * valid if PRST_CHUNK_ALLOCATED is set */ VmAddr vaddr; /* starting virtual address */ * valid if PRST_CHUNK_MAPPED is set */ } PrstChunk; /* * Persistent memory descriptor */ typedef struct PrstMem { int maxChunks; /* max number of persistent memory chunks */ int numChunks; /* number of persistent memory chunks */ PrstChunk* chunks; /* array of persistent memory chunks */ int maxStrLen; /* max cumulated size of chunk ids */ int curStrLen; /* current cumulated size of chunk ids */ char* str; /* chunk ids buffer*/ } PrstMem; /* * The state kept over a hot reboot */ typedef struct HotRebootDesc { PrstMem prstMem; /* persistent memory descriptor */ } HotRebootDesc;
The prstMem field of HotRebootDesc describes the persistent memory (the portion of system RAM that must remain the same during subsequent hot reboots). The description is an array of PrstChunk structures, each one describing a persistent memory device (a contiguous named portion of persistent memory). The array is statically allocated in the reboot program data segment.
A persistent memory device is described by its symbolic name, id, the starting physical address, paddr, the starting virtual addresses, vaddr, and the size.
Different operating system components are involved in persistent memory device allocations:
The allocation request comes from the sysReboot() system call. The system call specifies the name and size of the device to be created during the subsequent hot boot.
The reboot program allocates a new PrstChunk descriptor and initializes the id and size fields with the values specified by the sysReboot() call. See "Hot Reboot" for details.
Then, after reboot, the bootstrap program dynamically allocates RAM blocks and initializes the paddr field. It also sets the PRST_CHUNK_ALLOCATED bit in the status field. See "kernel_start()" for details.
Finally, the micro-kernel allocates a virtual address range and sets the vaddr field. It also sets the PRST_CHUNK_MAPPED bit in the status field.
This section describes how the reboot program reboots the board.
"Cold Reboot" describes the cold reboot implementation.
"Hot Reboot" describes the hot reboot implementation.
"Rebooting The New System Image" describes how the reboot program boots a new system image.
Example 4-3 is the reboot service routine source code provided in src/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c
void do_reboot(RebootDesc* rd, KnRebootReq* req) { ASSERT(rd == &rebootDesc); if (req->mode == K_REBOOT_COLD) { rebootCold(req); } if (req->mode == K_REBOOT_HOT) { rebootHot(req); } if (req->mode == K_REBOOT_NEW) { rebootNew(req); } ASSERT(0); }
This service routine dispatches execution to a particular reboot procedure.
Example 4-4 is the cold reboot service routine source code provided in src/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
void rebootCold(KnRebootReq* req) { printf ("Cold reboot ...\n"); resetOp(0); }
This cold reboot service calls the debug agent hardware reset service routine.
Example 4-5 is the hot reboot service routine source code provided in src/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
#define STACK_SIZE 0x400 char stack[STACK_SIZE]; void rebootHot(KnRebootReq* req) { printf ("Hot reboot ...\n"); /* * Extend the persistemt memory w.r.t the rebbot request */ prstExtend(&rebootDesc.hot.prstMem, &req->u.hot); /* * switch to a private stack */ call_and_switch_stack(req, rebootHot_cont, stack + STACK_SIZE); } void rebootHot_cont(KnRebootReq* req) { /* * Disable I/D-Caches. */ cachesDisable(); /* * Disable pagination. */ setMSR(getMSR() & ~(MSR_IR | MSR_DR)); /* * Reboot */ rebootEntry(&rebootDesc, &bootParams); }
This hot reboot service routine:
Allocates, if required, a new persistent memory device descriptor. See "prstExtend() Routine" for details.
Enters in the real mode, disabling the memory management unit.
Jumps to the bootconf program reboot entry point.
Note that the implementation switches from the original stack to a small stack area which is statically allocated in the reboot program data segment. This occurs prior to the MMU disabling to ensure that the current stack is safely accessible in the real mode. See "call_and_switch_stack() routine" for details.
The ChorusOS boot monitor is implemented as an application on top of ChorusOS. The boot monitor reads the new system image from an external medium (for example, a network) and stores that image in a buffer. It then performs a sysReboot() request that eventually invokes the rebootNew() service routine. To simplify the reboot implementation, the ChorusOS boot monitor has to use a ChorusOS system, configured with flat memory management, model.s. Example 4-6 is the service routine source code provided in src/nucleus/bsp/powerpc/sbc8260/src/reboot/reboot.c.
void rebootNew(KnRebootReq* req) { KnNewRebootReq* nreq = >u.nw; BootParams* bootp; printf ("Boot new image ...\n"); if (nreq->workSize < (launch_end - launch_start) + sizeof(BootParams)) { printf ("Not enough working memory to reboot\n"); printf ("Try cold reboot\n"); resetOp(0); } /* * Disable I/D-Caches. */ cachesDisable(); if (nreq->ipcSiteNb) { /* * Get Chours IPC site number from the loader */ bootParams.ipcSiteNb = nreq->ipcSiteNb; } /* * Copy bootParams at the end of the working area */ bootp = (BootParams*) (nreq->workAddr + nreq->workSize - sizeof(BootParams)); bcopy(&bootParams, bootp, sizeof(BootParams)); /* * Copy the launch routine to the working memory */ bcopy(launch_start, (void*) nreq->workAddr, launch_end - launch_start); /* * Jump to the copied launch code */ ((launch) nreq->workAddr)(nreq->dstAddr, nreq->srcAddr, nreq->size, nreq->entry, bootp); }
The caller (that is, sysReboot()) provides a working area that is separate to that from the current system image, including the currently executing reboot program. The buffer, holding the new system image, and the system image destination area are also outside the working area. In the working area, the rebootNew() routine installs the bootParams structure and a small positional independent program, called launch(), and then runs launch(). The launch() program copies the new system image from the buffer to the destination address and jumps to its entry point, passing the address of the bootParams structure as the first argument. See "launch() Routine" for details.