bootimage.c
Skip to line: 7050 - 7100 - 7150 - 7200 - 7250 - 7300 - 7350 - 7400 - 7450 - 7500 - 7550 - 7600 - 7650


Highlighted entries were made in the last day
Select a different time increment to highlight entries
Current GMT time: Apr 26 2024 17:10:11

If you have a comment for bootimage.c, please click here.
Expand/Collapse Item
Expand/Collapse Item7000    /*     bootimage.c - Load an image and start it.      Author: Kees J. Bot
Expand/Collapse Item7001     *                                                            19 Jan 1992
Expand/Collapse Item7002     */
bootminix() (line 7647) is invoked by the boot monitor boot command and (if options are passed to the kernel) by the boot monitor boot -optsvar command.  bootminix() is the high-level function that:

1)  calls
select_image() (line 7589) to find the desired OS image file on disk.

2)  calls
exec_image() (line 7348); exec_image() loads the OS image and calls minix(), which switches the system to protected mode (if the kernel was compiled for protected mode) and then jumps to the kernel.

bootminix() returns when the OS returns to the boot monitor (for example, when the user issues the shutdown command).
Expand/Collapse Item7003    #define BIOS          1       /* Can only be used under the BIOS.*/
#define is a preprocessor command.  The preprocessor replaces all occurrences of the first string (in this case, "BIOS") with the second string (in this case, "1") before compilation.

The string "
BIOS" actually never occurs in this file after this #define.  It is important to note, however, that bootimage.c is used to create boot, the boot monitor, but is not used to create the edparams utility program.
Expand/Collapse Item7004   #define nil 0
The string "nil" is used to increase readability.
7006
7005   #define _POSIX_SOURCE   1
Expand/Collapse Item7006    #define _MINIX        \1
_POSIX_SOURCE and _MINIX are macros that are used by library routines.  In fact, nearly all macros that begin with an underscore (_) are specific to library routines.

The POSIX standard was created to improve portability between UNIX systems.  The 12th paragraph of section 2.6.2, lines 01184-011200, and lines 01241-01245 in Operating Systems describe the
_POSIX_SOURCE and _MINIX macros.
Expand/Collapse Item7007    #include <stddef.h>
A function must be either defined or declared in a file before it can be used.  Header files (files ending in .h) make the task of declaring variables easier.

Before compilation begins, the preprocessor replaces any
#include <filename.h> statement with the contents of filename.h.  If the filename is enclosed in < and >, the preprocessor searches for the file in the default directory (typically /usr/include) and other directories specified by the -I option of the compiler (see line 7018).  If the filename is quoted (see line 7021), the preprocessor looks for the include file in the same directory that the source file is found.  (In this case, bootimage.c is the source file and is found in the /usr/src/boot directory.)

As an example,
strlen() (see line 7622) is declared in the file string.h (see line 7012).  string.h is located in the directory /usr/include.

Header files, in addition to containing function declarations, also frequently contain
#defines.  For example, RATIO (see line 7232) is #defined in boot.h.  Since boot.h is quoted (see line 7023), the preprocessor searches for boot.h in the same directory as bootimage.c.  Indeed, both files are in the /usr/src/boot directory.
7008   #include <sys/types.h>
7009   #include <sys/stat.h>
7010   #include <stdlib.h>
7011   #include <limits.h>
7012   #include <string.h>
Expand/Collapse Item7013    #include <errno.h>
errno is declared in errno.h (see line 00230 in the book) as extern.  Memory for a variable can be allocated in only one file (i.e. the variable is "defined") but the variable must be declared as extern in every other file that accesses it.  However, memory is not allocated for errno in boothead.s, boot.c, bootimage.c, or rawfs.c.  If you understand how memory is allocated for errno, please submit a comment to the site which will be displayed below.
Name: Christos KarayiannisEmail:  christos@kar.forthnet.grDate: Dec 07 2003 17:10:23 GMT
Subject:  where is errno defined?
Reponse: errno is declared in errno.h as extern and is defined in src/lib/other/errno.c
This file contains:

=========

#include
/* errno.c - declare variable errno Author: F. Meulenbroeks */

int errno = 0;

=========

Note that includes
This is because the .c file containing the definition should also #include the header file containing the external declaration, so that the compiler can check that the declarations match.
Name: Christos KarayiannisEmail:  christos@kar.forthnet.grDate: Dec 13 2003 17:18:19 GMT
Subject: Re: where is errno defined?
Reponse: Somehow some words are missing from my text.
Please add lib.h after the #include
and replace the "Note that includes" with
"Note that lib.h includes errno.h"
(I ommited the brackets in the header files because those was the reason of the problem)
 
 Continue discussion.
 
 
7014   #include <a.out.h>
7015   #include <minix/config.h>
7016   #include <minix/const.h>
7017   #include <minix/type.h>
Expand/Collapse Item7018    #include <kernel/const.h>
If a filename is enclosed in < and >, the preprocessor searches for the file in the default directory (typically /usr/include) and other directories specified by the -I option of the compiler.  In Makefile, the parent directory (..) is specified by the -I option.  Makefile is found in /usr/src/boot; therefore, the parent directory is /usr/src.  After the preprocessor unsuccessfully looks for const.h in the /usr/include/kernel directory, the preprocessor looks for (and finds) const.h in the /usr/src/kernel directory.  Note that the preprocessor can't find const.h in /usr/src/kernel since this directory doesn't exist.
7019   #include <kernel/type.h>
7020   #include <ibm/partition.h>
7021   #include "rawfs.h"
7022   #include "image.h"
7023   #include "boot.h"
7024
Expand/Collapse Item7025    #define click_shift clck_shft      /* 7 char clash with click_size. */
The kernel often uses "clicks" instead of bytes to describe the size of memory objects.  The click shift for the kernel is found at the beginning of the kernel code (i.e. text) segment (see line 06053 in the book) and is equal to 8.  A size in bytes is shifted 8 bits to the right to get the size in clicks.  For example, 0x0300 bytes is equal to 0x03 clicks.

The kernel and the boot monitor save space by converting sizes in bytes to sizes in clicks.  The text and data sizes in clicks for the kernel, memory manager (mm), file system (fs), and init (plus any other server that is part of the OS image) are patched into the beginning of the kernel data (see line 7182).  2 bytes are used for each size in clicks; if byte sizes were instead used, 2 bytes would not be enough.
7026
Expand/Collapse Item7027  /*Some kernels have extra features: */
Different kernels have different capabilities and different requirements.  The word (2 bytes) at offset FLAGS_OFF (=4; see line 7056) into the kernel code (see line 06055 in Operating Systems ) holds the 8 flags that define these capabilities and requirements.  The value of this word is stored in k_flags (on line 7305).

Note that
k_flags for the kernel given in the book is 0x2D ( K_I386, K_CHMEM, K_HIGH, and K_RET are set).
Expand/Collapse Item7028    #define K_I386   0x0001 /* Make the 386 transition before you call me. */
In boothead.s, a switch to protected mode is made if the K_I386 flag is set in k_flags.
Expand/Collapse Item7029    #define K_CLAIM  0x0002 /* I will acquire my own bss pages, thank you. */
If the kernel flag K_CLAIM is set, memory for the stack or the bss is not allocated to any process except the kernel (see line 7481).
Expand/Collapse Item7030    #define K_CHMEM  0x0004 /* This kernel listens to chmem for its stack size. */
If the kernel flag K_CHMEM is not set, memory for the stack is not allocated to any of the processes (see lines 7435 and 7497-7503).
Expand/Collapse Item7031    #define K_HIGH   0x0008 /* Load mm, fs, etc. in extended memory. */
If the K_HIGH flag is set in k_flags, mm, fs, inet (if network support is enabled) and init are loaded into extended memory (>1MB) (see line 7508).  Note that your system must have extended memory if you compile a kernel with this flag set.
Expand/Collapse Item7032    #define K_HDR    0x0010 /* No need to patch sizes, kernel uses the headers. */
Various sizes need to be patched into various locations (see patch_sizes() on line 7160).  However, if the kernel flag K_HDR flag is set, this patching is unnecessary and the kernel instead uses the headers to determine the sizes.
Expand/Collapse Item7033    #define K_RET    0x0020 /* Returns to the monitor on reboot. */
If the K_RET flag is set in k_flags, the shutdown command returns minix to the boot monitor.  The return code is in boothead.s.
Expand/Collapse Item7034    #define K_INT86  0x0040 /* Requires generic INT support. */
The code for generic INT support is in boothead.s.   Generic INT support allows the kernel to switch back to real mode to make a bios function call.
Expand/Collapse Item7035    #define K_MEML   0x0080 /* Pass a list of free memory. */
The array mem[] is the memory map (or list).  mem[] is initialized in boothead.s and is passed to the kernel as the environment variable memory.
7036
7037
7038 /*Data about the different processes. */
7039
Expand/Collapse Item7040    #define PROCESS_MAX    16     /* Must match the space in kernel/mpx.x */
The text and data click sizes (see comments for line 7025) for the kernel, memory manager (mm), file system (fs) and init (plus any other server that is part of the OS image) are patched into the beginning of the kernel data (see line 7182).  16*2*2 = 64 bytes are reserved for these sizes on line 06478 in the book - 2 bytes for each text size and 2 bytes for each data size.  64 bytes is enough space for the kernel sizes, mm sizes, fs sizes, init sizes and the sizes for 12 other servers.

Note that the magic number on line 06480 (in the book) is overwritten.
exec_image() (line 7348) first checks the magic number (see line 7521) and then patches the sizes (see line 7528).
Expand/Collapse Item7041    #define KERNEL        0       /* The first process is the kernel.*/
Expand/Collapse Item7042    #define FS             2       /* The third must be fs. */
The OS is loaded into memory in the following order:

