The hard part of system bootstrap is done by the node singleton
itself, in srvstart.
First, we initialize the memory bank for the node core to allow dynamic memory allocation inside the kernel and then we create the portal server so that other servers could create their portals. After doing so, we start the kernel virtual memory service and remaining hardware servers and system servers.
<off_Node::srvstartimplementation. >= (U->) // Creates an Off Node. void off_Node::srvstart(const off_Protection &prot) { assert(valid()); <Create memory banks for node core protected withprot. > <Create KVM. > <Start relocation table operation. > <Create the portal server protected withprot. > <Create hardware system servers protected withprot. > <Create abstract system servers protected withprot. > <Start non boot processors. > }
To know how many memory banks must be created we use the machine
dependent node information, n_mdep, which already knows about
existing hardware. The machine dependent method get_mbank will
return information about every memory banks. Besides we call to
set_core to give memory banks a chance to do any static
initialization which depends on the memory banks installed in the
system.
<Create memory banks for node core protected with prot. >= (<-U)
off_MBank::set_core(n_mdep.get_minfo(0));
n_nmbanks=n_mdep.get_num_mbanks();
for (natural_t i=0; i<n_nmbanks; i++){
n_mbank[i].protect(prot);
n_mbank[i].start(n_mdep.get_minfo(i),get_server_id());
}
Identifiers for containers being created are obtained with
get_server_id as we will see later. Depending on the exact number
of system servers, portal identifiers for a given server may differ
from node to node. This is not a problem because navigators and
inspectors can be used to obtain their identifiers in a portable way.
To initialize the kernel virtual memory server we tell it whether support for DTLB is included and also the first available address after kernel memory.
<Create KVM. >= (<-U) kvm.start(n_mbank[n_nmbanks-1].get_kend(),n_opts->usedmm());
<Off node implementation dependencies. >= (<-U) [D->] #include <dmm/KVM.h> // for kvm
When the KVM has been started, we can start relocation table
operation. We simply need a region of kernel virtual memory to
maintain relocation table entries.
<Start relocation table operation. >= (<-U) off_RelocTbl::start(kvm.create_region());
We do the same with remaining hardware servers. Only processors are a little bit special. The boot processor has code to run, but others must wait until the shuttle server has been started to get their initial idle shuttles.
<Create hardware system servers protected with prot. >= (<-U)
n_niobanks=n_mdep.get_num_iobanks();
for (natural_t i=0; i<n_niobanks; i++){
n_iobank[i].protect(prot);
n_iobank[i].start(n_mdep.get_ioinfo(i),get_server_id());
}
n_nprocs=n_mdep.get_num_procs();
n_proc[0].protect(prot);
n_proc[0].start(n_mdep.get_procinfo(0),get_server_id());
#if 0 // XXX fix this
n_ndmas=n_mdep.get_num_dmas();
for (natural_t i=0; i<n_ndmas; i++){
n_dma[i].protect(prot);
n_dma[i].start(n_mdep.get_dmainfo(i),get_server_id());
}
#endif
<Start non boot processors. >= (<-U)
for (natural_t i=1; i<n_nprocs; i++){
n_proc[i].protect(prot);
n_proc[i].start(n_mdep.get_procinfo(i),get_server_id());
}
Starting (abstract) system servers is almost the same.
<Create the portal server protected with prot. >= (<-U)
// n_prtl->protect(prot); XXX fix this
// n_prtl->start(get_server_id());
<Create abstract system servers protected with prot. >= (<-U)
#if 0 // XXX fix this
n_shtl->protect(prot);
n_shtl->start();
n_dmm->protect(prot);
if (n_opts->usedmm()){
n_dmm->start(get_server_id());
}
#endif
We start the \dmm{} only when \dtlb{} support is required.
The srvstart implementation is also kept in node/Node.C.
<Implementation of other methods ofoff_Node. >+= (<-U) [<-D->] <off_Node::srvstartimplementation. >
To assign identifiers for system servers, we used
get_server_id.
<Other public methods of off_Node. >+= (<-U) [<-D->]
// Obtain the indentifier for a new system server.
off_id_t get_server_id(void);
Its implementation uses the sequence number stored in the node instance to generate identifiers taking as a template the node identifier.
<off_Node::get_server_id implementation. >= (U->)
// Obtain the indentifier for a new system server.
off_id_t off_Node::get_server_id(void)
{
off_id_t id = get_id();
assert(valid());
id.i_seq = get_seq();
id.i_slot = (off_slot_t)id.i_seq;
return id;
}
<Implementation of other methods ofoff_Node. >+= (<-U) [<-D->] <off_Node::get_server_idimplementation. >
We set the slot field with the same value of the seq field
because portals will be created (to export system servers services) to
match this identifiers. The portal server will assign slots in its
internal allocator matching the seq field. Therefore, we avoid
fake relocations by initializing the slot field.
Note also how locking is missing. When this method is used the node will be booting and it will be no longer used before any shuttle is ever created.