boot.c
Skip to line: 5050 - 5100 - 5150 - 5200 - 5250 - 5300 - 5350 - 5400 - 5450 - 5500 - 5550 - 5600 - 5650 - 5700 - 5750 - 5800 - 5850 - 5900 - 5950 - 6000 - 6050 - 6100 - 6150 - 6200 - 6250 - 6300 - 6350 - 6400 - 6450 - 6500 - 6550 - 6600 - 6650 - 6700


Highlighted entries were made in the last day
Select a different time increment to highlight entries
Current GMT time: Jul 23 2008 21:46:10

If you have a comment for boot.c, please click here.
Name: Egdares FutchEmail:  efutch@gmail.comDate: Jun 21 2006 01:27:36 GMT
Subject:  Change the key used for the default boot option
Reponse: If you need to change the default '=' key to select the default Minix boot image in order to support NLS keyboards, edit the string in line 5834 and change the character directly in the display string.
 
Respond to Egdares Futch's comment.
 
 
Name: christosEmail:  Date: Oct 15 2005 12:41:33 GMT
Subject:  line 5354
Reponse: At the figure of line 5354, string2 should be swapped with string3
 
Respond to christos's comment.
 
 
5000   /*      boot.c - Load and start Minix.             Author: Kees J. Bot
5001    *                                                         27 Dec 1991
5002    *
5003    * Copyright 1998 Kees J. Bot, All rights reserved.
5004    * This package may be freely used and modified except that changes that
5005    * do not increase the functionality or that are incompatible with the
5006    * original may not be released to the public without permission from the
5007    * author.  Use of so called "C beautifiers" is explicitly prohibited.
Expand/Collapse Item5008     */
The key to understanding the boot sequence is understanding the function boot() (line 6605).  boot() is called from boothead.s ; it is the first function from boot.c to be called. boot() initializes the system with calls to initialize(), get_parameters(), and r_super() before processing commands from the bootparams sector (see lines 5848-5864) and commands typed in by the user.

Study the code from boot() before returning to the beginning.

5009
5010 char version[]=         "2.11";
5011
Expand/Collapse Item5012    #define BIOS    (!UNIX)         /* Either uses BIOS or UNIX syscalls. */
boot.c is used for both the boot monitor and edparams, a utility program.  Since the boot monitor runs independently of any operating system, it relies on bios function calls for its input/output (BIOS=TRUE, UNIX=FALSE).  edparams is an application that depends on the kernel; it relies on the operating system for its input/output (UNIX=TRUE, BIOS=FALSE).

In Makefile, edparams.c is compiled with the -D option.  (Note that edparams.c is a link to boot.c.)  The -DUNIX option sets UNIX equal to 1 (TRUE).  boot.c, on the other hand, is not compiled with this option (see line 0126 of Makefile). UNIX is therefore undefined and FALSE.

Different sections of code are parsed depending on whether UNIX or BIOS is TRUE.  For example, if BIOS is TRUE, lines 5030-5031 are parsed and lines 5034-5039 are discarded.  If UNIX is TRUE, lines 5034-5039 are parsed and lines 5030-5031 are discarded.  I do not cover sections of code that are specific to UNIX; for example, I do not cover lines 5143-5268.

5013
Expand/Collapse Item5014  #define nil 0
#define is a preprocessor command.  The preprocessor replaces all occurrences of the first string (in this case, "nil") with the second string (in this case, "0") before compilation.

The string "nil" is used to increase readability.

Expand/Collapse Item5015    #define _POSIX_SOURCE   1
Expand/Collapse Item5016    #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 the minix code in Operating Systems describe the _POSIX_SOURCE and _MINIX macros.

Expand/Collapse Item5017    #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 a default directory (typically /usr/include) and other directories specified by the -I option of the compiler (see line 5030).  If the filename is quoted (see line 5041), the preprocessor looks for the include file in the same directory that the source file is found.  (In this case, boot.c is the source file and is found in the /usr/src/boot directory.)

As an example, strlen() (see line 5613) is declared in the file string.h (see line 5022).  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 5130) is #defined in boot.h.  Since boot.h is quoted (see line 5044), the preprocessor searches for boot.h in the same directory as boot.c.  Indeed, both files are in the /usr/src/boot directory.

5018   #include <sys/types.h>
5019   #include <sys/stat.h>
5020   #include <stdlib.h>
5021   #include <limits.h>
5022   #include <string.h>
Expand/Collapse Item5023    #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" once) 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 08 2003 15:02:52 GMT
Subject:  Where is errno defined?
Reponse: errno is defined in src/lib/other/errno.c
(see also the comment of line 7013 in bootimage.c)
 