1) kernel
2) memory manager (mm)
3) file system (fs)
4) inet (if network support is enabled)
5) init
7043
Expand/Collapse Item7044    struct  process{       /* Per-process memory adresses. */
struct process describes the layout of a process in memory. process[] (see line 7050) holds the memory layout for each process in the OS image file.  process[] is populated by exec_image() (see lines 7448, 7450, 7458, 7463, 7465, and 7506) and then the values from process[] are used to patch sizes into the kernel data section (see patch_sizes() on line7160).
Expand/Collapse Item7045           u32_t  entry;         /* Entry point.*/
entry is the offset of the first instruction to be executed in an executable.  For example, process[KERNEL].entry is the first argument in the call to minix() (see lines 7055-7056).
Expand/Collapse Item7046           u32_t  cs;            /*Code segment. */
Expand/Collapse Item7047           u32_t  ds;            /*Data segment. */
Expand/Collapse Item7048           u32_t  data;          /* To access the data segment. */
Expand/Collapse Item7049           u32_t   end;           /* End of this process, size = (end - cs). */
cs is the absolute memory address of the beginning of the code and data is the absolute memory address of the beginning of the data. end is the absolute memory address of the end of the process in memory.  The meaning of ds depends on whether the A_SEP flag is set (see line 7453).  (see comment on line 7212 for an explanation of the A_SEP flag.)

7050   }process[PROCESS_MAX ];
Expand/Collapse Item7051    int  n_procs;                   /* Number of processes. */
If the kernel, the memory manager (mm), the file system (fs), the network server (inet), and init are in the OS image file and all these processes are loaded into memory, n_procs will be equal to 5.  n_procs is set in exec_image() (see line 7515).
7052
7053 /*Magic numbers in process' data space. */
Expand/Collapse Item7054    #define MAGIC_OFF     0       /* Offset of magic # in data seg.*/
The magic number (KERNEL_D_MAGIC = 0x526F) is at the beginning (MAGIC_OFF = 0) of the kernel data segment (see line 06478 in the book).

Note that
MAGIC_OFF and P_SIZ_OFF (see line 7060) are both offsets into the kernel data segment and are both equal to 0.  The text and data sizes (see comments for line 7040) are patched into the kernel data segment at offset P_SIZ_OFF (see line 7075). exec_image() (line 7348) first checks the magic number (see line 7521) and then patches over the magic number with the sizes (see line 7528).
Expand/Collapse Item7055    #define CLICK_OFF     2       /* Offset in kernel text to click_shift.*/
Expand/Collapse Item7056    #define FLAGS_OFF      4       /* Offset in kernel text to flags.*/
The kernel often uses "clicks" instead of bytes to describe the sizes of different memory objects.  The click shift for the kernel is found at an offset of CLICK_OFF in the kernel code (i.e. text) segment (see line 06053 in the book) and is equal to 8.  A size in bytes is shifted 8 bits to the right to get the click size.  For example, 0x0300 bytes is equal to 0x03 clicks.

The kernel and the boot monitor save space by converting byte sizes to click sizes.  The text and data click sizes for the kernel, memory manager (mm), file system (fs), and init (plus any other server that is part of the OS image) are patched into the beginning of the kernel data (see line 7182).  2 bytes are used for each click size; if byte sizes were instead used, 2 bytes would not be enough.

The kernel flags are found at an offset of
FLAGS_OFF in the kernel code segment (see line 06055 in the book).  These flags are described in the comments for lines 7028-7035.
7057   #define KERNEL_D_MAGIC  0x526F  /*Kernel magic number. */
7058
7059 /*Offsetsof sizes to be patched into kernel and fs. */
Expand/Collapse Item7060    #define P_SIZ_OFF     0       /* Process' sizes into kernel data.*/
Note that MAGIC_OFF and P_SIZ_OFF (see line 7060) are both offsets into the kernel data segment and are both equal to 0.  The text and data sizes (see comments for line 7040) are patched into the kernel data segment at offset P_SIZ_OFF (see line 7075).  exec_image() (line 7348) first checks the magic number (see line 7521) and then patches the sizes (see line 7528).
Expand/Collapse Item7061    #define P_INIT_OFF     4       /* Init cs & sizes into fs data.*/
The text and data click sizes for init are patched into the file system (fs) data segment (see line 7191).
7062
7063
Expand/Collapse Item7064    #define  between(a,c,z)        ((unsigned) ((c) - (a)) lt;=((z) - (a)))
between() returns TRUE if c is between a and z.
7065
Expand/Collapse Item7066    void  pretty_image(char*image)
OS image files are typically placed in the /minix directory.
7067   /* Pretty print the name of the image to load. Translate '/' and '_' to
7068    * space, first letter goes uppercase. An 'r' before a digit prints as
7069    * 'revision'.  E.g. 'minix/1.6.16r10'->'Minix 1.6.16 revision 10'.
7070    * The idea is that the part before the 'r'is the official Minix release
7071    * and after the 'r' you can put versionnumbers for your own changes.
7072    */
7073   {
7074          int up= 0, c;
7075
Expand/Collapse Item7076           while ((c= *image++) != 0) {
Expand/Collapse Item7077                  if (c == '/' || c == '_') c= ' ';
Replace forward slashes ('/') and underscores ('_') with spaces.
7078
7079                 if (c == 'r' && between ('0', *image, '9')){
7080                         printf(" revision ");
7081                         continue;
7082                }
Expand/Collapse Item7083                  if (!up && between ('a', c, 'z')) c= c -'a'+ 'A';
Expand/Collapse Item7084
Expand/Collapse Item7085                  if (between ('A', c, 'Z')) up= 1;
The first alphabetic character is capitalized.  All subsequent characters are lower-case.
7086
Expand/Collapse Item7087                 putch (c);
putch() prints a single character to the screen.
7088         }
7089   }
7090
Expand/Collapse Item7091    char * params2params(size_t*size)
params2params() returns a string consisting of all the environment variables (but no environment functions).  The string will look something like the following:



params2params() is called by exec_image() on line 7542  The string returned (params) from params2params() is used as an argument to minix() on line 7555.  A reference to paramsize is passed to params2params() on line 7542; paramsize is also used as an argument to minix() on line 7555.
7092   /* Repackage the environment settings for the kernel. */
7093   {
7094          char *parms;
7095          size_t i, z;
Expand/Collapse Item7096          environment*e;
struct environment is declared in boot.h.   The flags field describes the behavior of the environment variable or function.
7097
7098          i=0;
7099          z=64;
Expand/Collapse Item7100           parms=malloc(z * sizeof(char *));
void *malloc(size_t size) allocates size bytes from the heap and returns a pointer to the first byte of the allocated memory. malloc() is declared in stdlib.h.  (The heap is the region between the stack and the bss.)
7101
Expand/Collapse Item7102           for(e= env;e != nil ; e= e->next) {
We look at each environment variable and function.  Only environment variables are passed to the minix kernel (see line 7107).
7103                 char *name= e->name, *value= e->value;
7104                 size_t n;
7105                 dev_t dev;
7106
Expand/Collapse Item7107                  if (!(e->flags & E_VAR))continue;
Only environment variables are passed to the minix kernel.  Environment functions are not passed to the minix kernel.
7108
7109                 if (e->flags & E_DEV){
Expand/Collapse Item7110                          if ((dev= name2dev(value)) == -1) {
name2dev() returns the architecture-independent device number for the device.  The architecture-independent device numbers are:


7111                                  free(parms);
7112                                  errno= 0;
7113                                 returnnil ;
7114                        }
Expand/Collapse Item7115                          value=ul2a10((u16_t) dev);
char *ul2a10(u32_t n) (line 5746) converts n (an unsigned long) to an ascii string (base 10).
7116               }
7117
Expand/Collapse Item7118                  n= i + strlen(name) + 1 + strlen(value) + 1;
1 byte is allocated for the equal sign "=" (see line 7124) and 1 byte is allocated for the terminating 0.
7119                  if (n > z) {
7120                         z+= n;
Expand/Collapse Item7121                          parms= realloc(parms, z * sizeof(char));
64 bytes are originally allocated for parms (see lines 7099-7100).  If 64 bytes are insufficient, the memory is reallocated for each additional environment variable.

void *realloc(void *p, size_t size) changes the size of the memory object that is pointed to by p to size.
7122               }
7123                 strcpy(parms + i, name);
7124                 strcat(parms + i, "=");
7125                 strcat(parms + i, value);
7126                 i= n;
7127         }
Expand/Collapse Item7128           parms[i++]=0;  /* End marked with empty string. */
2 consecutive terminating 0's mark the end of parms.

7129          *size=i;
7130          returnparms;
7131   }
7132
Expand/Collapse Item7133    void  raw_clear(u32_taddr,u32_t count)
A trick is used to make this function as efficient as possible:  The first 128 zeroes are copied from the array zeros[] to the memory address addr.  The next 128 zeroes are copied from address addr (not from zeros[]) to address addr+128.  The next 256 zeroes are copied from address addr to address addr+256.  The next 512 zeroes are copied from address addr to address addr+512.  This continues until count zeroes have been copied.

The figures below describe how 600 bytes (starting at addr) are zeroized.

7134   /* Clear "count" bytes at absolute address"addr". */
7135   {
Expand/Collapse Item7136           static char zeros[128];
Elements of an array are initialized to 0.
7137          u32_t dst;
7138          u32_t zct;
7139
7140          zct= sizeof(zeros);
7141          if (zct > count) zct= count;
Expand/Collapse Item7142          raw_copy(addr, mon2abs(&zeros), zct);
void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.
7143          count-= zct;
7144
7145          while (count > 0) {
7146                  dst= addr + zct;
7147                  if (zct > count) zct= count;
Expand/Collapse Item7148                raw_copy(dst, addr, zct);
void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddr.
7149                  count-= zct;
Expand/Collapse Item7150                   zct*= 2;
Each loop copies twice as many zeroes as the loop before.
7151         }
7152   }
7153
7154   /* Align a to a multiple of n (a power of 2):*/
Expand/Collapse Item7155    #define align(a,n)     (((u32_t)(a) + ((u32_t)(n) - 1)) & ~((u32_t)(n)- 1))
align(a,n) rounds a up to the next multiple of n . n must be a multiple of 2.
 

align(3251, 256) = (((u32_t)(3251)+((u32_t)(256)-1)) & ~((u32_t)(256)-1)) =

