minix/kernel/arch/i386/system.c
Arun Thomas 1f9ce647cf Move archtypes.h, fpu.h, and stackframe.h
Move archtypes.h to include/ dir, since several servers require it. Move
fpu.h and stackframe.h to arch-specific header directory. Make source
files and makefiles aware of the new header locations.
2010-03-09 09:41:14 +00:00

479 lines
10 KiB
C

/* system dependent functions for use inside the whole kernel. */
#include "../../kernel.h"
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <machine/cmos.h>
#include <machine/bios.h>
#include <minix/portio.h>
#include <minix/cpufeature.h>
#include <a.out.h>
#include "archconst.h"
#include "proto.h"
#include "../../proc.h"
#include "../../debug.h"
#ifdef CONFIG_APIC
#include "apic.h"
#endif
/* set MP and NE flags to handle FPU exceptions in native mode. */
#define CR0_MP_NE 0x0022
/* set CR4.OSFXSR[bit 9] if FXSR is supported. */
#define CR4_OSFXSR (1L<<9)
/* set OSXMMEXCPT[bit 10] if we provide #XM handler. */
#define CR4_OSXMMEXCPT (1L<<10)
FORWARD _PROTOTYPE( void ser_debug, (int c));
PUBLIC void arch_monitor(void)
{
monitor();
}
PUBLIC int cpu_has_tsc;
PUBLIC void arch_shutdown(int how)
{
/* Mask all interrupts, including the clock. */
outb( INT_CTLMASK, ~0);
if(minix_panicing) {
/* We're panicing? Then retrieve and decode currently
* loaded segment selectors.
*/
printseg("cs: ", 1, proc_ptr, read_cs());
printseg("ds: ", 0, proc_ptr, read_ds());
if(read_ds() != read_ss()) {
printseg("ss: ", 0, NULL, read_ss());
}
}
if(how != RBT_RESET) {
/* return to boot monitor */
outb( INT_CTLMASK, 0);
outb( INT2_CTLMASK, 0);
/* Return to the boot monitor. Set
* the program if not already done.
*/
if (how != RBT_MONITOR)
arch_set_params("", 1);
if(minix_panicing) {
int source, dest;
static char mybuffer[sizeof(params_buffer)];
char *lead = "echo \\n*** kernel messages:\\n";
int leadlen = strlen(lead);
strcpy(mybuffer, lead);
#define DECSOURCE source = (source - 1 + _KMESS_BUF_SIZE) % _KMESS_BUF_SIZE
dest = sizeof(mybuffer)-1;
mybuffer[dest--] = '\0';
source = kmess.km_next;
DECSOURCE;
while(dest >= leadlen) {
char c = kmess.km_buf[source];
if(c == '\n') {
mybuffer[dest--] = 'n';
mybuffer[dest] = '\\';
} else if(isprint(c) &&
c != '\'' && c != '"' &&
c != '\\' && c != ';') {
mybuffer[dest] = c;
} else mybuffer[dest] = ' ';
DECSOURCE;
dest--;
}
arch_set_params(mybuffer, strlen(mybuffer)+1);
}
arch_monitor();
} else {
/* Reset the system by forcing a processor shutdown. First stop
* the BIOS memory test by setting a soft reset flag.
*/
u16_t magic = STOP_MEM_CHECK;
phys_copy(vir2phys(&magic), SOFT_RESET_FLAG_ADDR,
SOFT_RESET_FLAG_SIZE);
reset();
}
}
/* address of a.out headers, set in mpx386.s */
phys_bytes aout;
PUBLIC void arch_get_aout_headers(int i, struct exec *h)
{
/* The bootstrap loader created an array of the a.out headers at
* absolute address 'aout'. Get one element to h.
*/
phys_copy(aout + i * A_MINHDR, vir2phys(h), (phys_bytes) A_MINHDR);
}
PRIVATE void tss_init(struct tss_s * tss, void * kernel_stack, unsigned cpu)
{
/*
* make space for process pointer and cpu id and point to the first
* usable word
*/
tss->sp0 = ((unsigned) kernel_stack) - 2 * sizeof(void *);
tss->ss0 = DS_SELECTOR;
/*
* set the cpu id at the top of the stack so we know on which cpu is
* this stak in use when we trap to kernel
*/
*((reg_t *)(tss->sp0 + 1 * sizeof(reg_t))) = cpu;
}
PUBLIC void arch_init(void)
{
unsigned short cw, sw;
fninit();
sw = fnstsw();
fnstcw(&cw);
if((sw & 0xff) == 0 &&
(cw & 0x103f) == 0x3f) {
/* We have some sort of FPU, but don't check exact model.
* Set CR0_NE and CR0_MP to handle fpu exceptions
* in native mode. */
write_cr0(read_cr0() | CR0_MP_NE);
fpu_presence = 1;
if(_cpufeature(_CPUF_I386_FXSR)) {
register struct proc *rp;
phys_bytes aligned_fp_area;
/* Enable FXSR feature usage. */
write_cr4(read_cr4() | CR4_OSFXSR | CR4_OSXMMEXCPT);
osfxsr_feature = 1;
for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; ++rp) {
/* FXSR requires 16-byte alignment of memory
* image, but unfortunately some old tools
* (probably linker) ignores ".balign 16"
* applied to our memory image.
* Thus we have to do manual alignment.
*/
aligned_fp_area =
(phys_bytes) &rp->p_fpu_state.fpu_image;
if(aligned_fp_area % FPUALIGN) {
aligned_fp_area += FPUALIGN -
(aligned_fp_area % FPUALIGN);
}
rp->p_fpu_state.fpu_save_area_p =
(void *) aligned_fp_area;
}
} else {
osfxsr_feature = 0;
}
} else {
/* No FPU presents. */
fpu_presence = 0;
osfxsr_feature = 0;
return;
}
#ifdef CONFIG_APIC
/*
* this is setting kernel segments to cover most of the phys memory. The
* value is high enough to reach local APIC nad IOAPICs before paging is
* turned on.
*/
prot_set_kern_seg_limit(0xfff00000);
reload_ds();
#endif
idt_init();
tss_init(&tss, &k_boot_stktop, 0);
#if defined(CONFIG_APIC) && !defined(CONFIG_SMP)
if (config_no_apic) {
BOOT_VERBOSE(printf("APIC disabled, using legacy PIC\n"));
}
else if (!apic_single_cpu_init()) {
BOOT_VERBOSE(printf("APIC not present, using legacy PIC\n"));
}
#endif
}
#define COM1_BASE 0x3F8
#define COM1_THR (COM1_BASE + 0)
#define COM1_RBR (COM1_BASE + 0)
#define COM1_LSR (COM1_BASE + 5)
#define LSR_DR 0x01
#define LSR_THRE 0x20
PUBLIC void ser_putc(char c)
{
int i;
int lsr, thr;
lsr= COM1_LSR;
thr= COM1_THR;
for (i= 0; i<100000; i++)
{
if (inb( lsr) & LSR_THRE)
break;
}
outb( thr, c);
}
/*===========================================================================*
* do_ser_debug *
*===========================================================================*/
PUBLIC void do_ser_debug()
{
u8_t c, lsr;
lsr= inb(COM1_LSR);
if (!(lsr & LSR_DR))
return;
c = inb(COM1_RBR);
ser_debug(c);
}
PRIVATE void ser_dump_queues(void)
{
int q;
for(q = 0; q < NR_SCHED_QUEUES; q++) {
struct proc *p;
if(rdy_head[q])
printf("%2d: ", q);
for(p = rdy_head[q]; p; p = p->p_nextready) {
printf("%s / %d ", p->p_name, p->p_endpoint);
}
printf("\n");
}
}
PRIVATE void ser_dump_segs(void)
{
struct proc *pp;
for (pp= BEG_PROC_ADDR; pp < END_PROC_ADDR; pp++)
{
if (isemptyp(pp))
continue;
printf("%d: %s ep %d\n", proc_nr(pp), pp->p_name, pp->p_endpoint);
printseg("cs: ", 1, pp, pp->p_reg.cs);
printseg("ds: ", 0, pp, pp->p_reg.ds);
if(pp->p_reg.ss != pp->p_reg.ds) {
printseg("ss: ", 0, pp, pp->p_reg.ss);
}
}
}
PRIVATE void ser_debug(int c)
{
serial_debug_active = 1;
switch(c)
{
case 'Q':
minix_shutdown(NULL);
NOT_REACHABLE;
case '1':
ser_dump_proc();
break;
case '2':
ser_dump_queues();
break;
case '3':
ser_dump_segs();
break;
#if DEBUG_TRACE
#define TOGGLECASE(ch, flag) \
case ch: { \
if(verboseflags & flag) { \
verboseflags &= ~flag; \
printf("%s disabled\n", #flag); \
} else { \
verboseflags |= flag; \
printf("%s enabled\n", #flag); \
} \
break; \
}
TOGGLECASE('8', VF_SCHEDULING)
TOGGLECASE('9', VF_PICKPROC)
#endif
}
serial_debug_active = 0;
}
PRIVATE void printslot(struct proc *pp, int level)
{
struct proc *depproc = NULL;
endpoint_t dep;
#define COL { int i; for(i = 0; i < level; i++) printf("> "); }
if(level >= NR_PROCS) {
printf("loop??\n");
return;
}
COL
printf("%d: %s %d prio %d/%d time %d/%d cycles 0x%x%08x cr3 0x%lx rts %s misc %s",
proc_nr(pp), pp->p_name, pp->p_endpoint,
pp->p_priority, pp->p_max_priority, pp->p_user_time,
pp->p_sys_time, pp->p_cycles.hi, pp->p_cycles.lo, pp->p_seg.p_cr3,
rtsflagstr(pp->p_rts_flags), miscflagstr(pp->p_misc_flags));
if((dep = P_BLOCKEDON(pp)) != NONE) {
printf(" blocked on: ");
if(dep == ANY) {
printf(" ANY\n");
} else {
int procno;
if(!isokendpt(dep, &procno)) {
printf(" ??? %d\n", dep);
} else {
depproc = proc_addr(procno);
if(isemptyp(depproc)) {
printf(" empty slot %d???\n", procno);
depproc = NULL;
} else {
printf(" %s\n", depproc->p_name);
}
}
}
} else {
printf("\n");
}
COL
proc_stacktrace(pp);
if(depproc)
printslot(depproc, level+1);
}
PUBLIC void ser_dump_proc()
{
struct proc *pp;
for (pp= BEG_PROC_ADDR; pp < END_PROC_ADDR; pp++)
{
if (isemptyp(pp))
continue;
printslot(pp, 0);
}
}
#if SPROFILE
PUBLIC int arch_init_profile_clock(u32_t freq)
{
int r;
/* Set CMOS timer frequency. */
outb(RTC_INDEX, RTC_REG_A);
outb(RTC_IO, RTC_A_DV_OK | freq);
/* Enable CMOS timer interrupts. */
outb(RTC_INDEX, RTC_REG_B);
r = inb(RTC_IO);
outb(RTC_INDEX, RTC_REG_B);
outb(RTC_IO, r | RTC_B_PIE);
/* Mandatory read of CMOS register to enable timer interrupts. */
outb(RTC_INDEX, RTC_REG_C);
inb(RTC_IO);
return CMOS_CLOCK_IRQ;
}
PUBLIC void arch_stop_profile_clock(void)
{
int r;
/* Disable CMOS timer interrupts. */
outb(RTC_INDEX, RTC_REG_B);
r = inb(RTC_IO);
outb(RTC_INDEX, RTC_REG_B);
outb(RTC_IO, r & ~RTC_B_PIE);
}
PUBLIC void arch_ack_profile_clock(void)
{
/* Mandatory read of CMOS register to re-enable timer interrupts. */
outb(RTC_INDEX, RTC_REG_C);
inb(RTC_IO);
}
#endif
#define COLOR_BASE 0xB8000L
PRIVATE void cons_setc(int pos, int c)
{
char ch;
ch= c;
phys_copy(vir2phys((vir_bytes)&ch), COLOR_BASE+(20*80+pos)*2, 1);
}
PRIVATE void cons_seth(int pos, int n)
{
n &= 0xf;
if (n < 10)
cons_setc(pos, '0'+n);
else
cons_setc(pos, 'A'+(n-10));
}
/* Saved by mpx386.s into these variables. */
u32_t params_size, params_offset, mon_ds;
PUBLIC int arch_get_params(char *params, int maxsize)
{
phys_copy(seg2phys(mon_ds) + params_offset, vir2phys(params),
MIN(maxsize, params_size));
params[maxsize-1] = '\0';
return OK;
}
PUBLIC int arch_set_params(char *params, int size)
{
if(size > params_size)
return E2BIG;
phys_copy(vir2phys(params), seg2phys(mon_ds) + params_offset, size);
return OK;
}
PUBLIC void arch_do_syscall(struct proc *proc)
{
/* Perform a previously postponed system call.
*/
int call_nr, src_dst_e;
message *m_ptr;
long bit_map;
/* Get the system call parameters from their respective registers. */
call_nr = proc->p_reg.cx;
src_dst_e = proc->p_reg.retreg;
m_ptr = (message *) proc->p_reg.bx;
bit_map = proc->p_reg.dx;
/* Make the system call, for real this time. */
proc->p_reg.retreg = do_ipc(call_nr, src_dst_e, m_ptr, bit_map);
}
PUBLIC struct proc * arch_finish_schedcheck(void)
{
char * stk;
stk = (char *)tss.sp0;
/* set pointer to the process to run on the stack */
*((reg_t *)stk) = (reg_t) proc_ptr;
return proc_ptr;
}