Respond to Christos Karayiannis's comment.
 
 
5024   #include <ibm/partition.h>
5025   #include <minix/config.h>
5026   #include <minix/type.h>
5027   #include <minix/const.h>
5028   #include <minix/minlib.h>
5029   #if BIOS
Expand/Collapse Item5030    #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.
5031   #include <kernel/type.h>
5032   #endif
Expand/Collapse Item5033    #if UNIX
I do not cover sections of code that are specific to UNIX (lines 5034-5040).
5034   #include <stdio.h>
5035   #include <time.h>
5036   #include <unistd.h>
5037   #include <fcntl.h>
5038   #include <signal.h>
5039   #include <termios.h>
5040   #endif
5041   #include "rawfs.h"
Expand/Collapse Item5042    #undef 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.  To accomplish this, the macro EXTERN is #defined as the empty string.  This prevents the EXTERN macro from being #defined as extern in boot.h when boot.h is #included in boot.c.  boot.h is also #included in bootimage.c.  Since EXTERN is not #defined (and is therefore undefined), EXTERN is replaced by extern in bootimage.c.  This mechanism ensures that memory for a variable is allocated only once.
5043   #define EXTERN  /* Empty */
5044   #include "boot.h"
5045
Expand/Collapse Item5046  #define arraysize(a)            (sizeof(a) / sizeof((a)[0]))
The sizeof operator returns the size of its argument (in this case, the array a and the first element of a).  For example, if a has 10 elements and each element is 2 bytes (a short):

arraysize(a) = sizeof(a)/sizeof(a[0]) = 20/2 = 10

Expand/Collapse Item5047    #define arraylimit(a)           ((a) + arraysize(a))
arraylimit(a) returns the address of the first byte after the last element of array a.

Let's say we have an array of shorts, short_array[], that has 10 elements.  As the above example shows, arraysize(short_array) = 10.  So if the address of the first byte of short_array[] was, for example, 0x1100000, then arraylimit(a) = a + arraysize(a) = 10 = 0x1100000 + 10 = 0x1100000 + 0xA = 0x110000A.  This is incorrect.

Pointer arithmetic can be confusing to the uninitiated.  The following program:

#include <stdio.h>

#define arraysize(a)    (sizeof(a)/sizeof((a)[0]))
#define arraylimit(a)   ((a) + arraysize(a))

int main()
{
  short short_array[5]= {1,2,3,4,5};
  int size_of_array;
  short *short_ptr;

  size_of_array= arraysize(short_array);
  printf("the size of short_array= %d\n", size_of_array);

  short_ptr= arraylimit(short_array);
  printf("the beginning of short_array is %p\n", short_array);
  printf("short_ptr= %p\n", short_ptr);

}

produces the output:

the size of short_array= 5
the beginning of short_array is 0xbffffc50
short_ptr= 0xbffffc5A

short_ptr equals 0xbffffc5A and not 0xbffffc55.  In other words, 0xbffffc50+5 (in this scenario) equals 0xbffffc5A and not 0xbffffc55.  Any value added to a pointer is first multiplied by the size of the type that the pointer references.  A short is 2 bytes and short_array has 5 elements.  So 0xbffffc50+2*5 = 0xbffffc50+10 = 0xbffffc50+0x0A = 0xbffffc5A.

Expand/Collapse Item5048    #define between(a, c, z)        ((unsigned) ((c) - (a)) <= ((z) - (a)))
between() returns TRUE if c is between a and z.
5049
5050   #if BIOS
Expand/Collapse Item5051    char *bios_err(int err)
The functions in boothead.s (for example, dev_open()) return an error code in ax if something goes wrong.  bios_err() converts this number to a readable string.

For example, on line 6131, the return value of dev_open() is stored in r.  If an error occurred, r will be nonzero.  On line 6133, bios_err() converts this number to a readable string so that printf() (on the next line:  line 6134) prints something meaningful.