(((u32_t)(3251)+((u32_t)(255)) & ~((u32_t)255)) =

((u32_t)3506) & 0xFFFFFF00 =         (0xFFFFFF00 is
                                                                              equal to ~((u32_t)255))
(0x00000DB2) & 0xFFFFFF00 =

0x00000D00 = 3328         ( 3328 is a multiple of 256 - successful! )
 
 

This is a slightly more difficult example:

align(-56, 256) = (((u32_t)(-56)+((u32_t(256)-1)) & ~((u32_t)(256) - 1)) =

(((u32_t)(-56)+((u32_t(255)) & ~((u32_t)255)) =

((u32_t)(0xFFFFFFC8+0x000000FF) & 0xFFFFFF00) =

(0x000000C7 & 0xFFFFFF00) = 0  (-56 is rounded up to the next multiple
                                                                                         of 256 - 0)

Here's a review of the 2's complement representation of negative numbers:

56=0x00000038

Bit by bit complement:
0xFFFFFFC7

Add 1 to that:
0xFFFFFFC7
+1
----------
0xFFFFFFC8

(u32_t)(-56) = 0xFFFFFFC8        (in 2's complement)
Expand/Collapse Item7156    unsigned click_shift;
Expand/Collapse Item7157    unsigned click_size;    /* click_size = Smallest kernel memory object. */
The kernel often uses "clicks" instead of bytes to describe the size of memory objects.  The click shift for the kernel is found at the beginning of the kernel code (i.e. text) segment (see line 06053 in the book) and is equal to 8.  A size in bytes is shifted 8 bits to the right to get the size in clicks.  For example, 0x0300 bytes is equal to 0x03 clicks.

The kernel and the boot monitor save space by converting sizes in bytes to sizes in clicks.  The text and data sizes in clicks for the kernel, memory manager (mm), file system (fs), and init (plus any other server that is part of the OS image) are patched into the beginning of the kernel data (see line 7182).  2 bytes are used for each size in clicks; if byte sizes were instead used, 2 bytes would not be enough.

The relationship between
click_size and click_shift is:

click_size= 1 << click_shift  (see line 7313)

Since
click_shift for the minix kernel is 8:

click_size= 1 << click_shift = 1 << 8 = 256
Expand/Collapse Item7158    unsigned k_flags;      /* Not all kernels are created equal. */
The kernel flags are found at an offset of FLAGS_OFF in the kernel code segment (see line 06055 in the book).  These flags are described in the comments for lines 7028-7035.  k_flags is set in get_clickshift() (see line 7305).
7159
Expand/Collapse Item7160    void  patch_sizes(void)
Expand/Collapse Item7161    /* Patch sizes of each process into kernel data space, kernel ds into kernel
Expand/Collapse Item7162     * text space, and sizes of init into data space of fs.  All the patched
Expand/Collapse Item7163     * numbers are based on the kernel click size, not hardware segments.
Expand/Collapse Item7164     */
The kernel often uses "clicks" instead of bytes to describe the size of memory objects.  The click shift for the kernel is found at the beginning of the kernel code (i.e. text) segment (see line 06053 in the book) and is equal to 8.  A size in bytes is shifted 8 bits to the right to get the size in clicks.  For example, 0x0300 bytes is equal to 0x03 clicks.

The kernel and the boot monitor save space by converting sizes in bytes to sizes in clicks.  The text and data sizes in clicks for the kernel, memory manager (mm), file system (fs), and init (plus any other server that is part of the OS image) are patched into the beginning of the kernel data (see line 06471-06483 in the book and see line 7182 below).  2 bytes are used for each size in clicks; if byte sizes were instead used, 2 bytes would not be enough.
7165   {
7166          u16_t text_size, data_size;
7167          int i;
7168          struct process *procp, *initp;
7169          u32_t doff;
7170
Expand/Collapse Item7171           if (k_flags & K_HDR ) return;   /* Uses the headers. */
If K_HDR is set, the sizes don't need to be patched into the kernel and file system.
7172
7173          /* Patch text and data sizes of the processes into kernel data space.
7174           */
Expand/Collapse Item7175           doff= process [KERNEL ].data + P_SIZ_OFF ;
The sizes are patched into the beginning of the kernel data (P_SIZ_OFF = 0).
7176
7177          for (i= 0; i < n_procs ; i++) {
7178                  procp= &process [i];
Expand/Collapse Item7179                   text_size= (procp->ds - procp->cs) >> click_shift;
Expand/Collapse Item7180                   data_size= (procp->end - procp->ds) >> click_shift;
cs is the absolute address of the beginning of the code segment, ds is the absolute address of the beginning of the data segment, and end is the absolute address of the end of the process.

The sizes are in clicks rather than bytes (i.e. the byte sizes are shifted to the right by
click_shift (=8) bits).
7181
7182                  /* Two words per process, the text and data size: */
Expand/Collapse Item7183                put_word(doff, text_size); doff+= 2;
Expand/Collapse Item7184                put_word(doff, data_size); doff+= 2;
void put_word(u32_t addr, u16_t word) puts the 2-byte word at absolute address addr.
7185
7186                  initp= procp;   /* The last process must be init. */
7187         }
7188
Expand/Collapse Item7189           if (k_flags & (K_HIGHK_MEML )) return;  /* Doesn't need FS patching.*/
Since the kernel in the book (see line 06055 in the book) loads high, the file system doesn't leave any space to patch in sizes.
7190
7191          /* Patch cs and sizes of init into fs data. */
7192         put_word (process [FS ].data + P_INIT_OFF +0, initp->cs >> click_shift );
7193         put_word (process [FS ].data + P_INIT_OFF +2, text_size);
7194         put_word(process [FS ].data + P_INIT_OFF +4, data_size);
7195   }
7196
Expand/Collapse Item7197    int  selected(char *name)
selected() returns FALSE (0) if name, a process in the OS image, should not be loaded into memory.

The kernel, memory manager (mm), file system (fs), network manager (inet), and init are compiled independently (which is one of the advantages of the micro kernel design).  installboot (using the -i option) "glues" the kernel, mm, fs, inet and init into an OS image.

Let's say you have 2 different versions of inet, a stable version and an experimental version, in an OS image.  You can choose at boot time which version is to be loaded; if you named the stable version
stable:inet and the experimental version test:inet and you wish to test out the experimental network manager:

hd2a>label test
hd2a>boot

The experimental version of the network manager is loaded into memory with the kernel and the other servers; the stable version is not loaded.
7198   /* True iff name has no label or the proper label. */
7199   {
7200          char *colon, *label;
7201          int cmp;
7202
Expand/Collapse Item7203           if ((colon= strchr(name, ':')) == nil ) return 1;
char *strchr(char *cs, char c)  returns a pointer to the first occurrence of c in cs or nil (0) if not present. strchr() is declared in string.h.

If
name does not contain a colon (:), it is loaded into memory.
Expand/Collapse Item7204           if ((label= b_value ("label")) == nil ) return 1;
If label is not defined, all processes in an OS image are loaded into memory.
7205
Expand/Collapse Item7206           *colon= 0;
Expand/Collapse Item7207           cmp= strcmp(label, name);
int strcmp(char *s1, char *s2) compares string s1 to string s2 and returns 0 if the two strings are the same.

Expand/Collapse Item7208           *colon= ':';
7209          return cmp == 0;
7210   }
7211
Expand/Collapse Item7212    u32_t  proc_size(struct image_header *hdr)
struct image_header is declared in image.h. struct exec is declared in a.out.h (see line 01400 in the book).

The
A_PAL (Page ALigned) flag indicates that the header appears twice, alone in the process's first sector and in the second sector directly before the text (see the figure for A_PAL, !A_SEP and the figure for A_PAL, A_SEP).

The
A_SEP (SEParate) flag indicates that the data segment begins on a sector boundary (see figure 3 below).



align(a,n) (line 7155) rounds a up to the next multiple of nn must be a multiple of 2.

SECTOR_SIZE=512 (a sector is 512 bytes) and SECTOR_SHIFT =9. (1=SECTOR_SIZE>>SECTOR_SHIFT)
7213   /* Return the size of a process in sectors as found in an image. */
7214   {
7215           u32_t len= hdr->process.a_text;
7216
7217           if (hdr->process.a_flags & A_PAL) len+= hdr->process.a_hdrlen;
7218           if (hdr->process.a_flags & A_SEP) len= align (len, SECTOR_SIZE);
7219          len= align (len + hdr->process.a_data, SECTOR_SIZE);
7220
7221          return len >> SECTOR_SHIFT;
7222   }
7223
Expand/Collapse Item7224    off_t image_off, image_size;
If a minix file system is not present on the booted partition, image must be a "number:number" pair.  For example,

hd2a>image = 100:25

specifies that the OS image we wish to load begins at a sector offset of 100 (
image_off) within the booted partition and that the image's size is 25 sectors (image_size).

image_off and image_size are set in select_image() (see lines 7608-7609) and used in flat_vir2sec() (line 7239).
Expand/Collapse Item7225    u32_t (*vir2sec)(u32_t vsec);           /* Where is a sector on disk? */
vir2sec is a pointer to a function and is set to file_vir2sec (line 7227) if the booted partition has a minix file system (see line 7639) or flat_vir2sec (line 7239) if the booted partition does not have a minix file system (see line 7607).

A virtual sector number is a sector offset within an OS image file.

7226
Expand/Collapse Item7227    u32_t file_vir2sec(u32_t vsec)
file_vir2sec() is used if the booted partition has a minix file system and flat_vir2sec() (line 7239) is used if the booted partition does not have a minix file system.

If
vsec is a hole (a gap in the file), file_vir2sec() returns 0.

A virtual sector number is a sector offset within an OS image file.

7228   /* Translate a virtual sector number to an absolute disk sector. */
7229   {
7230          off_t blk;
7231
Expand/Collapse Item7232           if ((blk= r_vir2abs(vsec / RATIO )) == -1) {
The old and new minix file systems work with blocks rather than sectors.  The default block size for minix is 2 sectors (1 sector = 512 bytes; 1 block = 1K bytes).  For a discussion on block sizes, read section 5.3.3 in Operating Systems .

r_vir2abs(vsec) translates the virtual sector number vsec of the file curfil (which will be an OS image file) to an absolute disk block number and returns 0 for a hole (a gap in the file) and -1 if the block is beyond the end of the file.  r_stat() (see lines 7620 and 7637) sets curfil before file_vir2sec() is called.
Note that consecutive blocks within a file are not necessarily on consecutive physical blocks on the disk.  One of the primary responsibilities of a file system is keeping track of where all the blocks for a file are (read section 5.6.4 in Operating Systems ).

RATIO (=2) is the number of sectors per block.
7233                  errno= EIO;
7234                  return -1;
7235         }
Expand/Collapse Item7236           return blk == 0 ? 0 : lowsec + blk * RATIO+ vsec % RATIO;
r_vir2abs() returns a block number; the block number is here translated to a sector number.

If
vsec is a hole (a gap in the file), file_vir2sec() returns 0.
7237   }
7238
Expand/Collapse Item7239    u32_t  flat_vir2sec(u32_t vsec)
file_vir2sec() (line 7227) is used if the booted partition has a minix file system and flat_vir2sec() is used if the booted partition does not have a minix file system.

If
flat_vir2sec() is used, the OS image file begins at an offset of image_off within the partition and occupies size sectors (see lines 7608-7609).  The OS image file on disk must be in sequential order.
7240   /* Simply add an absolute sector offset to vsec. */
7241   {
7242          return lowsec + image_off+ vsec;
7243   }
7244
Expand/Collapse Item7245    char * get_sector(u32_t vsec)
Expand/Collapse Item7246    /* Read a sector "vsec" from the image into memory and return its address.
Expand/Collapse Item7247     * Return nil on error.  (This routine tries to read an entire track, so
Expand/Collapse Item7248     * the next request is usually satisfied from the track buffer.)
Expand/Collapse Item7249     */
get_sector() is called in get_clickshift() (see line 7301), get_segment() (see line 7327), and exec_image() (see line 7391).  Each time a disk read is necessary, get_sector() reads sector vsec plus every successive sector on the same track into buf[] (see line 7287).  If the next call to get_sector() asks for a sector in buf[], a disk read is not necessary; a pointer to the sector in memory is returned (see line 7270).

Here's a review of the relationship between sectors, tracks, and cylinders:



vsec is a sector offset within the file curfil (which should be an OS image file); sec (line 7251) is the corresponding absolute sector number (see line 7259).  If (*vir2sec)() returns 0, vsec is a hole (in other words, vsec is a gap in the file).  r_stat() (see lines 7620 and 7637) sets curfil before get_sector() is called.
7250   {
7251          u32_t sec;
7252          int r;
Expand/Collapse Item7253           static char buf[32 * SECTOR_SIZE];
Expand/Collapse Item7254           static size_t count;           /* Number of sectors in the buffer. */
Expand/Collapse Item7255           static u32_t bufsec;           /* First Sector now in the buffer. */
The static declaration specifies that variables (in this case, the array buf[], count, and bufsec) remain in existence after the function returns.  Since the cache must remain in existence from one invocation of get_sector() to the next, the static declaration for buf[], count, and bufsec is necessary.  Note that get_sector() returns an address within buf[] (see lines 7270 and 7293); if buf[] didn't stay in existence after get_sector() returned, this returned address would be meaningless (see lines 7327 and 7333).

The
static declaration has a different meaning for external variables.  The scope of an external static variable is only within the file in which the variable is declared.  Note that "external" variables are variables that are declared outside of all functions.
7256
7257          if (vsec == 0) count= 0;        /* First sector; initialize. */
7258
Expand/Collapse Item7259           if ((sec= (*vir2sec )(vsec)) == -1) return nil ;
vir2sec is a pointer to a function and is set to file_vir2sec (line 7227) if the booted partition has a minix file system (see line 7639) or flat_vir2sec (line 7239) if the booted partition does not have a minix file system (see line 7607).
7260
Expand/Collapse Item7261           if (sec == 0) {
If vsec is a hole (in other words, a gap in the file), set the first SECTOR_SIZE (=512) bytes of buf[] to 0 and return the address buf.

Note that setting
count to 0 marks the cache as invalid; the next call to get_sector() will be forced to make a disk read.
7262                  /* A hole. */
7263                  count= 0;
7264                  memset(buf, 0, SECTOR_SIZE);
7265                  return buf;
7266         }
7267
Expand/Collapse Item7268           /* Can we return a sector from the buffer? */
As an example, assume that the last call to get_sector() read absolute sectors 1230 through 1245 (bufsec=1230 and count =16).  If sec is a sector within this range, a disk read is not necessary.  A disk read will not be necessary until a request for a sector outside this range is made.

SECTOR_SIZE=512 (a sector is 512 bytes) and SECTOR_SHIFT =9. (1=SECTOR_SIZE>>SECTOR_SHIFT)
7269          if ((sec - bufsec) < count) {
7270                  return buf + ((size_t) (sec - bufsec) << SECTOR_SHIFT );
7271         }
7272
7273          /* Not in the buffer. */
7274          count= 0;
Expand/Collapse Item7275           bufsec= sec;
bufsec is the sector number of the first sector to be read into buf[].
7276
7277          /* Read a whole track if possible. */
Expand/Collapse Item7278           while (++count < 32 && !dev_boundary (bufsec + count)) {
int dev_boundary(u32_t sector) returns TRUE if sector is the first sector of a track.
7279                  vsec++;
Expand/Collapse Item7280                   if ((sec= (*vir2sec )(vsec)) == -1) break;
vir2sec is a pointer to a function and is set to file_vir2sec (line 7227) if the booted partition has a minix file system (see line 7639) or flat_vir2sec (line 7239) if the booted partition does not have a minix file system (see line 7607).
7281
7282                  /* Consecutive? */
7283                  if (sec != bufsec + count) break;
7284         }
7285
7286          /* Actually read the sectors. */
Expand/Collapse Item7287           if ((r= readsectors ( mon2abs (buf), bufsec, count)) != 0) {
int readsectors(u32_t bufaddr, u32_t sector, U8_t count) reads count bytes beginning at absolute sector address sector into absolute memory address bufaddr.
7288               readerr (bufsec, r);
7289                  count= 0;
7290                  errno= 0;
7291                  return nil ;
7292         }
7293          return buf;
7294   }
7295
Expand/Collapse Item7296    int  get_clickshift(u32_t ksec, struct image_header *hdr)
Expand/Collapse Item7297    /* Get the click shift and special flags from kernel text. */
get_clickshift() is called by exec_image() (see line 7414).

The kernel often uses "clicks" instead of bytes to describe the size of memory objects.  The click shift for the kernel is found at the beginning of the kernel code (i.e. text) segment (see line 06053 in the book) and is equal to 8.  A size in bytes is shifted 8 bits to the right to get the size in clicks.  For example, 0x0300 bytes is equal to 0x03 clicks.

get_clickshift() gets the first sector of the kernel text (i.e. code) and reads the click shift and the kernel flags (see lines 06053-06055 in the book).
7298   {
7299          char *textp;
7300
Expand/Collapse Item7301           if ((textp= get_sector (ksec)) == nil) return 0;
ksec is the second sector in the kernel.  The first sector contains the header and the second sector is the second sector of the code (and possibly the header again - see the comment for line 7303).
7302
Expand/Collapse Item7303           if (hdr->process.a_flags & A_PAL) textp+= hdr->process.a_hdrlen;
The A_PAL (Page ALigned) flag indicates that the header is present twice, alone in the process's first sector and in the second sector directly before the text.



If the header is also present directly before the text,
textp must skip past the header to the beginning of the code.
Expand/Collapse Item7304          click_shift = * (u16_t *) (textp + CLICK_OFFCLICK_OFF );
Expand/Collapse Item7305          k_flags = * (u16_t *) (textp + FLAGS_OFF FLAGS_OFF );
(u16_t *) (textp + CLICK_OFF) casts the value (textp + CLICK_OFF) as a pointer to a 2 byte (16 bit) value.  *(u16_t *) (textp + CLICK_OFF) is equal to this 2-byte value (in technical terms, this pointer is dereferenced).

CLICK_OFF (=2) and FLAGS_OFF (=4) are #define d on lines 7055-7056.
7306
7307          if (click_shift < HCLICK_SHIFT || click_shift 16) {
7308                  printf("%s click size is bad\n", hdr->name);
7309                  errno= 0;
7310                  return 0;
7311         }
7312
Expand/Collapse Item7313          click_size = 1 << click_shift ;
If click_shift=8 (the default), click_size=256.
7314
7315          return 1;
7316   }
7317
Expand/Collapse Item7318    int  get_segment(u32_t *vsec, long *size, u32_t *addr, u32_t limit)
Expand/Collapse Item7319    /* Read *size bytes starting at virtual sector *vsec to memory at *addr. */
get_segment() is called from exec_image() (see lines 7455 and 7478).  get_segment() gets a text (i.e. code) or data segment (or possibly both the text and data segments - see line 7453) beginning at sector vsec of length size bytes from disk and copies it to the absolute memory address addrget_segment() returns 1 (TRUE) if the call is successful and 0 (FALSE) if the call is unsuccessful.

vsec is a sector offset within the file curfil (which will be an OS image file).  r_stat() (see lines 7620 and 7637) sets curfil before get_segment() is called.

vsec, size, and addr are pointers; the values they reference are updated (see lines 7327, 7334-7335, and 7343-7344) so that the next call to get_segment() or get_sector() is ready for the next segment or sector in the OS image file.

limit is either the end of the unallocated space in lower memory (see lines 7371-7374) or the end of memory (see line 7511).
7320   {
7321          char *buf;
7322          size_t cnt, n;
7323
7324          cnt= 0;
Expand/Collapse Item7325           while (*size > 0) {
The while loop reads sectors (512 bytes) from the disk with get_sector() (see line 7327) but copies clicks (256 bytes) with raw_copy() (see line 7333) from buf to addr. *size (the value referenced by size) can become negative; raw_copy() copies clicks instead of sectors in order to prevent *size from becoming less than -256.
7326                  if (cnt == 0) {
Expand/Collapse Item7327                           if ((buf= get_sector ((*vsec)++)) == nil) return 0;
get_sector() (line 7245) returns a pointer to a buffer in memory containing the contents of vsecvsec is a sector offset within the file curfil (which will be an OS image file). r_stat() (see lines 7620 and 7637) sets curfil before get_segment() is called.
7328                          cnt= SECTOR_SIZE;
7329                   }
Expand/Collapse Item7330                  if (*addr + click_size > limit) { errno= ENOMEM; return 0; }
limit is either the end of the unallocated memory in lower memory (see lines 7371-7374) or the end of memory (see line 7511).
7331                  n= click_size ;
7332                  if (n > cnt) n= cnt;
Expand/Collapse Item7333                raw_copy(*addr, mon2abs(buf), n);
void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.
7334                  *addr+= n;
7335                  *size-= n;
7336                  buf+= n;
7337                  cnt-= n;
7338         }
7339
Expand/Collapse Item7340           /* Zero extend to a click. */
All segments should begin on click boundaries.
Expand/Collapse Item7341           n= align (*addr, click_size ) - *addr;
align(a,n) (line 7155) rounds a up to the next multiple of nn must be a multiple of 2.

Expand/Collapse Item7342           raw_clear (*addr, n);
void raw_clear(u32_t addr, u32_t count) (line 7133) clears count bytes beginning at absolute memory address addr.

7343          *addr+= n;
7344          *size-= n;
7345          return 1;
7346   }
7347
Expand/Collapse Item7348    void  exec_image(char *image)
Expand/Collapse Item7349    /* Get a Minix image into core, patch it up and execute. */
exec_image() is called by bootminix() (see line 7656) and does not return (unless something goes wrong) until the minix OS returns to the boot monitor.  The primary responsibilities of exec_image() are:

1)  load the processes from the file
image into memory.  The processes include the kernel, the memory manager (mm), the file system (fs), inet (if network support is enabled), and init.

2)  fill in
process[] (line 7044) for each process.

3)  jump to the minix OS by calling
minix() (see lines 7555-7556).

exec_image() is one of the most important functions in the boot sequence and, unfortunately, also one of the most difficult.  The difficulty is due to the process's flags and the kernel flags; the layout of the processes on disk is dependent on the process's flags and the addresses in memory where the processes are loaded is dependent on the kernel flags.

The kernel flags are
#defined on lines 7028-7035 and the process flags are #defined in a.out.h (see lines 01442-01446 in the book).
7350   {
7351          int i;
Expand/Collapse Item7352           struct image_header hdr;
struct image_header is declared in image.h.  Each process in the OS image begins with a header.  This header, hdr, is set on line 7393.
7353          char *buf;
Expand/Collapse Item7354           u32_t vsec, addr, limit, aout, n;
vsec is a sector offset within the file curfil (which should be an OS image file).  r_stat() (see lines 7620 and 7637) sets curfil before exec_image() is called.

Processes are copied segment by segment into memory. 
addr is the absolute memory address where the next segment should be copied.

limit is either the end of the unallocated memory in lower memory (see lines 7371-7374) or the end of memory (see line 7511).

aout is the address where the process headers are placed (see lines 7373-7377).
Expand/Collapse Item7355           struct process *procp;         /* Process under construction. */
struct process is declared on line 7044.

procp is used to fill in process[] (see lines 7458, 7463-7464, and 7506).
Expand/Collapse Item7356           long a_text, a_data, a_bss, a_stack;
The value of these variables depends on the process flags and the kernel flags.  To fully understand exec_image(), one must understand the role these variables play.
Expand/Collapse Item7357           int banner= 0;
The flag banner prevents the "banner" on lines 7425-7427 from being printed twice.
Expand/Collapse Item7358           long processor= a2l ( b_value ("processor"));
The environment variable processor is set in boot.c.

b_value() returns the value of the environment variable name or nil (0) if it is a function or it doesn't exist.

a2l() converts a string (like "-1023") to a long.

processor will be a long of value 86, 286, 386, 486, 586, or 686.
Expand/Collapse Item7359           char *params;
Expand/Collapse Item7360           size_t paramsize;
params2params() (see line 7542) packages the environment variables into a string and returns a pointer to the string in params. paramsize is the length of this string.  params and paramsize are arguments to minix() (see line 7555).
Expand/Collapse Item7361           u16_t mode;
Expand/Collapse Item7362           char *console;
mode and console are related by the expressions on lines 7545-7548; mode is used to set the video mode (see line 7549).
7363
7364          printf("\nLoading ");
Expand/Collapse Item7365          pretty_image (image);
pretty_image() (line 7066) "pretty prints" the name of an OS image file.  For example, pretty_image() converts the OS image file name "minix/1.6.16r10" to "Minix 1.6.16 revision 10".
7366          printf(".\n\n");
7367
7368          vsec= 0;                       /* Load this sector from image next. */
7369          addr= mem[0].base;             /* Into this memory block. */
7370          limit= mem[0].base + mem[0].size;
Expand/Collapse Item7371           if (limit > caddr ) limit= caddr ;
The array mem[]is the memory map (or list).  mem[] is initialized in boothead.s and is passed to the kernel as the environment variable memory.

caddr is declared in boot.h and initialized in boothead.s. caddr is the absolute memory address of the beginning of the boot monitor.

Since
limit is initially the end of unallocated space in lower memory, it should not be higher in memory than caddr.
7372
Expand/Collapse Item7373           /* Allocate and clear the area where the headers will be placed. */
Expand/Collapse Item7374           aout = (limit -= PROCESS_MAX * A_MINHDR);
Expand/Collapse Item7375
Expand/Collapse Item7376           /* Clear the area where the headers will be placed. */
Expand/Collapse Item7377          raw_clear (aout, PROCESS_MAX * A_MINHDR);
The headers are copied to this area on line 7422.

void raw_clear(u32_t address, u32_t count) (line 7133) clears count bytes beginning at absolute memory address address .

The memory layout is as shown in the figure:



PROCESS_MAX (=16) is #defined on line 7040. A_MINHDR (=32) is #defined in a.out.h (see line 01451 in the book).  Headers are either 32 bytes or 48 bytes; A_MINHDR is the size of the short header (see lines 01405-01424 in the book).
7378
7379          /* Read the many different processes: */
Expand/Collapse Item7380           for (i= 0; vsec < image_size ; i++) {
This for loop (which ends on line 7513) loads the kernel, the memory manager, the file system, the network server (if network support is enabled), and init from the OS image file image into memory.  This for loop loads one process at a time.  It begins by copying the process's header to memory address aout (lines 7390-7422).  Next, it copies the process's text and data segments (lines 7431-7478).  And finally, it zeroizes the space reserved for the bss and the stack (lines 7480-7503).  (Uninitialized global variables are stored in the bss.)
Expand/Collapse Item7381                   if (i == PROCESS_MAX ) {
PROCESS_MAX (=16) is #defined on line 7040.
7382                          printf("There are more then %d programs in %s\n",
7383                               PROCESS_MAX , image);
Expand/Collapse Item7384                           errno= 0;
If exec_image() reports the error (as is done on lines 7382-7383), errno is set to 0 to avoid further error messages (see lines 7667-7668).
7385                          return;
7386               }
Expand/Collapse Item7387                   procp= &process [i];
procp is used to fill in process[] (see lines 7458, 7463-7464, and 7506).
7388
Expand/Collapse Item7389                   /* Read header. */
This for loop is used to read the current process's header. If the process isn't an executable (see line 7395) or the process does not have the correct label (see line 7398), the process is skipped (see line 7401).
7390                  for (;;) {
Expand/Collapse Item7391                           if ((buf= get_sector (vsec++)) == nil) return;
get_sector() (line 7245) returns a pointer to a buffer in memory containing the contents of vsecvsec is a sector offset within the file curfil (which should be an OS image file - in this case, curfil is image).  r_stat() (see lines 7620 and 7637) sets curfil before exec_image() is called.  get_sector() returns 0 (nil) if vsec is a hole (in other words, a gap in the file).
7392
Expand/Collapse Item7393                           memcpy(&hdr, buf, sizeof(hdr));
void *memcpy(void *s1, void * s2, size_t n) copies n characters from s2 to s1, and returns s1.

The
sizeof operator returns the size of its argument (in this case, the size of hdr).
7394
Expand/Collapse Item7395                           if (BADMAG(hdr.process)) { errno= ENOEXEC; return; }
BADMAG() is #defined in a.out.h (see line 01428 in the book).  The first two bytes of a minix executable (see lines 01406-1407) must be the magic numbers A_MAGIC0 (0x01) and A_MAGIC1 (0x03).
7396
7397                          /* Check the optional label on the process. */
Expand/Collapse Item7398                           if (selected (hdr.name)) break;
selected() (line 7197) returns FALSE (0)  if hdr.name , a process in the OS image, should not be loaded into memory.

The kernel, memory manager (mm), file system (fs), network manager (inet), and init are compiled independently (which is one of the advantages of the micro kernel design).  installboot (using the -i option) "glues" the kernel, mm, fs, inet and init into an OS image.

Let's say you have 2 different versions of inet, a stable version and an experimental version, in an OS image.  You can choose at boot time which version is to be loaded; if you named the stable version
stable:inet and the experimental version test:inet and you wish to test out the experimental network manager:

hd2a>label test
hd2a>boot

The experimental version of the network manager is loaded into memory with the kernel and the other servers; the stable version is not loaded.
7399
7400                          /* Bad label, skip this process. */
Expand/Collapse Item7401                           vsec+= proc_size (&hdr);
proc_size() (line 7212) returns the size (in sectors) of the process.
7402               }
7403
Expand/Collapse Item7404                   /* Sanity check: an 8086 can't run a 386 kernel. */
A kernel compiled to run in protected mode can't run on an 8086 or an 80286.
7405                  if (hdr.process.a_cpu == A_I80386 && processor < 386) {
7406                          printf("You can't run a 386 kernel on this 80%ld\n",
7407                                  processor);
7408                          errno= 0;
7409                          return;
7410               }
7411
7412                  /* Get the click shift from the kernel text segment. */
7413                  if (i == KERNEL ) {
Expand/Collapse Item7414                           if (!get_clickshift (vsec, &hdr)) return;
get_clickshift() gets the first sector of the kernel text (i.e. code) and reads the click shift and the kernel flags (see lines 06053-06055 in the book).
Expand/Collapse Item7415                           addr= align (addr, click_size );
align(a,n) (line 7155) rounds a up to the next multiple of nn must be a multiple of 2.

The default value for
click_size is 256.
7416               }
7417
Expand/Collapse Item7418                   /* Save a copy of the header for the kernel, with a_syms
Expand/Collapse Item7419                    * misused as the address where the process is loaded at.
Expand/Collapse Item7420                    */
Expand/Collapse Item7421                   hdr.process.a_syms= addr;
a_syms is normally used to hold the size of the symbol table.  I believe that the symbol table is used for debugging.  If someone can give a detailed description of the symbol table, please send an e-mail to feedback@swartzbaugh.net.
Expand/Collapse Item7422                raw_copy (aout + i * A_MINHDR, mon2abs (&hdr.process), A_MINHDR);
void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.

A_MINHDR (=32) is #defined in a.out.h (see line 01451 in the book).  Headers are either 32 bytes or 48 bytes; A_MINHDR is the size of the short header (see lines 01405-01424 in the book).
7423
Expand/Collapse Item7424                   if (!banner) {
The banner is only printed once.
7425                          printf("    cs      ds   text    data     bss");
Expand/Collapse Item7426                           if (k_flags & K_CHMEM ) printf("    stack");
If the kernel flag K_CHMEM is not set, memory for the stack is not allocated to any of the processes (see lines 7435 and 7497-7503).
7427                       putch ('\n');
7428                          banner= 1;
7429               }
7430
Expand/Collapse Item7431                   /* Segment sizes. */
Expand/Collapse Item7432                   a_text= hdr.process.a_text;
Expand/Collapse Item7433                   a_data= hdr.process.a_data;
Expand/Collapse Item7434                   a_bss= hdr.process.a_bss;
The header has been inspected and copied to the appropriate place; the text (i.e. code) and data segments must now be copied from disk into memory and (optionally) memory must be reserved for the bss and the stack.

hdr.process.a_text, hdr.process.a_data, and hdr.process.a_bss are the respective sizes (in bytes) for the text, data and the bss.  The values of a_text, a_data, and a_bss, on the other hand, change to reflect the process's flags and the kernel flags.  To fully understand exec_image(), one must understand the role these variables play.

Have patience - lines 7431-7513 are difficult.
Expand/Collapse Item7435                   if (k_flags & K_CHMEM) {
If the kernel flag K_CHMEM is not set, memory for the stack is not allocated for any of the processes (see lines 7497-7503).

Expand/Collapse Item7436                           a_stack= hdr.process.a_total - a_data - a_bss;
Expand/Collapse Item7437                           if (!(hdr.process.a_flags & A_SEP)) a_stack-= a_text;
The meaning of hdr.process.a_total depends on whether the process's A_SEP flag is set.  If the A_SEP flag is set, hdr.process.a_total is the size (in bytes) of the data+bss+stack.  If the A_SEP flag is not set, hdr.process.a_total is the size (in bytes) of the data+bss+stack+text.  (Uninitialized global variables are stored in the bss.)

The
A_SEP (SEParate) flag indicates that the data segment begins on a sector boundary (see the A_PAL, A_SEP figure below).

7438               } else {
7439                          a_stack= 0;
7440               }
7441
7442                  /* Collect info about the process to be. */
7443                  procp->cs= addr;
7444
7445                  /* Process may be page aligned so that the text segment contains
7446                   * the header, or have an unmapped zero page against vaxisms.
7447                   */
7448                  procp->entry= hdr.process.a_entry;
Expand/Collapse Item7449                   if (hdr.process.a_flags & A_PAL) a_text+= hdr.process.a_hdrlen;
The A_PAL (Page ALigned) flag indicates that the header appears twice, alone in the process's first sector and in the second sector directly before the text (see the !A_PAL, !A_SEP figure and the A_PAL, !A_SEP figure).



a_text is the number of bytes that must be read from disk for the text segment (see line 7455).  If the header is in the second sector directly before the text segment, the header's length must be included in a_text.
Expand/Collapse Item7450                   if (hdr.process.a_flags & A_UZP) procp->cs-= click_size ;
I do not understand the A_UZP flag.  If anyone understands this flag and when this flag is set, please submit a comment to the site which will be displayed below.
Name: Christos KarayiannisEmail:  christos@kar.forthnet.grDate: Dec 10 2005 14:02:30 GMT
Subject:  A_UZP flag
Reponse: UZP stands for unmapped zero page. This is currently used only from Minix-VMD. When A_UZP is set and a nul pointer is dereferenced,
there is a trap, because the address range [0...1023] is not mapped in the
virtual memory system.
 
Respond to Christos Karayiannis's comment.
 
 
7451
7452                  /* Separate I&D: two segments.  Common I&D: only one. */
Expand/Collapse Item7453                   if (hdr.process.a_flags & A_SEP) {
The A_SEP (SEParate) flag indicates that the data segment begins on a sector boundary (see the A_PAL, A_SEP figure below).



The meaning of
a_data depends on whether the A_SEP flag is set.  If the A_SEP flag is set, a_data is the size (in bytes) of the data segment.  If the A_SEP flag is not set, a_data is the size (in bytes) of the data and text segments (plus the header if the A_PAL flag is set) combined (see line 7465).

The meaning of
ds depends on whether the A_SEP flag is set (see lines 7458 and 7464).

7454                          /* Read the text segment. */
Expand/Collapse Item7455                           if (!get_segment (&vsec, &a_text, &addr, limit)) return;
get_segment() (line 7318) gets a text segment (in this case) beginning at sector vsec of length size bytes from disk and copies it to the absolute memory address addrget_segment() returns 1 (TRUE) if the call is successful and 0 (FALSE) if the call is unsuccessful.

vsec is a sector offset within the file curfil (which will be the OS image file image).  r_stat() (see lines 7620 and 7637) sets curfil before exec_image() is called.

References to
vsec, size, and addr are passed to get_segment(); get_segment() updates the variables so that the next call to get_segment() (see line 7478) is ready for the next segment in the OS image file.

limit is either the end of the unallocated space in lower memory (see lines 7371-7374) or the end of memory (see line 7511).
7456
7457                          /* The data segment follows. */
Expand/Collapse Item7458                           procp->ds= addr;
addr was advanced by get_segment() (see line 7455).  The next call to get_segment() (see line 7478) copies the data segment to addr.
Expand/Collapse Item7459                           if (hdr.process.a_flags & A_UZP) procp->ds-= click_size ;
I do not understand the A_UZP flag.  If anyone understands this flag and when this flag is set, please submit a comment to the site which will be displayed below.
7460                          procp->data= addr;
7461               } else {
7462                          /* Add text to data to form one segment. */
7463                          procp->data= addr + a_text;
7464                          procp->ds= procp->cs;
7465                          a_data+= a_text;
7466               }
7467
7468                  printf("%06lx  %06lx %7ld %7ld %7ld",
7469                          procp->cs, procp->ds,
7470                          hdr.process.a_text, hdr.process.a_data,
7471                          hdr.process.a_bss
7472                  );
7473                  if (k_flags & K_CHMEM ) printf(" %8ld", a_stack);
7474
7475                  printf("  %s\n", hdr.name);
7476
Expand/Collapse Item7477                   /* Read the data segment. */
Expand/Collapse Item7478                   if (!get_segment (&vsec, &a_data, &addr, limit)) return;
get_segment() (line 7318) gets a data segment (or a text and data segment if the A_SEP flag is set for the process) beginning at sector vsec of length size bytes from disk and copies it to the absolute memory address addrget_segment() returns 1 (TRUE) if the call is successful and 0 (FALSE) if the call is unsuccessful.

vsec is a sector offset within the file curfil (which will be the OS image file image).  r_stat() (see lines 7620 and 7637) sets curfil before exec_image() is called.

References to
vsec, size, and addr are passed to get_segment(); get_segment() updates the variables so that the next call to get_segment() (see line 7478) is ready for the next process in the OS image file.

limit is either the end of the unallocated space in lower memory (see lines 7371-7374) or the end of memory (see line 7511).
7479
Expand/Collapse Item7480                   /* Make space for bss and stack unless... */
If the kernel flag K_CLAIM is set, space for the stack or the bss is not allocated to any process except the kernel.
7481                  if (i != KERNEL && (k_flags & K_CLAIM )) a_bss= a_stack= 0;
7482
Expand/Collapse Item7483                   /* Note that a_data may be negative now, but we can look at it
Expand/Collapse Item7484                    * as -a_data bss bytes.
Expand/Collapse Item7485                    */
In fact, a_data is most likely negative.  a_data is 0 if the data segment ends on a click boundary.

7486
Expand/Collapse Item7487                   /* Compute the number of bss clicks left. */
Lines 7488-7503 make space for the bss and stack and zeroize the bss.  The stack does not need to be zeroized since a value must be pushed onto the stack before it can be popped off.
Expand/Collapse Item7488                   a_bss+= a_data;
a_bss is also likely to become negative (it's initially positive - see line 7434) on this line or on line 7490.

Expand/Collapse Item7489                   n= align (a_bss, click_size );
align(a,n) rounds a up to the next multiple of n . n must be a multiple of 2.  If a is between -255 and 0,

align(a, click_size) = align(a, 256) = 0    (see second example in comments for line 7155)
Expand/Collapse Item7490                   a_bss-= n;
7491
7492                  /* Zero out bss. */
Expand/Collapse Item7493                   if (addr + n > limit) { errno= ENOMEM; return; }
limit is either the end of the unallocated space in lower memory (see lines 7371-7374) or the end of memory (see line 7511).
Expand/Collapse Item7494                raw_clear (addr, n);
void raw_clear(u32_t addr, u32_t count) (line 7133) clears count bytes beginning at absolute memory address addr.
7495                  addr+= n;
7496
7497                  /* And the number of stack clicks. */
Expand/Collapse Item7498                   a_stack+= a_bss;
Lines 7498-7500 are similar to lines 7488-7490.  a_stack is also likely to become negative on this line or line 7500.
7499                  n= align (a_stack, click_size);
7500                  a_stack-= n;
7501
7502                  /* Add space for the stack. */
Expand/Collapse Item7503                   addr+= n;
The stack ends on a click boundary.
7504
7505                  /* Process endpoint. */
7506                  procp->end= addr;
7507
Expand/Collapse Item7508                   if (i == 0 && (k_flags & K_HIGH )) {
If the kernel flag K_HIGH is set, all processes except for the kernel are loaded into extended memory (extended memory is memory above 1MB).

To be consistent with lines 7413 and 7481, this should have been written
(i == KERNEL ... ) rather than (i == 0 ... ).
7509                          /* Load the rest in extended memory. */
Expand/Collapse Item7510                           addr= mem [1].base;
Expand/Collapse Item7511                           limit= mem [1].base + mem[1].size;
The array mem[] is the memory map (or list).  mem[] is initialized in boothead.s and is passed to the kernel as the environment variable memory.
7512               }
7513         }
7514
Expand/Collapse Item7515           if ((n_procs = i) == 0) {
If the kernel, the memory manager (mm), the file system (fs), the network server (inet), and init are in the OS image file and all these processes are loaded into memory, n_procs will be 5.  If n_procs is 0, there is a serious problem.
7516                  printf("There are no programs in %s\n", image);
7517                  errno= 0;
7518                  return;
7519         }
7520
7521          /* Check the kernel magic number. */
Expand/Collapse Item7522           if ( get_word (process [KERNEL ].data + MAGIC_OFF ) != KERNEL_D_MAGIC) {
u16_t get_word(u32_t addr) returns the 2 byte (16 bit) value at absolute memory address addr.

The magic number (
KERNEL_D_MAGIC = 0x526F) is at the beginning (MAGIC_OFF = 0) of the kernel data segment (see line 06478 in the book).  If the magic number is not at this address, there is a serious problem.
7523                  printf("Kernel magic number is incorrect\n");
7524                  errno= 0;
7525                  return;
7526         }
7527
7528          /* Patch sizes, etc. into kernel data. */
Expand/Collapse Item7529          patch_sizes ();
patch_sizes() (line 7160) patches various sizes into various locations.  Most importantly, patch_sizes() patches the processes' sizes to the beginning of the kernel data (see lines 06471-06483 in the book).  If the kernel flag K_HDR flag is set, this patching is unnecessary and the kernel instead uses the headers to determine the sizes.
7530
7531   #if !DOS
Expand/Collapse Item7532   if (!(k_flags & K_MEML )) {
If a memory map is not passed in to the kernel, the kernel may expect the headers for the processes to be at memory address HEADERPOS.  These headers were copied to memory address aout (see line 7422).

HEADERPOS is #defined as 0x00600L in boot.h.   This address is above the real mode interrupt vectors (0x0000-0x03FF) and the bios data area (0x0400-0x4FF).
7533                  /* Copy the a.out headers to the old place. */
7534               raw_copy ( HEADERPOS , aout, PROCESS_MAX * A_MINHDR);
7535         }
7536   #endif
7537
7538          /* Run the trailer function just before starting Minix. */
Expand/Collapse Item7539            if (! run_trailer ()) { errno= 0; return; }
run_trailer() inserts trailer into the command chain and processes the commands in the command chain. trailer clears the screen.  run_trailer() returns TRUE (1) if there was no error.
7540
7541          /* Translate the boot parameters to what Minix likes best. */
Expand/Collapse Item7542           if ((params= params2params (&paramsize)) == nil ) return;
params2params() returns a string consisting of all the environment variables (but no environment functions).  The string will look something like the following:



params2params returns the length of params in paramsize . params and paramsize are used as arguments to minix() (see line 7555).
7543
7544          /* Set the video to the required mode. */
Expand/Collapse Item7545           if ((console= b_value ("console")) == nil || (mode= a2x (console)) == 0) {
If the user sets console to a valid hexadecimal number, the value for console is used instead of the value for chrome as the argument to set_mode() (see line 7549).

b_value() returns the value of the environment variable name or nil (0) if it is a function or it doesn't exist.

a2x() converts ascii strings in hexadecimal notation to unsigned 's.

int strcmp(char *s1, char *s2) compares string s1 to string s2 and returns 0 if the two strings are the same.
Expand/Collapse Item7546                   mode= strcmp( b_value ("chrome"), "color") == 0 ? COLOR_MODE :
Expand/Collapse Item7547                                                                MONO_MODE ;
COLOR_MODE (=0x03) and MONO_MODE (=0x07) are #define d in boot.h.
7548         }
Expand/Collapse Item7549          set_mode (mode);
void set_mode(unsigned mode) clears the screen and sets the video mode to mode.
7550
7551          /* Close the disk. */
Expand/Collapse Item7552           (void) dev_close ();
dev_close() does nothing under the BIOS.
7553
7554          /* Minix. */
Expand/Collapse Item7555          reboot_code = minix (process [KERNEL ].entry, process [KERNEL ].cs,
Expand/Collapse Item7556                                process [KERNEL ].ds, params, paramsize, aout);
minix() sets up the stack the way the kernel expects it, sets ds and es to the kernel's ds and es, and then jumps to the kernel.  If the kernel is minix-386 (as opposed to minix-86), minix() also switches from real mode to protected mode.

minix() returns when the OS returns to the boot monitor (for example, when the user issues the shutdown command).
7557          free(params);
7558
7559          /* Return from Minix; boot file system still around? */
Expand/Collapse Item7560           (void) dev_open();
dev_open() determines the number of heads and sectors of the device from which this code originated



and sets sectors (sectors/track) and secspcyl (sectors/cylinder) using these values.
Expand/Collapse Item7561          fsok = r_super () != 0;
r_super() returns TRUE if the file system on the device from which this code originated is a minix file system.  r_super() also fills in the variable super with the parameters of the file system.
Expand/Collapse Item7562           errno= 0;
There was either no error or the minix OS already reported the error (see lines 7667-7668).
7563   }
7564
Expand/Collapse Item7565    ino_t  latest_version(char *version, struct stat *stp)
Expand/Collapse Item7566    /* Recursively read the current directory, selecting the newest image on
Expand/Collapse Item7567     * the way up.  (One can't use r_stat while reading a directory.)
Expand/Collapse Item7568     */
The environment variable image is either the name of an OS image file or is the name of a directory.  If image is the name of a directory, latest_version() returns the inode of the file in the directory image that was last modified.  (File systems use inodes to describe files and directories - for a complete discussion of inodes, read section 5.3.1 - the I-nodes section - and section 5.6.4 in the book.)

latest_version() is called by select_image() (line 7589) in the block that spans lines 7622-7638.  This block is executed only if image is the name of a directory.

latest_version() is a recursive function, which means that latest_version() calls itself (see line 7576).  Recursive functions are often difficult to follow.  Here's an example that shows the control flow of latest_version().  In this example, there are 3 files in the current directory and the second file was the most recently modified.

1)  Determine inode number of first file in current directory.  We'll call it ino1.
2)  Call
latest_version().
    A)  Determine inode number of second file (ino2).
    B)  Call
