Minix1.5/kernel/cstart.c
/* This file contains the C startup code for Minix on Intel processors.
* It cooperates with start.x to set up a good environment for main().
*
* The code must run in 16-bit mode (and be compiled with a 16-bit
* compiler!) even for a 32-bit kernel.
* So care must be taken to avoid accessing data structures (such as the
* process table) which depend on type sizes, and to avoid calling functions
* except those in the companion files start.x and protect.c, and the stage
* 1 assembler hooks.
* Also, variables beyond 64K must not be accessed - this is guaranteed
* by keeping the kernel small.
* This is not so easy when the 32-bit compiler does not support separate
* I&D, since the kernel is already larger than 64K.
* The order of the objects in the makefile is chosen so all variables
* accessed from here are early in the list, and the linker is relied on
* not to alter the order.
* It might be better to separately-compile start.x, cstart.c and protect.c,
* and pass the results to main() in a big structure.
*/
#include "kernel.h"
#include <minix/boot.h>
/* Magic BIOS addresses. */
#define BIOS_CSEG 0xF000 /* segment of BIOS code */
#define BIOS_DSEG 0x0040 /* segment of BIOS data */
# define BIOS_CURSOR 0x0060 /* offset to cursor type word */
#define MACHINE_TYPE_SEG 0xFFFF /* segment of machine type word */
# define MACHINE_TYPE_OFF 0x000E /* offset of machine type word */
# define PC_AT 0xFC /* code in first byte for PC-AT */
# define PS 0xFA /* code in first byte for PS/2 Model 30 */
# define PS_386 0xF8 /* code in first byte for PS/2 386 70 & 80 */
# define PS_50 0x04 /* code in second byte for PS/2 Model 50 */
# define PS_50A 0xBA /* code in second byte on some Model 50s */
# define PS_50Z 0xE9 /* code in second byte for Model 50Z */
# define PS_60 0x05 /* code in second byte for PS/2 Model 60 */
PRIVATE char k_environ[128]; /* environment strings passed by loader */
FORWARD void db_init();
FORWARD int k_atoi();
FORWARD char *k_getenv();
FORWARD void rel_vec();
/*==========================================================================*
* cstart *
*==========================================================================*/
PUBLIC void cstart(ax, bx, cx, dx, si, di, cs, ds)
u16_t ax; /* boot code (registers from boot loader) */
u16_t bx; /* scan code */
u16_t cx; /* amount of boot parameters in bytes */
u16_t dx; /* not used */
u16_t si; /* offset of boot parameters in loader */
u16_t di; /* segment of boot parameters in loader */
u16_t cs; /* real mode kernel code segment */
u16_t ds; /* real mode kernel data segment */
{
/* Perform initializations which must be done in real mode. */
register u16_t *bootp;
register char *envp;
unsigned machine_magic;
unsigned mach_submagic;
/* Record where the kernel is. */
code_base = hclick_to_physb(cs);
data_base = hclick_to_physb(ds);
/* Copy the boot parameters, if any, to kernel memory. */
if (ax == 0) {
/* New boot loader (ax == bx == scan code for old one). */
if (cx > sizeof boot_parameters) cx = sizeof boot_parameters;
cx /= 2; /* word count */
for (bootp = (u16_t *) &boot_parameters; cx != 0; cx--, si += 2)
*bootp++ = get_word(di, si);
bx = boot_parameters.bp_scancode;
}
else if (ax == 0x100) {
/* Newer boot loader passes environment string. */
if (cx > sizeof k_environ - 2) cx = sizeof k_environ - 2;
cx /= 2; /* word count */
for (bootp = (u16_t *) &k_environ[0]; cx != 0; cx--, si += 2)
*bootp++ = get_word(di, si);
/* Just convert environment to boot parameters for now. */
envp = k_getenv("rootdev");
if (envp != NIL_PTR) boot_parameters.bp_rootdev = k_atoi(envp);
envp = k_getenv("ramimagedev");
if (envp != NIL_PTR) boot_parameters.bp_ramimagedev = k_atoi(envp);
envp = k_getenv("ramsize");
if (envp != NIL_PTR) boot_parameters.bp_ramsize = k_atoi(envp);
envp = k_getenv("scancode");
if (envp != NIL_PTR) bx = boot_parameters.bp_scancode = k_atoi(envp);
envp = k_getenv("processor");
if (envp != NIL_PTR) boot_parameters.bp_processor = k_atoi(envp);
}
scan_code = bx;
/* Get information from the BIOS. */
color = get_chrome();
ega = get_ega();
ext_memsize = get_ext_memsize();
low_memsize = get_low_memsize();
/* Determine machine type. */
processor = get_processor(); /* 86, 186, 286 or 386 */
machine_magic = get_word(MACHINE_TYPE_SEG, MACHINE_TYPE_OFF);
mach_submagic = (machine_magic >> 8) & BYTE;
machine_magic &= BYTE;
if (machine_magic == PC_AT) {
pc_at = TRUE;
/* could be a PS/2 Model 50 or 60 -- check submodel byte */
if (mach_submagic == PS_50 || mach_submagic == PS_60) ps_mca = TRUE;
if (mach_submagic == PS_50A || mach_submagic == PS_50Z) ps_mca = TRUE;
} else if (machine_magic == PS_386)
pc_at = ps_mca = TRUE;
else if (machine_magic == PS)
ps = TRUE;
/* Decide if mode is protected. */
if (processor >= 286 && boot_parameters.bp_processor >= 286 && !using_bios) {
protected_mode = TRUE;
}
boot_parameters.bp_processor = protected_mode; /* FS needs to know */
/* Prepare for relocation of the vector table. It may contain pointers into
* itself. Fix up only the ones used.
*/
rel_vec(WINI_0_PARM_VEC * 4, ds);
rel_vec(WINI_1_PARM_VEC * 4, ds);
/* Initialize debugger (requires 'protected_mode' to be initialized). */
db_init();
/* Call stage 1 assembler hooks to begin machine/mode specific inits. */
mpx_1hook();
klib_1hook();
/* Call main() and never return if not protected mode. */
if (!protected_mode) main();
/* Initialize protected mode (requires 'break_vector' and other globals). */
prot_init();
/* Return to assembler code to switch modes. */
}
/*==========================================================================*
* db_init *
*==========================================================================*/
PRIVATE void db_init()
{
/* Initialize vectors for external debugger. */
break_vector.offset = get_word(VEC_TABLE_SEG, BREAKPOINT_VECTOR * 4);
break_vector.selector = get_word(VEC_TABLE_SEG, BREAKPOINT_VECTOR * 4 + 2);
sstep_vector.offset = get_word(VEC_TABLE_SEG, DEBUG_VECTOR * 4);
sstep_vector.selector = get_word(VEC_TABLE_SEG, DEBUG_VECTOR * 4 + 2);
/* No debugger if the breakpoint vector points into the BIOS. */
if ((u16_t) break_vector.selector >= BIOS_CSEG) return;
/* Debugger vectors for protected mode are by convention stored as 16-bit
* offsets in the debugger code segment, just before the corresponding real
* mode entry points.
*/
if (protected_mode) {
break_vector.offset = get_word((u16_t) break_vector.selector,
(u16_t) break_vector.offset - 2);
sstep_vector.offset = get_word((u16_t) sstep_vector.selector,
(u16_t) sstep_vector.offset - 2);
/* Different code segments are not supported. */
if ((u16_t) break_vector.selector != (u16_t) sstep_vector.selector)
return;
}
/* Enable debugger. */
db_exists = TRUE;
db_enabled = TRUE;
/* Tell debugger about Minix's normal cursor shape via the BIOS. It would
* be nice to tell it the variable video parameters, but too hard. At least
* the others get refreshed by the console driver. Blame the 6845's
* read-only registers.
*/
put_word(BIOS_DSEG, BIOS_CURSOR, CURSOR_SHAPE);
}
/*==========================================================================*
* k_atoi *
*==========================================================================*/
PRIVATE int k_atoi(s)
register char *s;
{
/* Convert string to integer - kernel version of atoi to make sure it is in
* 16-bit mode and to avoid bloat from isspace().
*/
register int total = 0;
register unsigned digit;
register minus = 0;
while (*s == ' ' || *s == '\t') s++;
if (*s == '-') {
s++;
minus = 1;
}
while ((digit = *s++ - '0') < 10) {
total *= 10;
total += digit;
}
return(minus ? -total : total);
}
/*==========================================================================*
* k_getenv *
*==========================================================================*/
PRIVATE char *k_getenv(name)
char *name;
{
/* Get environment value - kernel version of getenv to avoid setting up the
* usual environment array.
*/
register char *namep;
register char *envp;
for (envp = k_environ; *envp != 0;) {
for (namep = name; *namep != 0 && *namep++ == *envp++;)
;
if (*namep == '\0' && *envp == '=') return(envp + 1);
while (*envp++ != 0)
;
}
return(NIL_PTR);
}
/*==========================================================================*
* rel_vec *
*==========================================================================*/
PRIVATE void rel_vec(vec4, ds)
unsigned vec4; /* vector to be relocated (times 4) */
u16_t ds; /* real mode kernel data segment */
{
/* If the vector 'vec4' points into the vector table, relocate it to the copy
* of the vector table.
*/
phys_bytes address;
unsigned off;
unsigned seg;
off = get_word(VEC_TABLE_SEG, vec4);
seg = get_word(VEC_TABLE_SEG, vec4 + 2);
address = hclick_to_physb(seg) + off;
if (address != 0 && address < VECTOR_BYTES) {
put_word(VEC_TABLE_SEG, vec4, off + (unsigned) vec_table);
put_word(VEC_TABLE_SEG, vec4 + 2, seg + ds);
}
}