5052   /* Translate BIOS error code to a readable string.  (This is a rare trait
5053    * known as error checking and reporting.  Take a good look at it, you
5054    * won't see it often.)
5055    */
5056   {
Expand/Collapse Item5057            static struct errlist {
The static declaration specifies that the variable (in this case, the array errlist[]) remains in existence after the function returns.  Since the contents of errlist[] don't change from one invocation of bios_err() to the next, it is preferable that the array remain in existence.  It saves the time for reinitialization that would otherwise be necessary.

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.

5058                   int     err;
5059                   char    *what;
5060           } errlist[] = {
5061   #if !DOS
5062            { 0x00, "No error" },
5063            { 0x01, "Invalid command" },
5064            { 0x02, "Address mark not found" },
5065            { 0x03, "Disk write-protected" },
5066            { 0x04, "Sector not found" },
5067            { 0x05, "Reset failed" },
5068            { 0x06, "Floppy disk removed" },
5069            { 0x07, "Bad parameter table" },
5070            { 0x08, "DMA overrun" },
5071            { 0x09, "DMA crossed 64 KB boundary" },
5072            { 0x0A, "Bad sector flag" },
5073            { 0x0B, "Bad track flag" },
5074            { 0x0C, "Media type not found" },
5075            { 0x0D, "Invalid number of sectors on format" },
5076            { 0x0E, "Control data address mark detected" },
5077            { 0x0F, "DMA arbitration level out of range" },
5078            { 0x10, "Uncorrectable CRC or ECC data error" },
5079            { 0x11, "ECC corrected data error" },
5080            { 0x20, "Controller failed" },
5081            { 0x40, "Seek failed" },
5082            { 0x80, "Disk timed-out" },
5083            { 0xAA, "Drive not ready" },
5084            { 0xBB, "Undefined error" },
5085            { 0xCC, "Write fault" },
5086            { 0xE0, "Status register error" },
5087            { 0xFF, "Sense operation failed" }
5088   #else /* DOS */
5089            { 0x00, "No error" },
5090            { 0x01, "Function number invalid" },
5091            { 0x02, "File not found" },
5092            { 0x03, "Path not found" },
5093            { 0x04, "Too many open files" },
5094            { 0x05, "Access denied" },
5095            { 0x06, "Invalid handle" },
5096            { 0x0C, "Access code invalid" },
5097   #endif /* DOS */
5098           };
Expand/Collapse Item5099            struct errlist *errp;
errp is a pointer to a struct errlist (see line 5060).
5100
Expand/Collapse Item5101            for (errp= errlist; errp < arraylimit(errlist); errp++) {
Expand/Collapse Item5102                    if (errp->err == err) return errp->what;
Expand/Collapse Item5103            }
Expand/Collapse Item5104            return "Unknown error";
The for loop compares the argument err with the err field of each element of the array errlist[].  When a match is found, the string what is returned.  If the for loop goes through the entire array errlist[] without finding a match, bios_err() returns the string "Unknown error".
5105   }
5106
5107   char *unix_err(int err)
5108   /* Translate the few errors rawfs can give. */
5109   {
5110           switch (err) {
5111           case ENOENT:    return "No such file or directory";
5112           case ENOTDIR:   return "Not a directory";
5113           default:        return "Unknown error";
5114           }
5115   }
5116
5117   void rwerr(char *rw, off_t sec, int err)
5118   {
5119           printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
5120                   rw, err, bios_err(err), sec);
5121   }
5122
Expand/Collapse Item5123    void readerr(off_t sec, int err)      { rwerr("Read", sec, err); }
readerr() and writerr() provide convenient interfaces to rwerr() and bios_err().

Notice the order of the functions bios_err(), rwerr(), and readerr()readerr() calls rwerr() which calls bios_err().  A function must be declared or defined before it can be called.  bios_err() is defined (lines 5051-5105) before rwerr() calls it (line 5120) and rwerr() is defined (lines 5117-5121) before readerr() calls it.  The need to carefully order the functions can be avoided if the functions are declared in a header file #included at the beginning of the file.  (In this case, boot.h would be the appropriate header file.)

5124   void writerr(off_t sec, int err)      { rwerr("Write", sec, err); }
5125
Expand/Collapse Item5126    void readblock(off_t blk, char *buf)
readblock() reads blk into the buffer buf.  Note that blk is not an absolute block on the hard drive; blk is the block offset from the beginning of the partition.  The first sector in the partition is lowsec.  (2 sectors = 1 block = 2*512 bytes = 1024 bytes)
5127   /* Read blocks for the rawfs package. */
5128   {
5129           int r;
Expand/Collapse Item5130            u32_t sec= lowsec + blk * RATIO;
sec is the absolute sector address of the first sector of blk.
5131
Expand/Collapse Item5132            if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO)) != 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.

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

RATIO = 2.  Both sectors of blk are read.