latest_version().
        1)  Determine inode number of third file (ino3).
        2)  Call
latest_version() .
            A)  Returns immediately because there are no more files in the current directory
                    (see line 7574).  Sets
stp->mtime equal to 0.
        3)  Fill in
stat struct stp with ino3 values (see line 7578).  Since ino3 is the first file
                to be looked at, keep its
mtime value.
        4)  Fill in
version with ino3's name and set newest to ino3 (see lines 7581-7582).
        5)  Return ino3 since ino3 is the most recently modified file so far (since it's the first).
    C)  Fill in
stat struct stp with ino2 values (see line 7578).  Since ino2 was more recently
            modified than ino3, keep its
mtime value.
    D)  Fill in
version with ino2's name and set newest to ino2 (see lines 7581-7582).
    E)  Return ino2 since ino2 is the most recently modified file so far.
3)  Fill in
stat struct stp with ino1 values (see line 7578).  Since ino2 was more recently
        modified than ino1, keep the current
mtime value and ignore ino1's mtime value.
4)  Since ino2 was more recently modified than ino1, don't fill in
version with ino1's name.
5)  Return ino2 since ino2 is the most recently modified file.

struct stat is a subset of struct inode (see line 20510 in the book) and therefore describes a file or directory.  struct stat is declared in stat.h (see line 02300 in the book).
7569   {
Expand/Collapse Item7570           char name[NAME_MAX + 1];
ino_t r_readdir(char *name) sets name to the current entry (a file or subdirectory) in the current directory and returns the entry's inode number.  Each call to r_readdir() advances to the next entry in the current directory.
7571          ino_t ino, newest;
7572          time_t mtime;
7573
Expand/Collapse Item7574           if ((ino= r_readdir (name)) == 0) { stp->st_mtime=0; return 0; }
The st_mtime field in struct stat is the time (number of seconds since Jan. 1, 1970) of the last modification .  struct stat is declared in stat.h (line 02300 in the book).  A value of 0 is the least recent modification time possible - in other words, it's a long time ago.
7575
Expand/Collapse Item7576           newest= latest_version (version, stp);
Note that the variables version and stp are shared by all instances of latest_version().
Expand/Collapse Item7577           mtime= stp->st_mtime;
Expand/Collapse Item7578          r_stat (ino, stp);
stp->st_mtime holds the modification time of the most recently modified file.  This modification time is copied to mtime since r_stat() (see line 7578) overwrites struct stp with the information for the current file.  If mtime is more recent than stp->st_mtime, the modification time of the current file, mtime is copied back to stp->st_mtime (see line 7584).
7579
Expand/Collapse Item7580           if (S_ISREG(stp->st_mode) && stp->st_mtime > mtime) {
S_ISREG() returns TRUE if stp describes a regular file (in other words, stp doesn't describe a directory or a special file). S_ISREG() is #defined in stat.h (see line 02354 in the book).
7581                  newest= ino;
Expand/Collapse Item7582                   strcpy(version, name);
char *strcpy(char *s1, char *s2) copies string s2 to string s1, including '\0' and returns s1.
7583         }else {
7584                  stp->st_mtime= mtime;
7585         }
7586          return newest;
7587   }
7588
Expand/Collapse Item7589    char * select_image(char *image)
Expand/Collapse Item7590    /* Look image up on the filesystem, if it is a file then we're done, but
Expand/Collapse Item7591     * if its a directory then we want the newest file in that directory.  If
Expand/Collapse Item7592     * it doesn't exist at all, then see if it is 'number:number' and get the
Expand/Collapse Item7593     * image from that absolute offset off the disk.
Expand/Collapse Item7594     */
There are three acceptable values for the environment variable image :

1) 
image is the name of an OS image file.  This OS image file contains the OS image that we wish to load.

2) 
image is the name of a directory.  In this case, the most recently modified file in this directory contains the image that is loaded (see latest_version() on line 7565).

3) 
image is a "number:number" pair.  For example,

hd2a>image = 100:25

specifies that the OS image we wish to load begins at a sector offset of 100 (
image_off - see line 7608) within the partition and that the image's size is 25 sectors (image_size - see line 7609).  If a minix file system doesn't exist on the booted partition, this is the only option.
7595   {
7596          ino_t image_ino;
Expand/Collapse Item7597           truct stat st;
struct stat is a subset of struct inode (see line 20510 in the book) and therefore describes a file or directory.  struct stat is declared in stat.h (see line 02300 in the book).
7598
Expand/Collapse Item7599           image= strcpy(malloc((strlen(image) + 1 + NAME_MAX + 1)
Expand/Collapse Item7600                                                    * sizeof(char)), image);
In case image is the name of a directory, additional space for a file name (see lines 7622 and 7633) and a '/' (see line 7631) and a terminating zero is allocated.

char *strcpy(char *s1, char *s2) copies string s2 to string s1, including '\0' and returns s1.

size_t strlen(char *s) returns the length of the string s . strlen() is declared in <string.h>.

void *malloc(size_t size) allocates size bytes from the heap and returns a pointer to the first byte of the allocated memory. malloc() is declared in stdlib.h.  (The heap is the region between the stack and the bss.)

NAME_MAX (=14) is #defined in limits.h (see line 00171 in the book).
7601
Expand/Collapse Item7602            if (! fsok || (image_ino= r_lookup (ROOT_INO , image)) == 0) {
If a minix file system does not exist on the booted partition or the file or directory image cannot be found in the file system, the block spanning lines 7603-7618 is executed.

ino_t r_lookup(Ino_t cwd, char *path) returns the inode number of the file or directory path.  If path is a relative path then it is relative to the directory with inode number cwd.  (An absolute path begins with a '/'  and a relative path does not. /minix/minix_386_09282000 is an example of an absolute path and minix/minix_386_09282000 is an example of a relative path.)

ROOT_INO (=1) is the inode number of the root directory (' /'). ROOT_INO is #defined in rawfs.h.
7603                  char *size;
7604
Expand/Collapse Item7605                   if ( numprefix (image, &size) && *size++ == ':'
Expand/Collapse Item7606                                                   && numeric (size)) {
image must be a number:number pair.

numprefix() returns the address of the first nondigit from the string image in sizenumprefix() returns FALSE (0) if the first character is a nondigit.

numeric() returns TRUE if size is a number.
Expand/Collapse Item7607                        vir2sec = flat_vir2sec ;
vir2sec (line 7225) is a pointer set to the function flat_vir2sec() (line 7239) and is dereferenced in get_sector() (see lines7259 and 7280).
Expand/Collapse Item7608                        image_off ">image_off= a2l (image);
Expand/Collapse Item7609                        image_size = a2l(size);
The boot monitor image command:

hd2a>image = 100:25

specifies that the OS image we wish to load begins at a sector offset of 100 (
image_off) within the booted partition and that the image's size is 25 sectors (image_size).

image_off and image_size are used in flat_vir2sec() (line 7239).
Expand/Collapse Item7610                           strcpy(image, "Minix");
An image described by a number:number pair is given the generic name "Minix".

char *strcpy(char *s1,char *s2) copies string s2 to string s1, including '\0' and returns s1.
7611                          return image;
7612               }
7613                  if (!fsok )
7614                          printf("No image selected\n");
7615                  else
7616                          printf("Can't load %s: %s\n", image, unix_err (errno));
Expand/Collapse Item7617                    goto bail_out;
One doesn't see goto's too often.  The label bail_out is found on line 7642.
7618         }
7619
Expand/Collapse Item7620          r_stat (image_ino, &st);
void r_stat(ino_t file, struct stat *stp) returns information in *stp about the file whose inode number is file.
Expand/Collapse Item7621           if (!S_ISREG(st.st_mode)) {
S_ISREG() returns TRUE if st describes a regular file (in other words, st doesn't describe a directory or a special file). S_ISREG() is #defined in stat.h (see line 02354 in the book).
Expand/Collapse Item7622                   char *version= image + strlen(image);
If the block spanning lines 7622-7638 is executed, image is a directory (unless something has gone wrong).  Space was allocated on lines 7599-7600 for a '/' and a file name in case image is a directory.  version points to where the '/' and the file name are inserted (see lines 7631-7633).
Expand/Collapse Item7623                   char dots[NAME_MAX + 1];
The first two entries of every directory are the entries dot ". " (the directory itself) and dot dot ".." (the parent directory). r_readdir() (see lines 7629-7630) returns the name of these two entries in dots[].  These two entries are ignored.
7624
Expand/Collapse Item7625                   if (!S_ISDIR(st.st_mode)) {
S_ISDIR() returns TRUE if st describes a directory. S_ISDIR() is #defined in stat.h (see line 02355 in the book).
7626                          printf("%s: %s\n", image, unix_err (ENOTDIR));
7627                          goto bail_out;
7628               }
Expand/Collapse Item7629                   (void) r_readdir (dots);
Expand/Collapse Item7630                    (void) r_readdir (dots); /* "." & ".." */
The first two entries of every directory are the entries dot "." (the directory itself) and dot dot ".." (the parent directory). r_readdir() returns the name of these two entries in dots[] .  These two entries are ignored.

ino_t r_readdir(char *name) sets name to the current entry (a file or subdirectory) in the current directory and returns the entry's inode number.  Each call to r_readdir() advances to the next entry in the current directory.
7631                  *version++= '/';
7632                  *version= 0;
Expand/Collapse Item7633                   if ((image_ino= latest_version (version, &st)) == 0) {
ino_t latest_version(char *version, struct stat *stp) (line 7565) returns the inode number of the most recently modified file in the directory described by stplatest_version() also returns the name of this file in versionlatest_version() returns 0 if there are no regular files in the directory described by stp .
7634                          printf("There are no images in %s\n", image);
Expand/Collapse Item7635                           goto bail_out;
One doesn't see goto's too often.  The label bail_out is found on line 7642.
7636               }
Expand/Collapse Item7637                r_stat (image_ino, &st);
Before this call to r_stat(), st described a directory.  After this call to r_stat(), st describes the most recently modified file in this directory.  Note that r_stat() sets curfil, which is used by r_vir2abs() in file_vir2sec() (see line 7232). void r_stat(ino_t file, struct stat *stp) returns information in *stp about the file whose inode number is file.
7638         }
Expand/Collapse Item7639          vir2sec = file_vir2sec;
vir2sec (line 7225) is a pointer set to the function file_vir2sec() (line 7227) and is dereferenced in get_sector() (see lines7259 and 7280).
Expand/Collapse Item7640          image_size = (st.st_size + SECTOR_SIZE - 1) >> SECTOR_SHIFT ;
image_size is the size in sectors of image. image_size is rounded up to the next sector.

SECTOR_SIZE (=512) and SECTOR_SHIFT (=9) are #define d in boot.h.  A sector is 512 bytes.  The relationship between SECTOR_SIZE and SECTOR_SHIFT is:

SECTOR_SIZE == 1 << SECTOR_SHIFT
7641          return image;
7642   bail_out:
7643          free(image);
7644          return nil ;
7645   }
7646
Expand/Collapse Item7647    void  bootminix(void)
Expand/Collapse Item7648    /* Load Minix and run it.  (Given the size of this program it is surprising
Expand/Collapse Item7649     * that it ever gets to that.)
Expand/Collapse Item7650     */
bootminix() is invoked by the boot monitor boot command and (if options are passed to the kernel) by the boot monitor boot -optsvar command.  bootminix() is the high-level function that loads the OS image and calls minix(), which switches to protected mode (if the kernel was compiled for protected mode) and then jumps to the kernel.  bootminix() returns when the OS returns to the boot monitor (for example, when the user issues the shutdown command).
7651   {
7652          char *image;
7653
Expand/Collapse Item7654           if ((image= select_image ( b_value ("image"))) == nil ) return;
There are three acceptable values for the environment variable image:

1) 
image is the name of an OS image file.  This OS image file contains the OS image we wish to load.

2) 
image is the name of a directory.  In this case, the most recently modified file in this directory contains the image that is loaded (see latest_version() on line 7565).