5133                readerr(sec, r); exit(1);
5134           }
5135   }
5136
Expand/Collapse Item5137    #define istty           (1)
Expand/Collapse Item5138    #define alarm(n)        (0)
Expand/Collapse Item5139    #define pause()         (0)
istty, alarm(n), and pause() expand to TRUE, FALSE, and FALSE, respectively.
5140
5141   #endif /* BIOS */
5142
Expand/Collapse Item5143    #if UNIX
I do not cover UNIX-specific sections of code (lines 5143-5268).  I may cover these sections at a later time.
5144
5145   /* The Minix boot block must start with these bytes: */
5146   char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
5147
5148   struct biosdev {
5149           char *name;             /* Name of device. */
5150           int device;             /* Device to edit parameters. */
5151   } bootdev;
5152
5153   struct termios termbuf;
5154   int istty;
5155
5156   void quit(int status)
5157   {
5158           if (istty) (void) tcsetattr(0, TCSANOW, &termbuf);
5159           exit(status);
5160   }
5161
5162   #define exit(s) quit(s)
5163
5164   void report(char *label)
5165   /* edparams: label: No such file or directory */
5166   {
5167           fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno));
5168   }
5169
5170   void fatal(char *label)
5171   {
5172           report(label);
5173           exit(1);
5174   }
5175
5176   void *alloc(void *m, size_t n)
5177   {
5178           m= m == nil ? malloc(n) : realloc(m, n);
5179           if (m == nil) fatal("");
5180           return m;
5181   }
5182
5183   #define malloc(n)       alloc(nil, n)
5184   #define realloc(m, n)   alloc(m, n)
5185
5186   #define mon2abs(addr)   ((void *) (addr))
5187
5188   int rwsectors(int rw, void *addr, u32_t sec, int nsec)
5189   {
5190           ssize_t r;
5191           size_t len= nsec * SECTOR_SIZE;
5192
5193           if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1)
5194                   return errno;
5195
5196           if (rw == 0) {
5197                   r= read(bootdev.device, (char *) addr, len);
5198           } else {
5199                   r= write(bootdev.device, (char *) addr, len);
5200           }
5201           if (r == -1) return errno;
5202           if (r != len) return EIO;
5203           return 0;
5204   }
5205
5206   #define readsectors(a, s, n)     rwsectors(0, (a), (s), (n))
5207   #define writesectors(a, s, n)    rwsectors(1, (a), (s), (n))
5208   #define readerr(sec, err)       (errno= (err), report(bootdev.name))
5209   #define writerr(sec, err)       (errno= (err), report(bootdev.name))
5210   #define putch(c)                putchar(c)
5211   #define unix_err(err)           strerror(err)
5212
5213   void readblock(off_t blk, char *buf)
5214   /* Read blocks for the rawfs package. */
5215   {
5216           errno= EIO;
5217           if (lseek(bootdev.device, blk * BLOCK_SIZE, SEEK_SET) == -1
5218                   || read(bootdev.device, buf, BLOCK_SIZE) != BLOCK_SIZE)
5219           {
5220                   fatal(bootdev.name);
5221           }
5222   }
5223
5224   int trapsig;
5225
5226   void trap(int sig)
5227   {
5228           trapsig= sig;
5229           signal(sig, trap);
5230   }
5231
5232   int escape(void)
5233   {
5234           if (trapsig == SIGINT) {
5235                   trapsig= 0;
5236                   return 1;
5237           }
5238           return 0;
5239   }
5240
5241   int getch(void)
5242   {
5243           char c;
5244
5245           fflush(stdout);
5246
5247           switch (read(0, &c, 1)) {
5248           case -1:
5249                   if (errno != EINTR) fatal("");
5250                   return(ESC);
5251           case 0:
5252                   if (istty) putch('\n');
5253                   exit(0);
5254           default:
5255                   if (istty && c == termbuf.c_cc[VEOF]) {
5256                           putch('\n');
5257                           exit(0);
5258                   }
5259                   return c & 0xFF;
5260           }
5261   }
5262
5263   #define get_tick()              ((u32_t) time(nil))
5264   #define clear_screen()          printf("[clear]");
5265   #define boot_device(device)     printf("[boot %s]\n", device);
5266   #define bootminix()             (run_trailer() && printf("[boot]\n"))
5267
5268   #endif /* UNIX */
5269
5270   char *readline(void)
5271   /* Read a line including a newline with echoing. */
5272   {
5273           char *line;
5274           size_t i, z;
5275           int c;
5276
5277           i= 0;
5278           z= 20;
Expand/Collapse Item5279            line= 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.)

Space for 20 char's (20 bytes) is initially allocated.  If this is used up, another 20 bytes are allocated.  If the 40 bytes are used up, 40 additional bytes are allocated.  Each reallocation of memory doubles the total amount of memory allocated.  See lines 5298-5299.

5280