3) 
image is a "number:number" pair.  For example,

hd2a>image = 100:25

specifies that the OS image we wish to load begins at a sector offset of 100 (
image_off - see line 7608) within the partition and that the image's size is 25 sectors (image_size - see line 7609).  If a minix file system doesn't exist on the booted partition, this is the only option.

If a minix file system exists on the booted partition and
image is not a "number:number" pair, select_image() (line 7589) sets curfil, a variable used in r_vir2abs(), to the inode number of the OS image file we wish to load.  If image is a "number:number" pair, select_image() sets image_off to the first number in the "number:number" pair and sets image_size to the second number.

b_value() returns the value of the environment variable image or nil (0) if it doesn't exist.
7655
Expand/Collapse Item7656          exec_image (image);
exec_image() does not return (unless something goes wrong) until the minix OS returns to the boot monitor.  The primary responsibilities of exec_image() are:

1)  load the processes from the file
image into memory.  The processes include the kernel, the memory manager (mm), the file system (fs), inet (if network support is enabled), and init.

2)  fill in
process[] (line 7044) for each process.

3)  jump to the minix OS by calling
minix() (see lines 7555-7556).
7657
Expand/Collapse Item7658           switch (errno) {
errno is a global variable declared in errno.h (see line 00230 in the book).
Expand/Collapse Item7659           case ENOEXEC:
If any of the processes in the OS image file are not executable, exec_image() (line 7348) sets errno to ENOEXEC (see line 7395).
7660                  printf("%s contains a bad program header\n", image);
7661                  break;
Expand/Collapse Item7662           case ENOMEM:
If there's not enough memory, exec_image() (line 7348) sets errno to ENOMEM (see lines 7330 and 7493).
7663                  printf("Not enough memory to load %s\n", image);
7664                  break;
Expand/Collapse Item7665           case EIO:
If the minix file system has trouble reading the OS image file from disk, file_vir2sec() sets errno to EIO (see line 7233).
7666                  printf("Unsuspected EOF on %s\n", image);
Expand/Collapse Item7667           case 0:
exec_image() (line 7348) reports many of the errors it discovers (see lines 7382-7384, 7406-7408, and 7523-7524).
7668                  /* No error or error already reported. */;
7669         }
7670          free(image);
7671   }
end of fileback up