OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/arch/i386/kernel
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/time.c
0,0 → 1,505
/*
* linux/arch/i386/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*
* This file contains the PC-specific time handling details:
* reading the RTC at bootup, etc..
* 1994-07-02 Alan Modra
* fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
* 1995-03-26 Markus Kuhn
* fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
* precision CMOS clock update
* 1996-05-03 Ingo Molnar
* fixed time warps in do_[slow|fast]_gettimeoffset()
* 1997-09-10 Updated NTP code according to technical memorandum Jan '96
* "A Kernel Model for Precision Timekeeping" by Dave Mills
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
 
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
 
#include <linux/mc146818rtc.h>
#include <linux/timex.h>
#include <linux/config.h>
 
extern int setup_x86_irq(int, struct irqaction *);
 
#ifndef CONFIG_APM /* cycle counter may be unreliable */
/* Cycle counter value at the previous timer interrupt.. */
static struct {
unsigned long low;
unsigned long high;
} init_timer_cc, last_timer_cc;
 
/*
* This is more assembly than C, but it's also rather
* timing-critical and we have to use assembler to get
* reasonable 64-bit arithmetic
*/
static unsigned long do_fast_gettimeoffset(void)
{
register unsigned long eax asm("ax");
register unsigned long edx asm("dx");
unsigned long tmp, quotient, low_timer, missing_time;
 
/* Last jiffy when do_fast_gettimeoffset() was called.. */
static unsigned long last_jiffies=0;
 
/* Cached "clocks per usec" value.. */
static unsigned long cached_quotient=0;
 
/* The "clocks per usec" value is calculated once each jiffy */
tmp = jiffies;
quotient = cached_quotient;
low_timer = last_timer_cc.low;
missing_time = 0;
if (last_jiffies != tmp) {
last_jiffies = tmp;
/*
* test for hanging bottom handler (this means xtime is not
* updated yet)
*/
if (test_bit(TIMER_BH, &bh_active) )
{
missing_time = 1000020/HZ;
}
 
/* Get last timer tick in absolute kernel time */
eax = low_timer;
edx = last_timer_cc.high;
__asm__("subl "SYMBOL_NAME_STR(init_timer_cc)",%0\n\t"
"sbbl "SYMBOL_NAME_STR(init_timer_cc)"+4,%1"
:"=a" (eax), "=d" (edx)
:"0" (eax), "1" (edx));
 
/*
* Divide the 64-bit time with the 32-bit jiffy counter,
* getting the quotient in clocks.
*
* Giving quotient = "average internal clocks per usec"
*/
__asm__("divl %2"
:"=a" (eax), "=d" (edx)
:"r" (tmp),
"0" (eax), "1" (edx));
 
edx = 1000020/HZ;
tmp = eax;
eax = 0;
 
__asm__("divl %2"
:"=a" (eax), "=d" (edx)
:"r" (tmp),
"0" (eax), "1" (edx));
cached_quotient = eax;
quotient = eax;
}
 
/* Read the time counter */
__asm__(".byte 0x0f,0x31"
:"=a" (eax), "=d" (edx));
 
/* .. relative to previous jiffy (32 bits is enough) */
edx = 0;
eax -= low_timer;
 
/*
* Time offset = (1000020/HZ * time_low) / quotient.
*/
 
__asm__("mull %2"
:"=a" (eax), "=d" (edx)
:"r" (quotient),
"0" (eax), "1" (edx));
 
/*
* Due to rounding errors (and jiffies inconsistencies),
* we need to check the result so that we'll get a timer
* that is monotonic.
*/
if (edx >= 1000020/HZ)
edx = 1000020/HZ-1;
 
eax = edx + missing_time;
return eax;
}
#endif
 
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*
* However, the pc-audio speaker driver changes the divisor so that
* it gets interrupted rather more often - it loads 64 into the
* counter rather than 11932! This has an adverse impact on
* do_gettimeoffset() -- it stops working! What is also not
* good is that the interval that our timer function gets called
* is no longer 10.0002 ms, but 9.9767 ms. To get around this
* would require using a different timing source. Maybe someone
* could use the RTC - I know that this can interrupt at frequencies
* ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
* it so that at startup, the timer code in sched.c would select
* using either the RTC or the 8253 timer. The decision would be
* based on whether there was any other device around that needed
* to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
* and then do some jiggery to have a version of do_timer that
* advanced the clock by 1/1024 s. Every time that reached over 1/100
* of a second, then do all the old code. If the time was kept correct
* then do_gettimeoffset could just return 0 - there is no low order
* divider that can be accessed.
*
* Ideally, you would be able to use the RTC for the speaker driver,
* but it appears that the speaker driver really needs interrupt more
* often than every 120 us or so.
*
* Anyway, this needs more thought.... pjsg (1993-08-28)
*
* If you are really that interested, you should be reading
* comp.protocols.time.ntp!
*/
 
#define TICK_SIZE tick
 
static unsigned long do_slow_gettimeoffset(void)
{
int count;
static int count_p = 0;
unsigned long offset = 0;
static unsigned long jiffies_p = 0;
 
/*
* cache volatile jiffies temporarily; we have IRQs turned off.
*/
unsigned long jiffies_t;
 
/* timer count may underflow right here */
outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8;
 
jiffies_t = jiffies;
 
/*
* avoiding timer inconsistencies (they are rare, but they happen)...
* there are three kinds of problems that must be avoided here:
* 1. the timer counter underflows
* 2. hardware problem with the timer, not giving us continuous time,
* the counter does small "jumps" upwards on some Pentium systems,
* thus causes time warps
* 3. we are after the timer interrupt, but the bottom half handler
* hasn't executed yet.
*/
if( count > count_p ) {
if( jiffies_t == jiffies_p ) {
if( count > LATCH-LATCH/100 )
offset = TICK_SIZE;
else
/*
* argh, the timer is bugging we cant do nothing
* but to give the previous clock value.
*/
count = count_p;
} else {
if( test_bit(TIMER_BH, &bh_active) ) {
/*
* we have detected a counter underflow.
*/
offset = TICK_SIZE;
count_p = count;
} else {
count_p = count;
jiffies_p = jiffies_t;
}
}
} else {
count_p = count;
jiffies_p = jiffies_t;
}
 
 
count = ((LATCH-1) - count) * TICK_SIZE;
count = (count + LATCH/2) / LATCH;
 
return offset + count;
}
 
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
 
/*
* This version of gettimeofday has near microsecond resolution.
*/
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
 
save_flags(flags);
cli();
*tv = xtime;
tv->tv_usec += do_gettimeoffset();
if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
restore_flags(flags);
}
 
void do_settimeofday(struct timeval *tv)
{
cli();
/* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is
* is value at the last tick.
* Discover what correction gettimeofday
* would have done, and then undo it!
*/
tv->tv_usec -= do_gettimeoffset();
 
if (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
tv->tv_sec--;
}
 
xtime = *tv;
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_state = TIME_ERROR; /* p. 24, (a) */
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
sti();
}
 
 
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be
* called 500 ms after the second nowtime has started, because when
* nowtime is written into the registers of the CMOS clock, it will
* jump to the next second precisely 500 ms later. Check the Motorola
* MC146818A or Dallas DS12887 data sheet for details.
*
* BUG: This routine does not handle hour overflow properly; it just
* sets the minutes. Usually you'll only notice that after reboot!
*/
static int set_rtc_mmss(unsigned long nowtime)
{
int retval = 0;
int real_seconds, real_minutes, cmos_minutes;
unsigned char save_control, save_freq_select;
 
save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
cmos_minutes = CMOS_READ(RTC_MINUTES);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
BCD_TO_BIN(cmos_minutes);
 
/*
* since we're only adjusting minutes and seconds,
* don't interfere with hour overflow. This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60;
real_minutes = nowtime / 60;
if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
real_minutes += 30; /* correct for half hour time zone */
real_minutes %= 60;
 
if (abs(real_minutes - cmos_minutes) < 30) {
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BIN_TO_BCD(real_seconds);
BIN_TO_BCD(real_minutes);
}
CMOS_WRITE(real_seconds,RTC_SECONDS);
CMOS_WRITE(real_minutes,RTC_MINUTES);
} else {
printk(KERN_WARNING
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
}
 
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 (popular MC146818A clone with integrated
* battery and quartz) will not reset the oscillator and will not
* update precisely 500 ms later. You won't find this mentioned in
* the Dallas Semiconductor data sheets, but who believes data
* sheets anyway ... -- Markus Kuhn
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
return retval;
}
 
/* last time the cmos clock got updated */
static long last_rtc_update = 0;
 
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
do_timer(regs);
 
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
*/
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec > 500000 - (tick >> 1) &&
xtime.tv_usec < 500000 + (tick >> 1))
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
/* As we return to user mode fire off the other CPU schedulers.. this is
basically because we don't yet share IRQ's around. This message is
rigged to be safe on the 386 - basically it's a hack, so don't look
closely for now.. */
/*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */
}
 
#ifndef CONFIG_APM /* cycle counter may be unreliable */
/*
* This is the same as the above, except we _also_ save the current
* cycle counter value at the time of the timer interrupt, so that
* we later on can estimate the time of day more exactly.
*/
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/* read Pentium cycle counter */
__asm__(".byte 0x0f,0x31"
:"=a" (last_timer_cc.low),
"=d" (last_timer_cc.high));
timer_interrupt(irq, NULL, regs);
}
#endif
 
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
static inline unsigned long mktime(unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec)
{
if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return (((
(unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
 
/* not static: needed by APM */
unsigned long get_cmos_time(void)
{
unsigned int year, mon, day, hour, min, sec;
int i;
 
/* The Linux interpretation of the CMOS clock register contents:
* When the Update-In-Progress (UIP) flag goes from 1 to 0, the
* RTC registers show the second which has precisely just started.
* Let's hope other operating systems interpret the RTC the same way.
*/
/* read RTC exactly on falling edge of update flag */
for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
break;
for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
break;
do { /* Isn't this overkill ? UIP above should guarantee consistency */
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
} while (sec != CMOS_READ(RTC_SECONDS));
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
if ((year += 1900) < 1970)
year += 100;
return mktime(year, mon, day, hour, min, sec);
}
 
static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL};
 
void time_init(void)
{
xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;
 
/* If we have the CPU hardware time counters, use them */
#ifndef CONFIG_APM
/* Don't use them if a suspend/resume could
corrupt the timer value. This problem
needs more debugging. */
if (x86_capability & 16)
if (strncmp(x86_vendor_id, "Cyrix", 5) != 0) {
do_gettimeoffset = do_fast_gettimeoffset;
 
if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) {
if( x86 == 5 ) {
if( x86_model == 0 ) {
/* turn on cycle counters during power down */
__asm__ __volatile__ (" movl $0x83, %%ecx \n \
.byte 0x0f,0x32 \n \
orl $1,%%eax \n \
.byte 0x0f,0x30 \n "
: : : "ax", "cx", "dx" );
udelay(500);
}
}
}
 
/* read Pentium cycle counter */
__asm__(".byte 0x0f,0x31"
:"=a" (init_timer_cc.low),
"=d" (init_timer_cc.high));
irq0.handler = pentium_timer_interrupt;
}
#endif
setup_x86_irq(0, &irq0);
}
/trampoline32.S
0,0 → 1,20
!
! 32bit side of the trampoline code
!
#define __ASSEMBLY__
#include <asm/segment.h>
!
!
! Anything but a relative address here will be wrong by 8K...
!
.globl startup32
.text
startup32:
! Run the kernel
mov eax,#KERNEL_DS
mov ds,ax
mov eax,#0xA5A5A5A5
mov [8192],eax
jmpi 0x100000,KERNEL_CS
l1:
.byte 0xEA,0x00,0x00,0x10,0x00,0x10,0x00
/smp.c
0,0 → 1,1233
/*
* Intel MP v1.1/v1.4 specification support routines for multi-pentium
* hosts.
*
* (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
* Supported by Caldera http://www.caldera.com.
* Much of the core SMP work is based on previous work by Thomas Radke, to
* whom a great many thanks are extended.
*
* Thanks to Intel for making available several different Pentium and
* Pentium Pro MP machines.
*
* This code is released under the GNU public license version 2 or
* later.
*
* Fixes
* Felix Koop : NR_CPUS used properly
* Jose Renau : Handle single CPU case.
* Alan Cox : By repeated request 8) - Total BogoMIP report.
* Greg Wright : Fix for kernel stacks panic.
* Erich Boleyn : MP v1.4 and additional changes.
*/
 
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h>
#include <asm/i82489.h>
#include <linux/smp.h>
#include <asm/pgtable.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
#include <asm/smp.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
 
/*
* Why isn't this somewhere standard ??
*/
extern __inline int max(int a,int b)
{
if(a>b)
return a;
return b;
}
 
 
int smp_found_config=0; /* Have we found an SMP box */
 
unsigned long cpu_present_map = 0; /* Bitmask of existing CPU's */
int smp_num_cpus = 1; /* Total count of live CPU's */
int smp_threads_ready=0; /* Set when the idlers are all forked */
volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */
volatile int cpu_logical_map[NR_CPUS]; /* which logical number maps to which CPU */
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */
volatile unsigned long smp_invalidate_needed; /* Used for the invalidate map that's also checked in the spinlock */
struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per cpu bogomips and other parameters */
static unsigned int num_processors = 1; /* Internal processor count */
static unsigned long io_apic_addr = 0xFEC00000; /* Address of the I/O apic (not yet used) */
unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */
static unsigned char *kstack_base,*kstack_end; /* Kernel stack list pointers */
static int smp_activated = 0; /* Tripped once we need to start cross invalidating */
int apic_version[NR_CPUS]; /* APIC version number */
static volatile int smp_commenced=0; /* Tripped when we start scheduling */
unsigned long apic_addr=0xFEE00000; /* Address of APIC (defaults to 0xFEE00000) */
unsigned long nlong = 0; /* dummy used for apic_reg address + 0x20 */
unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the vremap() of the APIC */
unsigned long apic_retval; /* Just debugging the assembler.. */
unsigned char *kernel_stacks[NR_CPUS]; /* Kernel stack pointers for CPU's (debugging) */
 
static volatile unsigned char smp_cpu_in_msg[NR_CPUS]; /* True if this processor is sending an IPI */
static volatile unsigned long smp_msg_data; /* IPI data pointer */
static volatile int smp_src_cpu; /* IPI sender processor */
static volatile int smp_msg_id; /* Message being sent */
 
volatile unsigned long kernel_flag=0; /* Kernel spinlock */
volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */
volatile unsigned long kernel_counter=0; /* Number of times the processor holds the lock */
volatile unsigned long syscall_count=0; /* Number of times the processor holds the syscall lock */
 
volatile unsigned long ipi_count; /* Number of IPI's delivered */
#ifdef __SMP_PROF__
volatile unsigned long smp_spins[NR_CPUS]={0}; /* Count interrupt spins */
volatile unsigned long smp_spins_syscall[NR_CPUS]={0}; /* Count syscall spins */
volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall */
volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle */
volatile unsigned long smp_idle_count[1+NR_CPUS]={0,}; /* Count idle ticks */
#endif
#if defined (__SMP_PROF__)
volatile unsigned long smp_idle_map=0; /* Map for idle processors */
#endif
 
volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};/* for computing process time */
volatile unsigned long smp_process_available=0;
 
/*#define SMP_DEBUG*/
 
#ifdef SMP_DEBUG
#define SMP_PRINTK(x) printk x
#else
#define SMP_PRINTK(x)
#endif
 
 
/*
* Checksum an MP configuration block.
*/
static int mpf_checksum(unsigned char *mp, int len)
{
int sum=0;
while(len--)
sum+=*mp++;
return sum&0xFF;
}
 
/*
* Processor encoding in an MP configuration block
*/
static char *mpc_family(int family,int model)
{
static char n[32];
static char *model_defs[]=
{
"80486DX","80486DX",
"80486SX","80486DX/2 or 80487",
"80486SL","Intel5X2(tm)",
"Unknown","Unknown",
"80486DX/4"
};
if(family==0x6)
return("Pentium(tm) Pro");
if(family==0x5)
return("Pentium(tm)");
if(family==0x0F && model==0x0F)
return("Special controller");
if(family==0x04 && model<9)
return model_defs[model];
sprintf(n,"Unknown CPU [%d:%d]",family, model);
return n;
}
 
/*
* Read the MPC
*/
 
static int smp_read_mpc(struct mp_config_table *mpc)
{
char str[16];
int count=sizeof(*mpc);
int apics=0;
unsigned char *mpt=((unsigned char *)mpc)+count;
 
if(memcmp(mpc->mpc_signature,MPC_SIGNATURE,4))
{
printk("Bad signature [%c%c%c%c].\n",
mpc->mpc_signature[0],
mpc->mpc_signature[1],
mpc->mpc_signature[2],
mpc->mpc_signature[3]);
return 1;
}
if(mpf_checksum((unsigned char *)mpc,mpc->mpc_length))
{
printk("Checksum error.\n");
return 1;
}
if(mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04)
{
printk("Bad Config Table version (%d)!!\n",mpc->mpc_spec);
return 1;
}
memcpy(str,mpc->mpc_oem,8);
str[8]=0;
printk("OEM ID: %s ",str);
memcpy(str,mpc->mpc_productid,12);
str[12]=0;
printk("Product ID: %s ",str);
printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
 
/* set the local APIC address */
apic_addr = mpc->mpc_lapic;
/*
* Now process the configuration blocks.
*/
while(count<mpc->mpc_length)
{
switch(*mpt)
{
case MP_PROCESSOR:
{
struct mpc_config_processor *m=
(struct mpc_config_processor *)mpt;
if(m->mpc_cpuflag&CPU_ENABLED)
{
printk("Processor #%d %s APIC version %d\n",
m->mpc_apicid,
mpc_family((m->mpc_cpufeature&
CPU_FAMILY_MASK)>>8,
(m->mpc_cpufeature&
CPU_MODEL_MASK)>>4),
m->mpc_apicver);
#ifdef SMP_DEBUG
if(m->mpc_featureflag&(1<<0))
printk(" Floating point unit present.\n");
if(m->mpc_featureflag&(1<<7))
printk(" Machine Exception supported.\n");
if(m->mpc_featureflag&(1<<8))
printk(" 64 bit compare & exchange supported.\n");
if(m->mpc_featureflag&(1<<9))
printk(" Internal APIC present.\n");
#endif
if(m->mpc_cpuflag&CPU_BOOTPROCESSOR)
{
SMP_PRINTK((" Bootup CPU\n"));
boot_cpu_id=m->mpc_apicid;
}
else /* Boot CPU already counted */
num_processors++;
if(m->mpc_apicid>NR_CPUS)
printk("Processor #%d unused. (Max %d processors).\n",m->mpc_apicid, NR_CPUS);
else
{
cpu_present_map|=(1<<m->mpc_apicid);
apic_version[m->mpc_apicid]=m->mpc_apicver;
}
}
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_BUS:
{
struct mpc_config_bus *m=
(struct mpc_config_bus *)mpt;
memcpy(str,m->mpc_bustype,6);
str[6]=0;
SMP_PRINTK(("Bus #%d is %s\n",
m->mpc_busid,
str));
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_IOAPIC:
{
struct mpc_config_ioapic *m=
(struct mpc_config_ioapic *)mpt;
if(m->mpc_flags&MPC_APIC_USABLE)
{
apics++;
printk("I/O APIC #%d Version %d at 0x%lX.\n",
m->mpc_apicid,m->mpc_apicver,
m->mpc_apicaddr);
io_apic_addr = m->mpc_apicaddr;
}
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_INTSRC:
{
struct mpc_config_intsrc *m=
(struct mpc_config_intsrc *)mpt;
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
case MP_LINTSRC:
{
struct mpc_config_intlocal *m=
(struct mpc_config_intlocal *)mpt;
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
}
}
}
if(apics>1)
printk("Warning: Multiple APIC's not supported.\n");
return num_processors;
}
 
/*
* Scan the memory blocks for an SMP configuration block.
*/
int smp_scan_config(unsigned long base, unsigned long length)
{
unsigned long *bp=(unsigned long *)base;
struct intel_mp_floating *mpf;
SMP_PRINTK(("Scan SMP from %p for %ld bytes.\n",
bp,length));
if(sizeof(*mpf)!=16)
printk("Error: MPF size\n");
while(length>0)
{
if(*bp==SMP_MAGIC_IDENT)
{
mpf=(struct intel_mp_floating *)bp;
if(mpf->mpf_length==1 &&
!mpf_checksum((unsigned char *)bp,16) &&
(mpf->mpf_specification == 1
|| mpf->mpf_specification == 4) )
{
printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
if(mpf->mpf_feature2&(1<<7))
printk(" IMCR and PIC compatibility mode.\n");
else
printk(" Virtual Wire compatibility mode.\n");
smp_found_config=1;
/*
* Now see if we need to read further.
*/
if(mpf->mpf_feature1!=0)
{
unsigned long cfg;
 
/*
* We need to know what the local
* APIC id of the boot CPU is!
*/
 
/*
*
* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
*
* It's not just a crazy hack... ;-)
*/
/*
* Standard page mapping
* functions don't work yet.
* We know that page 0 is not
* used. Steal it for now!
*/
cfg=pg0[0];
pg0[0] = (apic_addr | 7);
local_flush_tlb();
 
boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID));
 
/*
* Give it back
*/
 
pg0[0]= cfg;
local_flush_tlb();
 
/*
*
* END OF HACK END OF HACK END OF HACK END OF HACK END OF HACK
*
*/
/*
* 2 CPUs, numbered 0 & 1.
*/
cpu_present_map=3;
num_processors=2;
printk("I/O APIC at 0xFEC00000.\n");
printk("Bus#0 is ");
}
switch(mpf->mpf_feature1)
{
case 1:
case 5:
printk("ISA\n");
break;
case 2:
printk("EISA with no IRQ8 chaining\n");
break;
case 6:
case 3:
printk("EISA\n");
break;
case 4:
case 7:
printk("MCA\n");
break;
case 0:
break;
default:
printk("???\nUnknown standard configuration %d\n",
mpf->mpf_feature1);
return 1;
}
if(mpf->mpf_feature1>4)
{
printk("Bus #1 is PCI\n");
 
/*
* Set local APIC version to
* the integrated form.
* It's initialized to zero
* otherwise, representing
* a discrete 82489DX.
*/
apic_version[0] = 0x10;
apic_version[1] = 0x10;
}
/*
* Read the physical hardware table.
* Anything here will override the
* defaults.
*/
if(mpf->mpf_physptr)
smp_read_mpc((void *)mpf->mpf_physptr);
 
/*
* Now that the boot CPU id is known,
* set some other information about it.
*/
nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */
cpu_logical_map[0] = boot_cpu_id;
 
printk("Processors: %d\n", num_processors);
/*
* Only use the first configuration found.
*/
return 1;
}
}
bp+=4;
length-=16;
}
 
return 0;
}
 
/*
* Trampoline 80x86 program as an array.
*/
 
static unsigned char trampoline_data[]={
#include "trampoline.hex"
};
 
/*
* Currently trivial. Write the real->protected mode
* bootstrap into the page concerned. The caller
* has made sure it's suitably aligned.
*/
static void install_trampoline(unsigned char *mp)
{
memcpy(mp,trampoline_data,sizeof(trampoline_data));
}
 
/*
* We are called very early to get the low memory for the trampoline/kernel stacks
* This has to be done by mm/init.c to parcel us out nice low memory. We allocate
* the kernel stacks at 4K, 8K, 12K... currently (0-03FF is preserved for SMM and
* other things).
*/
unsigned long smp_alloc_memory(unsigned long mem_base)
{
int size=(num_processors-1)*PAGE_SIZE; /* Number of stacks needed */
/*
* Our stacks have to be below the 1Mb line, and mem_base on entry
* is 4K aligned.
*/
if(mem_base+size>=0x9F000)
panic("smp_alloc_memory: Insufficient low memory for kernel stacks.\n");
kstack_base=(void *)mem_base;
mem_base+=size;
kstack_end=(void *)mem_base;
return mem_base;
}
/*
* Hand out stacks one at a time.
*/
static void *get_kernel_stack(void)
{
void *stack=kstack_base;
if(kstack_base>=kstack_end)
return NULL;
kstack_base+=PAGE_SIZE;
return stack;
}
 
 
/*
* The bootstrap kernel entry code has set these up. Save them for
* a given CPU
*/
void smp_store_cpu_info(int id)
{
struct cpuinfo_x86 *c=&cpu_data[id];
c->hard_math=hard_math; /* Always assumed same currently */
c->x86=x86;
c->x86_model=x86_model;
c->x86_mask=x86_mask;
c->x86_capability=x86_capability;
c->fdiv_bug=fdiv_bug;
c->wp_works_ok=wp_works_ok; /* Always assumed the same currently */
c->hlt_works_ok=hlt_works_ok;
c->have_cpuid=have_cpuid;
c->udelay_val=loops_per_sec;
strcpy(c->x86_vendor_id, x86_vendor_id);
}
 
/*
* Architecture specific routine called by the kernel just before init is
* fired off. This allows the BP to have everything in order [we hope].
* At the end of this all the AP's will hit the system scheduling and off
* we go. Each AP will load the system gdt's and jump through the kernel
* init into idle(). At this point the scheduler will one day take over
* and give them jobs to do. smp_callin is a standard routine
* we use to track CPU's as they power up.
*/
 
void smp_commence(void)
{
/*
* Lets the callin's below out of their loop.
*/
smp_commenced=1;
}
void smp_callin(void)
{
extern void calibrate_delay(void);
int cpuid=GET_APIC_ID(apic_read(APIC_ID));
unsigned long l;
extern struct desc_struct idt_descriptor;
extern int pentium_f00f_bug;
if (pentium_f00f_bug) {
__asm__ __volatile__("\tlidt %0": "=m" (idt_descriptor));
}
 
/*
* Activate our APIC
*/
SMP_PRINTK(("CALLIN %d\n",smp_processor_id()));
l=apic_read(APIC_SPIV);
l|=(1<<8); /* Enable */
apic_write(APIC_SPIV,l);
 
#ifdef CONFIG_MTRR
/*
* checks the MTRR configuration of this application processor
*/
check_mtrr_config();
#endif
 
sti();
/*
* Get our bogomips.
*/
calibrate_delay();
/*
* Save our processor parameters
*/
smp_store_cpu_info(cpuid);
/*
* Allow the master to continue.
*/
set_bit(cpuid, (unsigned long *)&cpu_callin_map[0]);
/*
* Until we are ready for SMP scheduling
*/
load_ldt(0);
/* printk("Testing faulting...\n");
*(long *)0=1; OOPS... */
local_flush_tlb();
while(!smp_commenced);
if (cpu_number_map[cpuid] == -1)
while(1);
local_flush_tlb();
SMP_PRINTK(("Commenced..\n"));
load_TR(cpu_number_map[cpuid]);
/* while(1);*/
}
 
/*
* Cycle through the processors sending pentium IPI's to boot each.
*/
void smp_boot_cpus(void)
{
int i;
int cpucount=0;
unsigned long cfg;
void *stack;
extern unsigned long init_user_stack[];
/*
* Initialize the logical to physical cpu number mapping
*/
 
for (i = 0; i < NR_CPUS; i++)
cpu_number_map[i] = -1;
 
/*
* Setup boot CPU information
*/
kernel_stacks[boot_cpu_id]=(void *)init_user_stack; /* Set up for boot processor first */
 
smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */
 
cpu_present_map |= (1 << smp_processor_id());
cpu_number_map[boot_cpu_id] = 0;
active_kernel_processor=boot_cpu_id;
 
/*
* If we don't conform to the Intel MPS standard, get out
* of here now!
*/
 
if (!smp_found_config)
return;
 
/*
* Map the local APIC into kernel space
*/
 
apic_reg = vremap(apic_addr,4096);
if(apic_reg == NULL)
panic("Unable to map local apic.\n");
#ifdef SMP_DEBUG
{
int reg;
 
/*
* This is to verify that we're looking at
* a real local APIC. Check these against
* your board if the CPUs aren't getting
* started for no apparent reason.
*/
 
reg = apic_read(APIC_VERSION);
SMP_PRINTK(("Getting VERSION: %x\n", reg));
 
apic_write(APIC_VERSION, 0);
reg = apic_read(APIC_VERSION);
SMP_PRINTK(("Getting VERSION: %x\n", reg));
 
/*
* The two version reads above should print the same
* NON-ZERO!!! numbers. If the second one is zero,
* there is a problem with the APIC write/read
* definitions.
*
* The next two are just to see if we have sane values.
* They're only really relevant if we're in Virtual Wire
* compatibility mode, but most boxes are anymore.
*/
 
 
reg = apic_read(APIC_LVT0);
SMP_PRINTK(("Getting LVT0: %x\n", reg));
 
reg = apic_read(APIC_LVT1);
SMP_PRINTK(("Getting LVT1: %x\n", reg));
}
#endif
/*
* Enable the local APIC
*/
cfg=apic_read(APIC_SPIV);
cfg|=(1<<8); /* Enable APIC */
apic_write(APIC_SPIV,cfg);
 
udelay(10);
/*
* Now scan the cpu present map and fire up the other CPUs.
*/
SMP_PRINTK(("CPU map: %lx\n", cpu_present_map));
for(i=0;i<NR_CPUS;i++)
{
/*
* Don't even attempt to start the boot CPU!
*/
if (i == boot_cpu_id)
continue;
if (cpu_present_map & (1 << i))
{
unsigned long send_status, accept_status;
int timeout, num_starts, j;
/*
* We need a kernel stack for each processor.
*/
stack=get_kernel_stack(); /* We allocated these earlier */
if(stack==NULL)
panic("No memory for processor stacks.\n");
kernel_stacks[i]=stack;
install_trampoline(stack);
 
printk("Booting processor %d stack %p: ",i,stack); /* So we set what's up */
 
/*
* This grunge runs the startup process for
* the targeted processor.
*/
 
SMP_PRINTK(("Setting warm reset code and vector.\n"));
 
/*
* Install a writable page 0 entry.
*/
cfg=pg0[0];
CMOS_WRITE(0xa, 0xf);
pg0[0]=7;
local_flush_tlb();
*((volatile unsigned short *) 0x469) = ((unsigned long)stack)>>4;
*((volatile unsigned short *) 0x467) = 0;
/*
* Protect it again
*/
pg0[0]= cfg;
local_flush_tlb();
 
/*
* Be paranoid about clearing APIC errors.
*/
 
if ( apic_version[i] & 0xF0 )
{
apic_write(APIC_ESR, 0);
accept_status = (apic_read(APIC_ESR) & 0xEF);
}
/*
* Status is now clean
*/
send_status = 0;
accept_status = 0;
 
/*
* Starting actual IPI sequence...
*/
 
SMP_PRINTK(("Asserting INIT.\n"));
 
/*
* Turn INIT on
*/
cfg=apic_read(APIC_ICR2);
cfg&=0x00FFFFFF;
apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
cfg=apic_read(APIC_ICR);
cfg&=~0xCDFFF; /* Clear bits */
cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG
| APIC_DEST_ASSERT | APIC_DEST_DM_INIT);
apic_write(APIC_ICR, cfg); /* Send IPI */
 
udelay(200);
SMP_PRINTK(("Deasserting INIT.\n"));
 
cfg=apic_read(APIC_ICR2);
cfg&=0x00FFFFFF;
apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
cfg=apic_read(APIC_ICR);
cfg&=~0xCDFFF; /* Clear bits */
cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG
| APIC_DEST_DM_INIT);
apic_write(APIC_ICR, cfg); /* Send IPI */
/*
* Should we send STARTUP IPIs ?
*
* Determine this based on the APIC version.
* If we don't have an integrated APIC, don't
* send the STARTUP IPIs.
*/
 
if ( apic_version[i] & 0xF0 )
num_starts = 2;
else
num_starts = 0;
 
/*
* Run STARTUP IPI loop.
*/
 
for (j = 1; !(send_status || accept_status)
&& (j <= num_starts) ; j++)
{
SMP_PRINTK(("Sending STARTUP #%d.\n",j));
 
apic_write(APIC_ESR, 0);
/*
* STARTUP IPI
*/
 
cfg=apic_read(APIC_ICR2);
cfg&=0x00FFFFFF;
apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
cfg=apic_read(APIC_ICR);
cfg&=~0xCDFFF; /* Clear bits */
cfg |= (APIC_DEST_FIELD
| APIC_DEST_DM_STARTUP
| (((int) stack) >> 12) ); /* Boot on the stack */
apic_write(APIC_ICR, cfg); /* Kick the second */
 
timeout = 0;
do {
udelay(10);
} while ( (send_status = (apic_read(APIC_ICR) & 0x1000))
&& (timeout++ < 1000));
udelay(200);
 
accept_status = (apic_read(APIC_ESR) & 0xEF);
}
 
if (send_status) /* APIC never delivered?? */
printk("APIC never delivered???\n");
if (accept_status) /* Send accept error */
printk("APIC delivery error (%lx).\n", accept_status);
if( !(send_status || accept_status) )
{
for(timeout=0;timeout<50000;timeout++)
{
if(cpu_callin_map[0]&(1<<i))
break; /* It has booted */
udelay(100); /* Wait 5s total for a response */
}
if(cpu_callin_map[0]&(1<<i))
{
cpucount++;
/* number CPUs logically, starting from 1 (BSP is 0) */
cpu_number_map[i] = cpucount;
cpu_logical_map[cpucount] = i;
}
else
{
if(*((volatile unsigned char *)8192)==0xA5)
printk("Stuck ??\n");
else
printk("Not responding.\n");
}
}
 
/* mark "stuck" area as not stuck */
*((volatile unsigned long *)8192) = 0;
}
/*
* Make sure we unmap all failed CPUs
*/
if (cpu_number_map[i] == -1)
cpu_present_map &= ~(1 << i);
}
 
/*
* Cleanup possible dangling ends...
*/
 
/*
* Install writable page 0 entry.
*/
 
cfg = pg0[0];
pg0[0] = 3; /* writeable, present, addr 0 */
local_flush_tlb();
 
/*
* Paranoid: Set warm reset code and vector here back
* to default values.
*/
 
CMOS_WRITE(0, 0xf);
 
*((volatile long *) 0x467) = 0;
 
/*
* Restore old page 0 entry.
*/
 
pg0[0] = cfg;
local_flush_tlb();
 
/*
* Allow the user to impress friends.
*/
if(cpucount==0)
{
printk("Error: only one processor found.\n");
cpu_present_map=(1<<smp_processor_id());
}
else
{
unsigned long bogosum=0;
for(i=0;i<32;i++)
{
if(cpu_present_map&(1<<i))
bogosum+=cpu_data[i].udelay_val;
}
printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
cpucount+1,
(bogosum+2500)/500000,
((bogosum+2500)/5000)%100);
smp_activated=1;
smp_num_cpus=cpucount+1;
}
}
 
 
/*
* A non wait message cannot pass data or cpu source info. This current setup
* is only safe because the kernel lock owner is the only person who can send a message.
*
* Wrapping this whole block in a spinlock is not the safe answer either. A processor may
* get stuck with irq's off waiting to send a message and thus not replying to the person
* spinning for a reply....
*
* In the end flush tlb ought to be the NMI and a very very short function (to avoid the old
* IDE disk problems), and other messages sent with IRQ's enabled in a civilised fashion. That
* will also boost performance.
*/
void smp_message_pass(int target, int msg, unsigned long data, int wait)
{
unsigned long cfg;
unsigned long target_map;
int p=smp_processor_id();
int irq=0x2d; /* IRQ 13 */
int ct=0;
static volatile int message_cpu = NO_PROC_ID;
 
/*
* During boot up send no messages
*/
if(!smp_activated || !smp_commenced)
return;
/*
* Skip the reschedule if we are waiting to clear a
* message at this time. The reschedule cannot wait
* but is not critical.
*/
if(msg==MSG_RESCHEDULE) /* Reschedules we do via trap 0x30 */
{
irq=0x30;
if(smp_cpu_in_msg[p])
return;
}
 
/*
* Sanity check we don't re-enter this across CPU's. Only the kernel
* lock holder may send messages. For a STOP_CPU we are bringing the
* entire box to the fastest halt we can.. A reschedule carries
* no data and can occur during a flush.. guess what panic
* I got to notice this bug...
*/
if(message_cpu!=NO_PROC_ID && msg!=MSG_STOP_CPU && msg!=MSG_RESCHEDULE)
{
panic("CPU #%d: Message pass %d but pass in progress by %d of %d\n",
smp_processor_id(),msg,message_cpu, smp_msg_id);
}
message_cpu=smp_processor_id();
 
/*
* We are busy
*/
smp_cpu_in_msg[p]++;
/*
* Reschedule is currently special
*/
if(msg!=MSG_RESCHEDULE)
{
smp_src_cpu=p;
smp_msg_id=msg;
smp_msg_data=data;
}
/* printk("SMP message pass #%d to %d of %d\n",
p, msg, target);*/
/*
* Wait for the APIC to become ready - this should never occur. Its
* a debugging check really.
*/
while(ct<1000)
{
cfg=apic_read(APIC_ICR);
if(!(cfg&(1<<12)))
break;
ct++;
udelay(10);
}
/*
* Just pray... there is nothing more we can do
*/
if(ct==1000)
printk("CPU #%d: previous IPI still not cleared after 10ms\n", smp_processor_id());
/*
* Program the APIC to deliver the IPI
*/
cfg=apic_read(APIC_ICR2);
cfg&=0x00FFFFFF;
apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(target)); /* Target chip */
cfg=apic_read(APIC_ICR);
cfg&=~0xFDFFF; /* Clear bits */
cfg|=APIC_DEST_FIELD|APIC_DEST_DM_FIXED|irq; /* Send an IRQ 13 */
 
/*
* Set the target requirement
*/
if(target==MSG_ALL_BUT_SELF)
{
cfg|=APIC_DEST_ALLBUT;
target_map=cpu_present_map;
cpu_callin_map[0]=(1<<smp_src_cpu);
}
else if(target==MSG_ALL)
{
cfg|=APIC_DEST_ALLINC;
target_map=cpu_present_map;
cpu_callin_map[0]=0;
}
else
{
target_map=(1<<target);
cpu_callin_map[0]=0;
}
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
apic_write(APIC_ICR, cfg);
/*
* Spin waiting for completion
*/
switch(wait)
{
case 1:
while(cpu_callin_map[0]!=target_map); /* Spin on the pass */
break;
case 2:
while(smp_invalidate_needed); /* Wait for invalidate map to clear */
break;
}
/*
* Record our completion
*/
smp_cpu_in_msg[p]--;
message_cpu=NO_PROC_ID;
}
 
/*
* This is fraught with deadlocks. Linus does a flush tlb at a whim
* even with IRQ's off. We have to avoid a pair of crossing flushes
* or we are doomed. See the notes about smp_message_pass.
*/
void smp_flush_tlb(void)
{
unsigned long flags;
if(smp_activated && smp_processor_id()!=active_kernel_processor)
panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor);
/* printk("SMI-");*/
/*
* The assignment is safe because it's volatile so the compiler cannot reorder it,
* because the i586 has strict memory ordering and because only the kernel lock holder
* may issue a tlb flush. If you break any one of those three change this to an atomic
* bus locked or.
*/
smp_invalidate_needed=cpu_present_map&~(1<<smp_processor_id());
/*
* Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will
* ensure they don't do a spurious flush tlb or miss one.
*/
save_flags(flags);
cli();
smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0L, 2);
/*
* Flush the local TLB
*/
local_flush_tlb();
restore_flags(flags);
/*
* Completed.
*/
/* printk("SMID\n");*/
}
 
/*
* Reschedule call back
*/
 
void smp_reschedule_irq(int cpl, struct pt_regs *regs)
{
#ifdef DEBUGGING_SMP_RESCHED
static int ct=0;
if(ct==0)
{
printk("Beginning scheduling on CPU#%d\n",smp_processor_id());
ct=1;
}
#endif
if(smp_processor_id()!=active_kernel_processor)
panic("SMP Reschedule on CPU #%d, but #%d is active.\n",
smp_processor_id(), active_kernel_processor);
 
need_resched=1;
 
/*
* Clear the IPI
*/
apic_read(APIC_SPIV); /* Dummy read */
apic_write(APIC_EOI, 0); /* Docs say use 0 for future compatibility */
}
 
/*
* Message call back.
*/
void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs)
{
int i=smp_processor_id();
/* static int n=0;
if(n++<NR_CPUS)
printk("IPI %d->%d(%d,%ld)\n",smp_src_cpu,i,smp_msg_id,smp_msg_data);*/
switch(smp_msg_id)
{
case 0: /* IRQ 13 testing - boring */
return;
/*
* A TLB flush is needed.
*/
case MSG_INVALIDATE_TLB:
if(clear_bit(i,(unsigned long *)&smp_invalidate_needed))
local_flush_tlb();
set_bit(i, (unsigned long *)&cpu_callin_map[0]);
/* cpu_callin_map[0]|=1<<smp_processor_id();*/
break;
/*
* Halt other CPU's for a panic or reboot
*/
case MSG_STOP_CPU:
while(1)
{
if(cpu_data[smp_processor_id()].hlt_works_ok)
__asm__("hlt");
}
default:
printk("CPU #%d sent invalid cross CPU message to CPU #%d: %X(%lX).\n",
smp_src_cpu,smp_processor_id(),smp_msg_id,smp_msg_data);
break;
}
/*
* Clear the IPI, so we can receive future IPI's
*/
apic_read(APIC_SPIV); /* Dummy read */
apic_write(APIC_EOI, 0); /* Docs say use 0 for future compatibility */
}
 
void irq_deadlock_detected(void)
{
printk("IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
__asm__("hlt");
}
 
void non_irq_deadlock_detected(void)
{
printk("NON-IRQ DEADLOCK DETECTED BY CPU %d\n", smp_processor_id());
__asm__("hlt");
}
/vm86.c
0,0 → 1,635
/*
* linux/kernel/vm86.c
*
* Copyright (C) 1994 Linus Torvalds
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/mm.h>
 
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/io.h>
 
/*
* Known problems:
*
* Interrupt handling is not guaranteed:
* - a real x86 will disable all interrupts for one instruction
* after a "mov ss,xx" to make stack handling atomic even without
* the 'lss' instruction. We can't guarantee this in v86 mode,
* as the next instruction might result in a page fault or similar.
* - a real x86 will have interrupts disabled for one instruction
* past the 'sti' that enables them. We don't bother with all the
* details yet..
*
* Hopefully these problems do not actually matter for anything.
*/
 
 
#define KVM86 ((struct kernel_vm86_struct *)regs)
#define VMPI KVM86->vm86plus
 
 
/*
* 8- and 16-bit register defines..
*/
#define AL(regs) (((unsigned char *)&((regs)->eax))[0])
#define AH(regs) (((unsigned char *)&((regs)->eax))[1])
#define IP(regs) (*(unsigned short *)&((regs)->eip))
#define SP(regs) (*(unsigned short *)&((regs)->esp))
 
/*
* virtual flags (16 and 32-bit versions)
*/
#define VFLAGS (*(unsigned short *)&(current->tss.v86flags))
#define VEFLAGS (current->tss.v86flags)
 
#define set_flags(X,new,mask) \
((X) = ((X) & ~(mask)) | ((new) & (mask)))
 
#define SAFE_MASK (0xDD5)
#define RETURN_MASK (0xDFF)
 
 
asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs)
{
if (!current->tss.vm86_info) {
printk("no vm86_info: BAD\n");
do_exit(SIGSEGV);
}
set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->tss.v86mask);
memcpy_tofs(&current->tss.vm86_info->regs,regs,sizeof(*regs));
put_fs_long(current->tss.screen_bitmap,&current->tss.vm86_info->screen_bitmap);
current->tss.esp0 = current->saved_kernel_stack;
current->saved_kernel_stack = 0;
return KVM86->regs32;
}
 
static void mark_screen_rdonly(struct task_struct * tsk)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
int i;
 
pgd = pgd_offset(tsk->mm, 0xA0000);
if (pgd_none(*pgd))
return;
if (pgd_bad(*pgd)) {
printk("vm86: bad pgd entry [%p]:%08lx\n", pgd, pgd_val(*pgd));
pgd_clear(pgd);
return;
}
pmd = pmd_offset(pgd, 0xA0000);
if (pmd_none(*pmd))
return;
if (pmd_bad(*pmd)) {
printk("vm86: bad pmd entry [%p]:%08lx\n", pmd, pmd_val(*pmd));
pmd_clear(pmd);
return;
}
pte = pte_offset(pmd, 0xA0000);
for (i = 0; i < 32; i++) {
if (pte_present(*pte))
set_pte(pte, pte_wrprotect(*pte));
pte++;
}
flush_tlb();
}
 
 
 
static int do_vm86_irq_handling(int subfunction, int irqnumber);
static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk);
 
asmlinkage int sys_vm86old(struct vm86_struct * v86)
{
struct kernel_vm86_struct info; /* declare this _on top_,
* this avoids wasting of stack space.
* This remains on the stack until we
* return to 32 bit user space.
*/
struct task_struct *tsk = current;
int error;
 
if (tsk->saved_kernel_stack)
return -EPERM;
/* v86 must be readable (now) and writable (for save_v86_state) */
error = verify_area(VERIFY_WRITE,v86,sizeof(*v86));
if (error)
return error;
memcpy_fromfs(&info,v86,sizeof(struct vm86_struct));
memset(&info.vm86plus, 0, (int)&info.regs32 - (int)&info.vm86plus);
info.regs32 = (struct pt_regs *) &v86;
tsk->tss.vm86_info = v86;
do_sys_vm86(&info, tsk);
return 0; /* we never return here */
}
 
 
asmlinkage int sys_vm86(unsigned long subfunction, struct vm86plus_struct * v86)
{
struct kernel_vm86_struct info; /* declare this _on top_,
* this avoids wasting of stack space.
* This remains on the stack until we
* return to 32 bit user space.
*/
struct task_struct *tsk = current;
int error;
 
switch (subfunction) {
case VM86_REQUEST_IRQ:
case VM86_FREE_IRQ:
case VM86_GET_IRQ_BITS:
case VM86_GET_AND_RESET_IRQ:
return do_vm86_irq_handling(subfunction,(int)v86);
case VM86_PLUS_INSTALL_CHECK:
/* NOTE: on old vm86 stuff this will return the error
from verify_area(), because the subfunction is
interpreted as (invalid) address to vm86_struct.
So the installation check works.
*/
return 0;
}
 
/* we come here only for functions VM86_ENTER, VM86_ENTER_NO_BYPASS */
if (tsk->saved_kernel_stack)
return -EPERM;
/* v86 must be readable (now) and writable (for save_v86_state) */
error = verify_area(VERIFY_WRITE,v86,sizeof(struct vm86plus_struct));
if (error)
return error;
memcpy_fromfs(&info,v86,sizeof(struct vm86plus_struct));
info.regs32 = (struct pt_regs *) &subfunction;
info.vm86plus.is_vm86pus = 1;
tsk->tss.vm86_info = (struct vm86_struct *)v86;
do_sys_vm86(&info, tsk);
return 0; /* we never return here */
}
 
 
static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk)
{
/*
* make sure the vm86() system call doesn't try to do anything silly
*/
info->regs.__null_ds = 0;
info->regs.__null_es = 0;
info->regs.__null_fs = 0;
info->regs.__null_gs = 0;
/*
* The eflags register is also special: we cannot trust that the user
* has set it up safely, so this makes sure interrupt etc flags are
* inherited from protected mode.
*/
VEFLAGS = info->regs.eflags;
info->regs.eflags &= SAFE_MASK;
info->regs.eflags |= info->regs32->eflags & ~SAFE_MASK;
info->regs.eflags |= VM_MASK;
 
switch (info->cpu_type) {
case CPU_286:
tsk->tss.v86mask = 0;
break;
case CPU_386:
tsk->tss.v86mask = NT_MASK | IOPL_MASK;
break;
case CPU_486:
tsk->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK;
break;
default:
tsk->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
break;
}
 
/*
* Save old state, set default return value (%eax) to 0
*/
info->regs32->eax = 0;
tsk->saved_kernel_stack = tsk->tss.esp0;
tsk->tss.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
 
tsk->tss.screen_bitmap = info->screen_bitmap;
if (info->flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(tsk);
__asm__ __volatile__(
"movl %0,%%esp\n\t"
"jmp ret_from_sys_call"
: /* no outputs */
:"r" (&info->regs));
/* we never return here */
}
 
static inline void return_to_32bit(struct vm86_regs * regs16, int retval)
{
struct pt_regs * regs32;
 
regs32 = save_v86_state(regs16);
regs32->eax = retval;
__asm__ __volatile__("movl %0,%%esp\n\t"
"jmp ret_from_sys_call"
: : "r" (regs32));
}
 
static inline void set_IF(struct vm86_regs * regs)
{
VEFLAGS |= VIF_MASK;
if (VEFLAGS & VIP_MASK)
return_to_32bit(regs, VM86_STI);
}
 
static inline void clear_IF(struct vm86_regs * regs)
{
VEFLAGS &= ~VIF_MASK;
}
 
static inline void clear_TF(struct vm86_regs * regs)
{
regs->eflags &= ~TF_MASK;
}
 
static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs)
{
set_flags(VEFLAGS, eflags, current->tss.v86mask);
set_flags(regs->eflags, eflags, SAFE_MASK);
if (eflags & IF_MASK)
set_IF(regs);
}
 
static inline void set_vflags_short(unsigned short flags, struct vm86_regs * regs)
{
set_flags(VFLAGS, flags, current->tss.v86mask);
set_flags(regs->eflags, flags, SAFE_MASK);
if (flags & IF_MASK)
set_IF(regs);
}
 
static inline unsigned long get_vflags(struct vm86_regs * regs)
{
unsigned long flags = regs->eflags & RETURN_MASK;
 
if (VEFLAGS & VIF_MASK)
flags |= IF_MASK;
return flags | (VEFLAGS & current->tss.v86mask);
}
 
static inline int is_revectored(int nr, struct revectored_struct * bitmap)
{
__asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
:"=r" (nr)
:"m" (*bitmap),"r" (nr));
return nr;
}
 
/*
* Boy are these ugly, but we need to do the correct 16-bit arithmetic.
* Gcc makes a mess of it, so we do it inline and use non-obvious calling
* conventions..
*/
#define pushb(base, ptr, val) \
__asm__ __volatile__( \
"decw %w0\n\t" \
"movb %2,%%fs:0(%1,%0)" \
: "=r" (ptr) \
: "r" (base), "q" (val), "0" (ptr))
 
#define pushw(base, ptr, val) \
__asm__ __volatile__( \
"decw %w0\n\t" \
"movb %h2,%%fs:0(%1,%0)\n\t" \
"decw %w0\n\t" \
"movb %b2,%%fs:0(%1,%0)" \
: "=r" (ptr) \
: "r" (base), "q" (val), "0" (ptr))
 
#define pushl(base, ptr, val) \
__asm__ __volatile__( \
"decw %w0\n\t" \
"rorl $16,%2\n\t" \
"movb %h2,%%fs:0(%1,%0)\n\t" \
"decw %w0\n\t" \
"movb %b2,%%fs:0(%1,%0)\n\t" \
"decw %w0\n\t" \
"rorl $16,%2\n\t" \
"movb %h2,%%fs:0(%1,%0)\n\t" \
"decw %w0\n\t" \
"movb %b2,%%fs:0(%1,%0)" \
: "=r" (ptr) \
: "r" (base), "q" (val), "0" (ptr))
 
#define popb(base, ptr) \
({ unsigned long __res; \
__asm__ __volatile__( \
"movb %%fs:0(%1,%0),%b2\n\t" \
"incw %w0" \
: "=r" (ptr), "=r" (base), "=q" (__res) \
: "0" (ptr), "1" (base), "2" (0)); \
__res; })
 
#define popw(base, ptr) \
({ unsigned long __res; \
__asm__ __volatile__( \
"movb %%fs:0(%1,%0),%b2\n\t" \
"incw %w0\n\t" \
"movb %%fs:0(%1,%0),%h2\n\t" \
"incw %w0" \
: "=r" (ptr), "=r" (base), "=q" (__res) \
: "0" (ptr), "1" (base), "2" (0)); \
__res; })
 
#define popl(base, ptr) \
({ unsigned long __res; \
__asm__ __volatile__( \
"movb %%fs:0(%1,%0),%b2\n\t" \
"incw %w0\n\t" \
"movb %%fs:0(%1,%0),%h2\n\t" \
"incw %w0\n\t" \
"rorl $16,%2\n\t" \
"movb %%fs:0(%1,%0),%b2\n\t" \
"incw %w0\n\t" \
"movb %%fs:0(%1,%0),%h2\n\t" \
"incw %w0\n\t" \
"rorl $16,%2" \
: "=r" (ptr), "=r" (base), "=q" (__res) \
: "0" (ptr), "1" (base)); \
__res; })
 
static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp)
{
unsigned long *intr_ptr, segoffs;
 
if (regs->cs == BIOSSEG)
goto cannot_handle;
if (is_revectored(i, &KVM86->int_revectored))
goto cannot_handle;
if (i==0x21 && is_revectored(AH(regs),&KVM86->int21_revectored))
goto cannot_handle;
intr_ptr = (unsigned long *) (i << 2);
if (verify_area(VERIFY_READ, intr_ptr, 4) < 0)
goto cannot_handle;
segoffs = get_fs_long(intr_ptr);
if ((segoffs >> 16) == BIOSSEG)
goto cannot_handle;
pushw(ssp, sp, get_vflags(regs));
pushw(ssp, sp, regs->cs);
pushw(ssp, sp, IP(regs));
regs->cs = segoffs >> 16;
SP(regs) -= 6;
IP(regs) = segoffs & 0xffff;
clear_TF(regs);
clear_IF(regs);
return;
 
cannot_handle:
return_to_32bit(regs, VM86_INTx + (i << 8));
}
 
 
 
int handle_vm86_trap(struct vm86_regs * regs, long error_code, int trapno)
{
if (VMPI.is_vm86pus) {
if ( (trapno==3) || (trapno==1) )
return_to_32bit(regs, VM86_TRAP + (trapno << 8));
do_int(regs, trapno, (unsigned char *) (regs->ss << 4), SP(regs));
return 0;
}
if (trapno !=1)
return 1; /* we let this handle by the calling routine */
if (current->flags & PF_PTRACED)
current->blocked &= ~(1 << (SIGTRAP-1));
send_sig(SIGTRAP, current, 1);
current->tss.trap_no = trapno;
current->tss.error_code = error_code;
return 0;
}
 
 
void handle_vm86_fault(struct vm86_regs * regs, long error_code)
{
unsigned char *csp, *ssp;
unsigned long ip, sp;
 
#define CHECK_IF_IN_TRAP \
if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
pushw(ssp,sp,popw(ssp,sp) | TF_MASK);
#define VM86_FAULT_RETURN \
if (VMPI.force_return_for_pic && (VEFLAGS & IF_MASK)) \
return_to_32bit(regs, VM86_PICRETURN); \
return;
csp = (unsigned char *) (regs->cs << 4);
ssp = (unsigned char *) (regs->ss << 4);
sp = SP(regs);
ip = IP(regs);
 
switch (popb(csp, ip)) {
 
/* operand size override */
case 0x66:
switch (popb(csp, ip)) {
 
/* pushfd */
case 0x9c:
SP(regs) -= 4;
IP(regs) += 2;
pushl(ssp, sp, get_vflags(regs));
VM86_FAULT_RETURN;
 
/* popfd */
case 0x9d:
SP(regs) += 4;
IP(regs) += 2;
CHECK_IF_IN_TRAP
set_vflags_long(popl(ssp, sp), regs);
VM86_FAULT_RETURN;
 
/* iretd */
case 0xcf:
SP(regs) += 12;
IP(regs) = (unsigned short)popl(ssp, sp);
regs->cs = (unsigned short)popl(ssp, sp);
CHECK_IF_IN_TRAP
set_vflags_long(popl(ssp, sp), regs);
VM86_FAULT_RETURN;
/* need this to avoid a fallthrough */
default:
return_to_32bit(regs, VM86_UNKNOWN);
}
 
/* pushf */
case 0x9c:
SP(regs) -= 2;
IP(regs)++;
pushw(ssp, sp, get_vflags(regs));
VM86_FAULT_RETURN;
 
/* popf */
case 0x9d:
SP(regs) += 2;
IP(regs)++;
CHECK_IF_IN_TRAP
set_vflags_short(popw(ssp, sp), regs);
VM86_FAULT_RETURN;
 
/* int xx */
case 0xcd: {
int intno=popb(csp, ip);
IP(regs) += 2;
if (VMPI.vm86dbg_active) {
if ( (1 << (intno &7)) & VMPI.vm86dbg_intxxtab[intno >> 3] )
return_to_32bit(regs, VM86_INTx + (intno << 8));
}
do_int(regs, intno, ssp, sp);
return;
}
 
/* iret */
case 0xcf:
SP(regs) += 6;
IP(regs) = popw(ssp, sp);
regs->cs = popw(ssp, sp);
CHECK_IF_IN_TRAP
set_vflags_short(popw(ssp, sp), regs);
VM86_FAULT_RETURN;
 
/* cli */
case 0xfa:
IP(regs)++;
clear_IF(regs);
VM86_FAULT_RETURN;
 
/* sti */
/*
* Damn. This is incorrect: the 'sti' instruction should actually
* enable interrupts after the /next/ instruction. Not good.
*
* Probably needs some horsing around with the TF flag. Aiee..
*/
case 0xfb:
IP(regs)++;
set_IF(regs);
VM86_FAULT_RETURN;
 
default:
return_to_32bit(regs, VM86_UNKNOWN);
}
}
 
/* ---------------- vm86 special IRQ passing stuff ----------------- */
 
#define VM86_IRQNAME "vm86irq"
 
static struct vm86_irqs {
struct task_struct *tsk;
int sig;
} vm86_irqs[16] = {{0},};
static int irqbits=0;
 
#define ALLOWED_SIGS ( 1 /* 0 = don't send a signal */ \
| (1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGIO) | (1 << SIGURG) \
| (1 << SIGUNUSED) )
static void irq_handler(int intno, void *dev_id, struct pt_regs * regs) {
int irq_bit;
unsigned long flags;
save_flags(flags);
cli();
irq_bit = 1 << intno;
if ((irqbits & irq_bit) || ! vm86_irqs[intno].tsk) {
restore_flags(flags);
return;
}
irqbits |= irq_bit;
if (vm86_irqs[intno].sig)
send_sig(vm86_irqs[intno].sig, vm86_irqs[intno].tsk, 1);
/* else user will poll for IRQs */
restore_flags(flags);
}
 
static inline void free_vm86_irq(int irqnumber)
{
free_irq(irqnumber,0);
vm86_irqs[irqnumber].tsk = 0;
irqbits &= ~(1 << irqnumber);
}
 
static inline int task_valid(struct task_struct *tsk)
{
struct task_struct *p;
 
for_each_task(p) {
if ((p == tsk) && (p->sig)) return 1;
}
return 0;
}
 
static inline void handle_irq_zombies(void)
{
int i;
for (i=3; i<16; i++) {
if (vm86_irqs[i].tsk) {
if (task_valid(vm86_irqs[i].tsk)) continue;
free_vm86_irq(i);
}
}
}
 
static inline int get_and_reset_irq(int irqnumber)
{
int bit;
unsigned long flags;
if ( (irqnumber<3) || (irqnumber>15) ) return 0;
if (vm86_irqs[irqnumber].tsk != current) return 0;
save_flags(flags);
cli();
bit = irqbits & (1 << irqnumber);
irqbits &= ~bit;
restore_flags(flags);
return bit;
}
 
 
static int do_vm86_irq_handling(int subfunction, int irqnumber)
{
int ret;
switch (subfunction) {
case VM86_GET_AND_RESET_IRQ: {
return get_and_reset_irq(irqnumber);
}
case VM86_GET_IRQ_BITS: {
return irqbits;
}
case VM86_REQUEST_IRQ: {
int sig = irqnumber >> 8;
int irq = irqnumber & 255;
handle_irq_zombies();
if (!suser() || securelevel > 0) return -EPERM;
if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM;
if ( (irq<3) || (irq>15) ) return -EPERM;
if (vm86_irqs[irq].tsk) return -EPERM;
ret = request_irq(irq, &irq_handler, 0, VM86_IRQNAME, 0);
if (ret) return ret;
vm86_irqs[irq].sig = sig;
vm86_irqs[irq].tsk = current;
return irq;
}
case VM86_FREE_IRQ: {
handle_irq_zombies();
if ( (irqnumber<3) || (irqnumber>15) ) return -EPERM;
if (!vm86_irqs[irqnumber].tsk) return 0;
if (vm86_irqs[irqnumber].tsk != current) return -EPERM;
free_vm86_irq(irqnumber);
return 0;
}
}
return -EINVAL;
}
 
/setup.c
0,0 → 1,553
/*
* linux/arch/i386/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* This file handles the architecture-dependent parts of initialization
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/ldt.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/config.h>
#ifdef CONFIG_APM
#include <linux/apm_bios.h>
#endif
#ifdef CONFIG_BLK_DEV_RAM
#include <linux/blk.h>
#endif
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/smp.h>
#include <asm/io.h>
 
/*
* Tell us the machine setup..
*/
char hard_math = 0; /* set by kernel/head.S */
char x86 = 0; /* set by kernel/head.S to 3..6 */
char x86_model = 0; /* set by kernel/head.S */
char x86_mask = 0; /* set by kernel/head.S */
int x86_capability = 0; /* set by kernel/head.S */
int x86_ext_capability = 0; /* newer CPUs have this */
int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */
int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */
int have_cpuid = 0; /* set if CPUID instruction works */
int ext_cpuid = 0; /* if != 0, highest available CPUID value */
 
char x86_vendor_id[13] = "GenuineIntel";/* default */
 
static char *Cx86_step = "unknown"; /* stepping info for Cyrix CPUs */
 
static unsigned char Cx86_mult = 0; /* clock multiplier for Cyrix CPUs */
 
static const char *x86_clkmult[] = {
"unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5",
"6", "6.5", "7", "7.5", "8"
};
 
char ignore_irq13 = 0; /* set if exception 16 works */
char wp_works_ok = -1; /* set if paging hardware honours WP */
char hlt_works_ok = 1; /* set if the "hlt" instruction works */
 
/*
* Bus types ..
*/
int EISA_bus = 0;
 
/*
* Setup options
*/
struct drive_info_struct { char dummy[32]; } drive_info;
struct screen_info screen_info;
#ifdef CONFIG_APM
struct apm_bios_info apm_bios_info;
#endif
 
unsigned char aux_device_present;
 
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
#endif
 
extern int root_mountflags;
extern int _etext, _edata, _end;
 
extern char empty_zero_page[PAGE_SIZE];
 
/*
* This is set up by the setup-routine at boot-time
*/
#define PARAM empty_zero_page
#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
#ifndef STANDARD_MEMORY_BIOS_CALL
#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
#endif
#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
#define KERNEL_START (*(unsigned long *) (PARAM+0x214))
#define INITRD_START (*(unsigned long *) (PARAM+0x218))
#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
#define COMMAND_LINE ((char *) (PARAM+2048))
#define COMMAND_LINE_SIZE 256
 
#define RAMDISK_IMAGE_START_MASK 0x07FF
#define RAMDISK_PROMPT_FLAG 0x8000
#define RAMDISK_LOAD_FLAG 0x4000
 
static char command_line[COMMAND_LINE_SIZE] = { 0, };
char saved_command_line[COMMAND_LINE_SIZE];
 
void setup_arch(char **cmdline_p,
unsigned long * memory_start_p, unsigned long * memory_end_p)
{
unsigned long memory_start, memory_end;
#ifndef STANDARD_MEMORY_BIOS_CALL
unsigned long memory_alt_end;
#endif
char c = ' ', *to = command_line, *from = COMMAND_LINE;
int len = 0;
static unsigned char smptrap=0;
 
if(smptrap==1)
{
return;
}
smptrap=1;
 
ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
drive_info = DRIVE_INFO;
screen_info = SCREEN_INFO;
#ifdef CONFIG_APM
apm_bios_info = APM_BIOS_INFO;
#endif
aux_device_present = AUX_DEVICE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
#ifndef STANDARD_MEMORY_BIOS_CALL
memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
if (memory_alt_end > memory_end) {
printk("Memory: sized by int13 0e801h\n");
memory_end = memory_alt_end;
}
else
printk("Memory: sized by int13 088h\n");
#endif
memory_end &= PAGE_MASK;
#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
#endif
#ifdef CONFIG_MAX_16M
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
#endif
 
/*
* The CONFIG_MAX_MEMSIZE sanity checker.
*/
if (memory_end > (CONFIG_MAX_MEMSIZE-128)*1024*1024)
{
memory_end = (CONFIG_MAX_MEMSIZE-128)*1024*1024;
printk(KERN_WARNING "ONLY %dMB RAM will be used, see Documentation/more-than-900MB-RAM.txt!.\n", CONFIG_MAX_MEMSIZE-128);
udelay(3*1000*1000);
}
if (!MOUNT_ROOT_RDONLY)
root_mountflags &= ~MS_RDONLY;
memory_start = (unsigned long) &_end;
init_task.mm->start_code = TASK_SIZE;
init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata;
init_task.mm->brk = TASK_SIZE + (unsigned long) &_end;
 
/* Save unparsed command line copy for /proc/cmdline */
memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
 
for (;;) {
/*
* "mem=nopentium" disables the 4MB page tables.
* "mem=XXX[kKmM]" overrides the BIOS-reported
* memory size
*/
if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") {
if (to != command_line) to--;
if (!memcmp(from+4, "nopentium", 9)) {
from += 9+4;
x86_capability &= ~8;
} else {
memory_end = simple_strtoul(from+4, &from, 0);
if ( *from == 'K' || *from == 'k' ) {
memory_end = memory_end << 10;
from++;
} else if ( *from == 'M' || *from == 'm' ) {
memory_end = memory_end << 20;
from++;
}
}
}
c = *(from++);
if (!c)
break;
if (COMMAND_LINE_SIZE <= ++len)
break;
*(to++) = c;
}
*to = '\0';
*cmdline_p = command_line;
*memory_start_p = memory_start;
*memory_end_p = memory_end;
 
#ifdef CONFIG_BLK_DEV_INITRD
if (LOADER_TYPE) {
initrd_start = INITRD_START;
initrd_end = INITRD_START+INITRD_SIZE;
if (initrd_end > memory_end) {
printk("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\ndisabling initrd\n",
initrd_end,memory_end);
initrd_start = 0;
}
}
#endif
 
/* request io space for devices used on all i[345]86 PC'S */
request_region(0x00,0x20,"dma1");
request_region(0x40,0x20,"timer");
request_region(0x80,0x20,"dma page reg");
request_region(0xc0,0x20,"dma2");
request_region(0xf0,0x10,"npu");
}
 
static const char * IDTmodel(void)
/* Right now IDT has a single CPU model in production: the C6.
* Adjust this when IDT/Centaur comes out with a new CPU model.
* Stepping information is correctly reported in x86_mask.
*/
{
static const char *model[] = {
"C6", "C6-3D"
};
return model[0];
}
 
static const char * Cx86model(void)
/* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1
* mechanism to figure out the model, bus clock multiplier and stepping.
* For the newest CPUs (GXm and MXi) we use the Extended CPUID function.
*/
{
unsigned char nr6x86 = 0;
unsigned char cx_dir0 = 0; /* Model and bus clock multiplier */
unsigned char cx_dir1 = 0; /* Stepping info */
unsigned int flags;
static const char *model[] = {
"unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX",
"M II"
};
if (x86_model == -1) { /* is this an old Cx486 without DIR0/DIR1? */
nr6x86 = 1; /* Cx486 */
Cx86_mult = 0; /* unknown multiplier */
}
else {
 
/* Get DIR0, DIR1 since all other Cyrix CPUs have them */
save_flags(flags);
cli();
cx_dir0 = getCx86(CX86_DIR0); /* we use the access macros */
cx_dir1 = getCx86(CX86_DIR1); /* defined in processor.h */
restore_flags(flags);
/* Now cook; the recipe is by Channing Corn, from Cyrix.
* We do the same thing for each generation: we work out
* the model, multiplier and stepping.
*/
if (cx_dir0 < 0x20) {
nr6x86 = 1; /* Cx486 */
Cx86_mult = 0; /* unknown multiplier */
sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
}
if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) {
nr6x86 = 2; /* 5x86 */
Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */
sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
}
if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) {
nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4); /* 6x86(L) */
Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */
sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f);
}
if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) {
if (x86 == 4) { /* MediaGX */
nr6x86 = 3;
Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7); /* either 3x or 4x */
switch (cx_dir1 >> 4) {
case (0) :
case (1) :
sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f);
break;
case (2) :
sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f);
break;
default :
break;
}
} /* endif MediaGX */
if (x86 == 5) { /* GXm */
char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */
ext_cpuid = 0x80000005; /* available */
Cx86_mult = GXm_mult[cx_dir0 & 0x0f];
sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f);
} /* endif GXm */
}
if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) {
nr6x86 = ((cx_dir1 > 7) ? 7 : 6); /* 6x86Mx or M II */
Cx86_mult = (cx_dir0 & 0x07) + 2; /* 2 to 5 in 0.5 steps */
if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10;
sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
}
}
x86_mask = 1; /* we don't use it, but has to be set to something */
return model[nr6x86];
}
 
struct cpu_model_info {
int cpu_x86;
char *model_names[16];
};
 
static struct cpu_model_info amd_models[] = {
{ 4,
{ NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
"DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
{ 5,
{ "K5/SSA5 (PR-75, PR-90, PR-100)"}},
};
 
static const char * AMDmodel(void)
{
const char *p=NULL;
int i;
if ((x86_model == 0) || (x86 == 4)) {
for (i=0; i<sizeof(amd_models)/sizeof(struct cpu_model_info); i++)
if (amd_models[i].cpu_x86 == x86) {
p = amd_models[i].model_names[(int)x86_model];
break;
}
}
else ext_cpuid = 0x80000005; /* available */
return p;
}
 
static struct cpu_model_info intel_models[] = {
{ 4,
{ "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL",
"486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL,
NULL, NULL, NULL, NULL, NULL }},
{ 5,
{ "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+",
"OverDrive PODP5V83", "Pentium MMX", NULL, NULL,
"Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL,
NULL, NULL, NULL, NULL }},
{ 6,
{ "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)",
NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL }},
};
 
static const char * Intelmodel(void)
{
const char *p = "386 SX/DX"; /* default to a 386 */
int i;
/*
* Old 486SX has no CPU ID. Set the model to 2 for this
* case.
*/
if( x86==4 && x86_model == 0 && hard_math == 0)
x86_model = 2;
for (i=0; i<sizeof(intel_models)/sizeof(struct cpu_model_info); i++)
if (intel_models[i].cpu_x86 == x86) {
p = intel_models[i].model_names[(int)x86_model];
break;
}
return p;
}
/* Recent Intel CPUs have an EEPROM and a ROM with CPU information. We'll use
* this information in future versions of this code.
* AMD and more recently Cyrix have decided to standardize on an extended
* cpuid mechanism for their CPUs.
*/
static const char * get_cpu_mkt_name(void)
{
static char mktbuf[48];
int dummy;
unsigned int *v;
v = (unsigned int *) mktbuf;
cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); /* name, flags */
cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
cpuid(0x80000001, &dummy, &dummy, &dummy, &x86_ext_capability);
return mktbuf;
}
 
static const char * getmodel(void)
/* Default is Intel. We disregard Nexgen processors. */
{
const char *p = NULL;
if (strcmp(x86_vendor_id, "AuthenticAMD") == 0) /* AuthenticAMD */
p = AMDmodel();
else if (strcmp(x86_vendor_id, "CyrixInstead") == 0) /* CyrixInstead */
p = Cx86model();
else if (strcmp(x86_vendor_id, "CentaurHauls") == 0) /* CentaurHauls */
p = IDTmodel();
/* This isnt quite right */
else if (strcmp(x86_vendor_id, "UMC UMC UMC ") == 0) /* UMC */
p = Intelmodel();
else /* default - this could be anyone */
p = Intelmodel();
if (ext_cpuid)
return get_cpu_mkt_name();
else
return p;
}
 
int get_cpuinfo(char * buffer)
{
int i, len = 0;
static const char *x86_cap_flags[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
"16", "17", "18", "19", "20", "21", "22", "mmx",
"24", "25", "26", "27", "28", "29", "30", "31"
};
static const char *x86_ext_cap_flags[] = {
"fpu","vme", "de", "pse", "tsc", "msr", "6", "mce",
"cx8", "9", "10", "syscr", "12", "pge", "14", "cmov",
"fpcmov", "17", "psn", "19", "20", "21", "22", "mmx",
"emmx", "25", "26", "27", "28", "29", "30", "3dnow"
};
#ifdef __SMP__
int n;
 
#define CD(X) (cpu_data[n].X)
/* SMP has the wrong name for loops_per_sec */
#define loops_per_sec udelay_val
#define CPUN n
 
for ( n = 0 ; n < 32 ; n++ ) {
if ( cpu_present_map & (1<<n) ) {
if (len) buffer[len++] = '\n';
 
#else
#define CD(X) (X)
#define CPUN 0
#endif
 
len += sprintf(buffer+len,"processor\t: %d\n"
"cpu\t\t: %c86\n"
"model\t\t: %s",
CPUN,
CD(x86)+'0',
getmodel());
len += sprintf(buffer+len,
"\nvendor_id\t: %s\n",
x86_vendor_id);
if (CD(x86_mask) || have_cpuid)
if ((strncmp(x86_vendor_id, "Au", 2) == 0)
&& (x86_model >= 6)) {
len += sprintf(buffer+len,
"stepping\t: %c\n",
x86_mask + 'A');
}
else if (strncmp(x86_vendor_id, "Cy", 2) == 0) {
len += sprintf(buffer+len,
"stepping\t: %s, core/bus clock ratio: %sx\n",
Cx86_step, x86_clkmult[Cx86_mult]);
}
else {
len += sprintf(buffer+len,
"stepping\t: %d\n",
CD(x86_mask));
}
else
len += sprintf(buffer+len,
"stepping\t: unknown\n");
len += sprintf(buffer+len,
"fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
"f00f_bug\t: %s\n"
"fpu\t\t: %s\n"
"fpu_exception\t: %s\n"
"cpuid\t\t: %s\n"
"wp\t\t: %s\n"
"flags\t\t:",
CD(fdiv_bug) ? "yes" : "no",
CD(hlt_works_ok) ? "no" : "yes",
pentium_f00f_bug ? "yes" : "no",
CD(hard_math) ? "yes" : "no",
(CD(hard_math) && ignore_irq13)
? "yes" : "no",
CD(have_cpuid) ? "yes" : "no",
CD(wp_works_ok) ? "yes" : "no");
for ( i = 0 ; i < 32 ; i++ ) {
if ( CD(x86_capability) & (1 << i) ) {
len += sprintf(buffer+len, " %s",
x86_cap_flags[i]);
}
else if ( CD(x86_ext_capability) & (1 << i) ) {
len += sprintf(buffer+len, " %s",
x86_ext_cap_flags[i]);
}
}
len += sprintf(buffer+len,
"\nbogomips\t: %lu.%02lu\n",
CD(loops_per_sec+2500)/500000,
(CD(loops_per_sec+2500)/5000) % 100);
#ifdef __SMP__
}
}
#endif
return len;
}
/bios32.c
0,0 → 1,914
/*
* bios32.c - BIOS32, PCI BIOS functions.
*
* $Id: bios32.c,v 1.1 2005-12-20 09:42:23 jcastillo Exp $
*
* Sponsored by
* iX Multiuser Multitasking Magazine
* Hannover, Germany
* hm@ix.de
*
* Copyright 1993, 1994 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* Drew@Colorado.EDU
* +1 (303) 786-7975
*
* For more information, please consult
*
* PCI BIOS Specification Revision
* PCI Local Bus Specification
* PCI System Design Guide
*
* PCI Special Interest Group
* M/S HF3-15A
* 5200 N.E. Elam Young Parkway
* Hillsboro, Oregon 97124-6497
* +1 (503) 696-2000
* +1 (800) 433-5177
*
* Manuals are $25 each or $50 for all three, plus $7 shipping
* within the United States, $35 abroad.
*
*
* CHANGELOG :
* Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
* Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
*
* Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic
* Potter, potter@cao-vlsi.ibp.fr
*
* Jan 10, 1995 : Modified to store the information about configured pci
* devices into a list, which can be accessed via /proc/pci by
* Curtis Varner, cvarner@cs.ucr.edu
*
* Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
* Alpha version. Intel & UMC chipset support only.
*
* Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
* moved to drivers/pci/pci.c.
*
* Dec 7, 1996 : Added support for direct configuration access of boards
* with Intel compatible access schemes (tsbogend@alpha.franken.de)
*
* Feb 3, 1997 : Set internal functions to static, save/restore flags
* avoid dead locks reading broken PCI BIOS, werner@suse.de
*
* Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
* (mj@atrey.karlin.mff.cuni.cz)
*
* May 7, 1997 : Added some missing cli()'s. [mj]
*
* Jun 20, 1997 : Corrected problems in "conf1" type accesses.
* (paubert@iram.es)
*/
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/bios32.h>
#include <linux/pci.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/io.h>
 
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
#define PCIBIOS_READ_CONFIG_BYTE 0xb108
#define PCIBIOS_READ_CONFIG_WORD 0xb109
#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
 
 
/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
 
/* PCI signature: "PCI " */
#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
 
/* PCI service signature: "$PCI" */
#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
 
/*
* This is the standard structure used to identify the entry point
* to the BIOS32 Service Directory, as documented in
* Standard BIOS 32-bit Service Directory Proposal
* Revision 0.4 May 24, 1993
* Phoenix Technologies Ltd.
* Norwood, MA
* and the PCI BIOS specification.
*/
 
union bios32 {
struct {
unsigned long signature; /* _32_ */
unsigned long entry; /* 32 bit physical address */
unsigned char revision; /* Revision level, 0 */
unsigned char length; /* Length in paragraphs should be 01 */
unsigned char checksum; /* All bytes must add up to zero */
unsigned char reserved[5]; /* Must be zero */
} fields;
char chars[16];
};
 
#ifdef CONFIG_PCI
/*
* Physical address of the service directory. I don't know if we're
* allowed to have more than one of these or not, so just in case
* we'll make pcibios_present() take a memory start parameter and store
* the array there.
*/
 
static unsigned long bios32_entry = 0;
static struct {
unsigned long address;
unsigned short segment;
} bios32_indirect = { 0, KERNEL_CS };
 
 
/*
* function table for accessing PCI configuration space
*/
struct pci_access {
int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
};
 
/*
* pointer to selected PCI access function table
*/
static struct pci_access *access_pci = NULL;
 
 
 
/*
* Returns the entry point for the given service, NULL on error
*/
 
static unsigned long bios32_service(unsigned long service)
{
unsigned char return_code; /* %al */
unsigned long address; /* %ebx */
unsigned long length; /* %ecx */
unsigned long entry; /* %edx */
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%edi)"
: "=a" (return_code),
"=b" (address),
"=c" (length),
"=d" (entry)
: "0" (service),
"1" (0),
"D" (&bios32_indirect));
restore_flags(flags);
 
switch (return_code) {
case 0:
return address + entry;
case 0x80: /* Not present */
printk("bios32_service(0x%lx) : not present\n", service);
return 0;
default: /* Shouldn't happen */
printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
service, return_code);
return 0;
}
}
 
static long pcibios_entry = 0;
static struct {
unsigned long address;
unsigned short segment;
} pci_indirect = { 0, KERNEL_CS };
 
 
static int check_pcibios(void)
{
unsigned long signature;
unsigned char present_status;
unsigned char major_revision;
unsigned char minor_revision;
unsigned long flags;
int pack;
 
if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
pci_indirect.address = pcibios_entry;
 
save_flags(flags); cli();
__asm__("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:\tshl $8, %%eax\n\t"
"movw %%bx, %%ax"
: "=d" (signature),
"=a" (pack)
: "1" (PCIBIOS_PCI_BIOS_PRESENT),
"D" (&pci_indirect)
: "bx", "cx");
restore_flags(flags);
 
present_status = (pack >> 16) & 0xff;
major_revision = (pack >> 8) & 0xff;
minor_revision = pack & 0xff;
if (present_status || (signature != PCI_SIGNATURE)) {
printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
" but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
" and signature of 0x%08lx (%c%c%c%c). mail drew@Colorado.EDU\n",
(signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
present_status, signature,
(char) (signature >> 0), (char) (signature >> 8),
(char) (signature >> 16), (char) (signature >> 24));
 
if (signature != PCI_SIGNATURE)
pcibios_entry = 0;
}
if (pcibios_entry) {
printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
major_revision, minor_revision, pcibios_entry);
return 1;
}
}
return 0;
}
 
 
static int pci_bios_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *device_fn)
{
unsigned long bx;
unsigned long ret;
unsigned long flags;
 
save_flags(flags); cli();
__asm__ ("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=b" (bx),
"=a" (ret)
: "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
"c" (class_code),
"S" ((int) index),
"D" (&pci_indirect));
restore_flags(flags);
*bus = (bx >> 8) & 0xff;
*device_fn = bx & 0xff;
return (int) (ret & 0xff00) >> 8;
}
 
 
static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
unsigned short index, unsigned char *bus, unsigned char *device_fn)
{
unsigned short bx;
unsigned short ret;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%edi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=b" (bx),
"=a" (ret)
: "1" (PCIBIOS_FIND_PCI_DEVICE),
"c" (device_id),
"d" (vendor),
"S" ((int) index),
"D" (&pci_indirect));
restore_flags(flags);
*bus = (bx >> 8) & 0xff;
*device_fn = bx & 0xff;
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_read_config_byte(unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (ret)
: "1" (PCIBIOS_READ_CONFIG_BYTE),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_read_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (ret)
: "1" (PCIBIOS_READ_CONFIG_WORD),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_read_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (ret)
: "1" (PCIBIOS_READ_CONFIG_DWORD),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_write_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (ret)
: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
"c" (value),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_write_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (ret)
: "0" (PCIBIOS_WRITE_CONFIG_WORD),
"c" (value),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
static int pci_bios_write_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
unsigned long flags;
 
save_flags(flags); cli();
__asm__("lcall (%%esi)\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (ret)
: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
"c" (value),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
restore_flags(flags);
return (int) (ret & 0xff00) >> 8;
}
 
/*
* function table for BIOS32 access
*/
static struct pci_access pci_bios_access = {
pci_bios_find_device,
pci_bios_find_class,
pci_bios_read_config_byte,
pci_bios_read_config_word,
pci_bios_read_config_dword,
pci_bios_write_config_byte,
pci_bios_write_config_word,
pci_bios_write_config_dword
};
 
 
 
/*
* Given the vendor and device ids, find the n'th instance of that device
* in the system.
*/
static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
unsigned short index, unsigned char *bus,
unsigned char *devfn)
{
unsigned int curr = 0;
struct pci_dev *dev;
 
for (dev = pci_devices; dev; dev = dev->next) {
if (dev->vendor == vendor && dev->device == device_id) {
if (curr == index) {
*devfn = dev->devfn;
*bus = dev->bus->number;
return PCIBIOS_SUCCESSFUL;
}
++curr;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
 
 
/*
* Given the class, find the n'th instance of that device
* in the system.
*/
static int pci_direct_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *devfn)
{
unsigned int curr = 0;
struct pci_dev *dev;
 
for (dev = pci_devices; dev; dev = dev->next) {
if (dev->class == class_code) {
if (curr == index) {
*devfn = dev->devfn;
*bus = dev->bus->number;
return PCIBIOS_SUCCESSFUL;
}
++curr;
}
}
return PCIBIOS_DEVICE_NOT_FOUND;
}
 
/*
* Functions for accessing PCI configuration space with type 1 accesses
*/
#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
 
static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long flags;
 
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
*value = inb(0xCFC + (where&3));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf1_read_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short *value)
{
unsigned long flags;
 
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
*value = inw(0xCFC + (where&2));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long flags;
 
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
*value = inl(0xCFC);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long flags;
 
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
outb(value, 0xCFC + (where&3));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long flags;
 
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
outw(value, 0xCFC + (where&2));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long flags;
 
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
outl(value, 0xCFC);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
#undef CONFIG_CMD
 
/*
* functiontable for type 1
*/
static struct pci_access pci_direct_conf1 = {
pci_direct_find_device,
pci_direct_find_class,
pci_conf1_read_config_byte,
pci_conf1_read_config_word,
pci_conf1_read_config_dword,
pci_conf1_write_config_byte,
pci_conf1_write_config_word,
pci_conf1_write_config_dword
};
 
/*
* Functions for accessing PCI configuration space with type 2 accesses
*/
#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
 
static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char *value)
{
unsigned long flags;
 
if (device_fn & 0x80)
return PCIBIOS_DEVICE_NOT_FOUND;
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
*value = inb(IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short *value)
{
unsigned long flags;
 
if (device_fn & 0x80)
return PCIBIOS_DEVICE_NOT_FOUND;
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
*value = inw(IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int *value)
{
unsigned long flags;
 
if (device_fn & 0x80)
return PCIBIOS_DEVICE_NOT_FOUND;
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
*value = inl (IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned char value)
{
unsigned long flags;
 
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
outb (value, IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned short value)
{
unsigned long flags;
 
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
outw (value, IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned char where, unsigned int value)
{
unsigned long flags;
 
save_flags(flags); cli();
outb (FUNC(device_fn), 0xCF8);
outb (bus, 0xCFA);
outl (value, IOADDR(device_fn,where));
outb (0, 0xCF8);
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
 
#undef IOADDR
#undef FUNC
 
/*
* functiontable for type 2
*/
static struct pci_access pci_direct_conf2 = {
pci_direct_find_device,
pci_direct_find_class,
pci_conf2_read_config_byte,
pci_conf2_read_config_word,
pci_conf2_read_config_dword,
pci_conf2_write_config_byte,
pci_conf2_write_config_word,
pci_conf2_write_config_dword
};
 
 
static struct pci_access *check_direct_pci(void)
{
unsigned int tmp;
unsigned long flags;
 
save_flags(flags); cli();
 
/*
* check if configuration type 1 works
*/
outb (0x01, 0xCFB);
tmp = inl (0xCF8);
outl (0x80000000, 0xCF8);
if (inl (0xCF8) == 0x80000000) {
outl (tmp, 0xCF8);
restore_flags(flags);
printk("pcibios_init: Using configuration type 1\n");
return &pci_direct_conf1;
}
outl (tmp, 0xCF8);
 
/*
* check if configuration type 2 works
*/
outb (0x00, 0xCFB);
outb (0x00, 0xCF8);
outb (0x00, 0xCFA);
if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
restore_flags(flags);
printk("pcibios_init: Using configuration type 2\n");
return &pci_direct_conf2;
}
restore_flags(flags);
printk("pcibios_init: Not supported chipset for direct PCI access !\n");
return NULL;
}
 
 
/*
* access defined pcibios functions via
* the function table
*/
 
int pcibios_present(void)
{
return access_pci ? 1 : 0;
}
 
int pcibios_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *device_fn)
{
if (access_pci && access_pci->find_class)
return access_pci->find_class(class_code, index, bus, device_fn);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_find_device (unsigned short vendor, unsigned short device_id,
unsigned short index, unsigned char *bus, unsigned char *device_fn)
{
if (access_pci && access_pci->find_device)
return access_pci->find_device(vendor, device_id, index, bus, device_fn);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_read_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char *value)
{
if (access_pci && access_pci->read_config_byte)
return access_pci->read_config_byte(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_read_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short *value)
{
if (access_pci && access_pci->read_config_word)
return access_pci->read_config_word(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_read_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int *value)
{
if (access_pci && access_pci->read_config_dword)
return access_pci->read_config_dword(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_write_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char value)
{
if (access_pci && access_pci->write_config_byte)
return access_pci->write_config_byte(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_write_config_word (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned short value)
{
if (access_pci && access_pci->write_config_word)
return access_pci->write_config_word(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
int pcibios_write_config_dword (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned int value)
{
if (access_pci && access_pci->write_config_dword)
return access_pci->write_config_dword(bus, device_fn, where, value);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
 
const char *pcibios_strerror (int error)
{
static char buf[80];
 
switch (error) {
case PCIBIOS_SUCCESSFUL:
return "SUCCESSFUL";
 
case PCIBIOS_FUNC_NOT_SUPPORTED:
return "FUNC_NOT_SUPPORTED";
 
case PCIBIOS_BAD_VENDOR_ID:
return "SUCCESSFUL";
 
case PCIBIOS_DEVICE_NOT_FOUND:
return "DEVICE_NOT_FOUND";
 
case PCIBIOS_BAD_REGISTER_NUMBER:
return "BAD_REGISTER_NUMBER";
 
case PCIBIOS_SET_FAILED:
return "SET_FAILED";
 
case PCIBIOS_BUFFER_TOO_SMALL:
return "BUFFER_TOO_SMALL";
 
default:
sprintf (buf, "UNKNOWN RETURN 0x%x", error);
return buf;
}
}
 
 
unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
{
return mem_start;
}
 
#endif
 
unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
{
#ifdef CONFIG_PCI
union bios32 *check;
unsigned char sum;
int i, length;
 
/*
* Follow the standard procedure for locating the BIOS32 Service
* directory by scanning the permissible address range from
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
*
*/
 
for (check = (union bios32 *) 0xe0000;
check <= (union bios32 *) 0xffff0;
++check) {
if (check->fields.signature != BIOS32_SIGNATURE)
continue;
length = check->fields.length * 16;
if (!length)
continue;
sum = 0;
for (i = 0; i < length ; ++i)
sum += check->chars[i];
if (sum != 0)
continue;
if (check->fields.revision != 0) {
printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
check->fields.revision, check);
continue;
}
printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
if (!bios32_entry) {
if (check->fields.entry >= 0x100000) {
printk("pcibios_init: entry in high memory, trying direct PCI access\n");
access_pci = check_direct_pci();
} else {
bios32_entry = check->fields.entry;
printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
bios32_indirect.address = bios32_entry;
}
}
}
if (bios32_entry && check_pcibios())
access_pci = &pci_bios_access;
#endif
return memory_start;
}
/ksyms.c
0,0 → 1,40
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/user.h>
#include <linux/elfcore.h>
#include <linux/delay.h>
 
#include <asm/semaphore.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
 
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(elf_fpregset_t *);
 
static struct symbol_table arch_symbol_table = {
#include <linux/symtab_begin.h>
/* platform dependent support */
X(x86_capability),
X(dump_thread),
X(dump_fpu),
X(get_pt_regs_for_task),
XNOVERS(__do_delay),
XNOVERS(down_failed),
XNOVERS(down_failed_interruptible),
XNOVERS(up_wakeup),
#ifdef __SMP__
X(apic_reg), /* Needed internally for the I386 inlines */
X(cpu_data),
X(syscall_count),
X(kernel_flag),
X(kernel_counter),
X(active_kernel_processor),
X(smp_invalidate_needed),
#endif
#include <linux/symtab_end.h>
};
 
void arch_syms_export(void)
{
register_symtab(&arch_symbol_table);
}
/ioport.c
0,0 → 1,89
/*
* linux/arch/i386/kernel/ioport.c
*
* This contains the io-permission bitmap code - written by obz, with changes
* by Linus.
*/
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/ioport.h>
 
/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
{
int mask;
unsigned long *bitmap_base = bitmap + (base >> 5);
unsigned short low_index = base & 0x1f;
int length = low_index + extent;
 
if (low_index != 0) {
mask = (~0 << low_index);
if (length < 32)
mask &= ~(~0 << length);
if (new_value)
*bitmap_base++ |= mask;
else
*bitmap_base++ &= ~mask;
length -= 32;
}
 
mask = (new_value ? ~0 : 0);
while (length >= 32) {
*bitmap_base++ = mask;
length -= 32;
}
 
if (length > 0) {
mask = ~(~0 << length);
if (new_value)
*bitmap_base++ |= mask;
else
*bitmap_base++ &= ~mask;
}
}
 
/*
* this changes the io permissions bitmap in the current task.
*/
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
{
if (from + num <= from)
return -EINVAL;
if (from + num > IO_BITMAP_SIZE*32)
return -EINVAL;
if (!suser() || securelevel > 0)
return -EPERM;
 
set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on);
return 0;
}
 
unsigned int *stack;
 
/*
* sys_iopl has to be used when you want to access the IO ports
* beyond the 0x3ff range: to get the full 65536 ports bitmapped
* you'd need 8kB of bitmaps/process, which is a bit excessive.
*
* Here we just change the eflags value on the stack: we allow
* only the super-user to do it. This depends on the stack-layout
* on system-call entry - see also fork() and the signal handling
* code.
*/
asmlinkage int sys_iopl(long ebx,long ecx,long edx,
long esi, long edi, long ebp, long eax, long ds,
long es, long fs, long gs, long orig_eax,
long eip,long cs,long eflags,long esp,long ss)
{
unsigned int level = ebx;
 
if (level > 3)
return -EINVAL;
if (!suser() || securelevel > 0)
return -EPERM;
*(&eflags) = (eflags & 0xffffcfff) | (level << 12);
return 0;
}
/apm.c
0,0 → 1,1269
/* -*- linux-c -*-
* APM BIOS driver for Linux
* Copyright 1994-1999 Stephen Rothwell
* (Stephen.Rothwell@canb.auug.org.au)
* Development of this driver was funded by NEC Australia P/L
* and NEC Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* October 1995, Rik Faith (faith@cs.unc.edu):
* Minor enhancements and updates (to the patch set) for 1.3.x
* Documentation
* January 1996, Rik Faith (faith@cs.unc.edu):
* Make /proc/apm easy to format (bump driver version)
* March 1996, Rik Faith (faith@cs.unc.edu):
* Prohibit APM BIOS calls unless apm_enabled.
* (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
* April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
* Version 1.0 and 1.1
* May 1996, Version 1.2
*
* History:
* 0.6b: first version in official kernel, Linux 1.3.46
* 0.7: changed /proc/apm format, Linux 1.3.58
* 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
* 0.9: only call bios if bios is present, Linux 1.3.72
* 1.0: use fixed device number, consolidate /proc/apm into this file,
* Linux 1.3.85
* 1.1: support user-space standby and suspend, power off after system
* halted, Linux 1.3.98
* 1.2: When resetting RTC after resume, take care so that the time
* is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
* <jtoth@princeton.edu>); improve interaction between
* screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
* 1.2a: Fix OOPs on power off with no APM BIOS
* Jan Echternach <echter@informatik.uni-rostock.de>
*
* Reference:
*
* Intel Corporation, Microsoft Corporation. Advanced Power Management
* (APM) BIOS Interface Specification, Revision 1.1, September 1993.
* Intel Order Number 241704-001. Microsoft Part Number 781-110-X01.
*
* [This document is available free from Intel by calling 800.628.8686 (fax
* 916.356.6100) or 800.548.4725; or via anonymous ftp from
* ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also
* available from Microsoft by calling 206.882.8080.]
*
*/
 
#include <linux/config.h>
#include <linux/module.h>
 
#include <asm/system.h>
#include <asm/segment.h>
 
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/timer.h>
#include <linux/fcntl.h>
#include <linux/malloc.h>
#include <linux/linkage.h>
#ifdef CONFIG_PROC_FS
#include <linux/stat.h>
#include <linux/proc_fs.h>
#endif
#include <linux/miscdevice.h>
#include <linux/apm_bios.h>
 
static struct symbol_table apm_syms = {
#include <linux/symtab_begin.h>
X(apm_register_callback),
X(apm_unregister_callback),
#include <linux/symtab_end.h>
};
 
extern unsigned long get_cmos_time(void);
 
/*
* The apm_bios device is one of the misc char devices.
* This is its minor number.
*/
#define APM_MINOR_DEV 134
 
/* Configurable options:
*
* CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
* This is necessary on the NEC Versa M series, which generates these when
* resuming from SYSTEM SUSPEND. However, enabling this on other laptops
* will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
* USER SUSPEND is ignored -- this may prevent the APM driver from updating
* the system time on a RESUME.
*
* CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of
* the specification: "When disabled, the APM BIOS does not automatically
* power manage devices, enter the Standby State, enter the Suspend State,
* or take power saving steps in response to CPU Idle calls." This driver
* will make CPU Idle calls when Linux is idle (unless this feature is
* turned off -- see below). This should always save battery power, but
* more complicated APM features will be dependent on your BIOS
* implementation. You may need to turn this option off if your computer
* hangs at boot time when using APM support, or if it beeps continuously
* instead of suspending. Turn this off if you have a NEC UltraLite Versa
* 33/C or a Toshiba T400CDT. This is off by default since most machines
* do fine without this feature.
*
* CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
* idle loop. On some machines, this can activate improved power savings,
* such as a slowed CPU clock rate, when the machine is idle. These idle
* call is made after the idle loop has run for some length of time (e.g.,
* 333 mS). On some machines, this will cause a hang at boot time or
* whenever the CPU becomes idle.
*
* CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some
* laptops can use this to turn of the LCD backlight when the VC screen
* blanker blanks the screen. Note that this is only used by the VC screen
* blanker, and probably won't turn off the backlight when using X11. Some
* problems have been reported when using this option with gpm (if you'd
* like to debug this, please do so).
*
* CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist
* on returning multiple suspend/standby events whenever one occurs. We
* really only need one at a time, so just ignore any beyond the first.
* This is probably safe on most laptops.
*
* If you are debugging the APM support for your laptop, note that code for
* all of these options is contained in this file, so you can #define or
* #undef these on the next line to avoid recompiling the whole kernel.
*
*/
 
/* KNOWN PROBLEM MACHINES:
*
* U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
* [Confirmed by TI representative]
* U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
* [Confirmed by BIOS disassembly]
* P: Toshiba 1950S: battery life information only gets updated after resume
*
* Legend: U = unusable with APM patches
* P = partially usable with APM patches
*/
 
/*
* Define to have debug messages.
*/
#undef APM_DEBUG
 
/*
* Define to always call the APM BIOS busy routine even if the clock was
* not slowed by the idle routine.
*/
#define ALWAYS_CALL_BUSY
 
/*
* Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
* should turn interrupts on before it does a 'hlt').
*/
#define APM_NOINTS
 
/*
* Define to make the APM BIOS calls zero all data segment registers (do
* that if an incorrect BIOS implementation will cause a kernel panic if it
* tries to write to arbitrary memory).
*/
#define APM_ZERO_SEGS
 
/*
* Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is
* supposed to provide limit information that it recognizes. Many machines
* do this correctly, but many others do not restrict themselves to their
* claimed limit. When this happens, they will cause a segmentation
* violation in the kernel at boot time. Most BIOS's, however, will
* respect a 64k limit, so we use that. If you want to be pedantic and
* hold your BIOS to its claims, then undefine this.
*/
#define APM_RELAX_SEGMENTS
 
/*
* Need to poll the APM BIOS every second
*/
#define APM_CHECK_TIMEOUT (HZ)
 
/*
* These are the actual BIOS calls in assembler. Depending on
* APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here! Not
* only are interrupts disabled, but all the segment registers (except SS)
* are saved and zeroed this means that if the BIOS tries to reference any
* data without explicitly loading the segment registers, the kernel will
* fault immediately rather than have some unforeseen circumstances for the
* rest of the kernel. And it will be very obvious! :-) Doing this
* depends on CS referring to the same physical memory as DS so that DS can
* be zeroed before the call. Unfortunately, we can't do anything about the
* stack segment/pointer. Also, we tell the compiler that everything could
* change.
*/
#ifdef APM_NOINTS
# define APM_DO_CLI "cli\n\t"
#else
# define APM_DO_CLI
#endif
#ifdef APM_ZERO_SEGS
# define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \
"pushl %%es\n\t" \
"pushl %%fs\n\t" \
"pushl %%gs\n\t" \
"xorl %%edx, %%edx\n\t" \
"mov %%dx, %%ds\n\t" \
"mov %%dx, %%es\n\t" \
"mov %%dx, %%fs\n\t" \
"mov %%dx, %%gs\n\t"
# define APM_DO_RESTORE_SEGS \
"popl %%gs\n\t" \
"popl %%fs\n\t" \
"popl %%es\n\t" \
"popl %%ds\n\t"
#else
# define APM_DO_ZERO_SEGS
# define APM_DO_RESTORE_SEGS
#endif
 
#define APM_BIOS_CALL(error_reg) \
__asm__ __volatile__( \
APM_DO_ZERO_SEGS \
"pushfl\n\t" \
APM_DO_CLI \
"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
"setc %%" # error_reg "\n\t" \
"popfl\n\t" \
APM_DO_RESTORE_SEGS
#define APM_BIOS_CALL_END \
: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
 
#ifdef CONFIG_APM_CPU_IDLE
#define APM_SET_CPU_IDLE(error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x5305) \
APM_BIOS_CALL_END
#endif
 
#define APM_SET_CPU_BUSY(error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x5306) \
APM_BIOS_CALL_END
 
#define APM_SET_POWER_STATE(state, error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x5307), "b" (0x0001), "c" (state) \
APM_BIOS_CALL_END
 
#ifdef CONFIG_APM_DISPLAY_BLANK
#define APM_SET_DISPLAY_POWER_STATE(state, error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x5307), "b" (0x01ff), "c" (state) \
APM_BIOS_CALL_END
#endif
 
#ifdef CONFIG_APM_DO_ENABLE
#define APM_ENABLE_POWER_MANAGEMENT(device, error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x5308), "b" (device), "c" (1) \
APM_BIOS_CALL_END
#endif
 
#define APM_GET_POWER_STATUS(bx, cx, dx, error) \
APM_BIOS_CALL(al) \
: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
: "a" (0x530a), "b" (1) \
APM_BIOS_CALL_END
 
#define APM_GET_EVENT(event, error) \
APM_BIOS_CALL(al) \
: "=a" (error), "=b" (event) \
: "a" (0x530b) \
APM_BIOS_CALL_END
 
#define APM_DRIVER_VERSION(ver, ax, error) \
APM_BIOS_CALL(bl) \
: "=a" (ax), "=b" (error) \
: "a" (0x530e), "b" (0), "c" (ver) \
APM_BIOS_CALL_END
 
#define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
APM_BIOS_CALL(al) \
: "=a" (error) \
: "a" (0x530f), "b" (device), "c" (1) \
APM_BIOS_CALL_END
 
/*
* Forward declarations
*/
static void suspend(void);
static void standby(void);
static void set_time(void);
 
static void check_events(void);
static void do_apm_timer(unsigned long);
 
static int do_open(struct inode *, struct file *);
static void do_release(struct inode *, struct file *);
static int do_read(struct inode *, struct file *, char *, int);
static int do_select(struct inode *, struct file *, int,
select_table *);
static int do_ioctl(struct inode *, struct file *, u_int, u_long);
 
#ifdef CONFIG_PROC_FS
static int apm_get_info(char *, char **, off_t, int, int);
#endif
 
extern int apm_register_callback(int (*)(apm_event_t));
extern void apm_unregister_callback(int (*)(apm_event_t));
 
/*
* Local variables
*/
static asmlinkage struct {
unsigned long offset;
unsigned short segment;
} apm_bios_entry;
static int apm_enabled = 0;
#ifdef CONFIG_APM_CPU_IDLE
static int clock_slowed = 0;
#endif
static int suspends_pending = 0;
static int standbys_pending = 0;
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
static int waiting_for_resume = 0;
#endif
 
static long clock_cmos_diff;
static int got_clock_diff = 0;
 
static struct wait_queue * process_list = NULL;
static struct apm_bios_struct * user_list = NULL;
 
static struct timer_list apm_timer;
 
static char driver_version[] = "1.2";/* no spaces */
 
#ifdef APM_DEBUG
static char * apm_event_name[] = {
"system standby",
"system suspend",
"normal resume",
"critical resume",
"low battery",
"power status change",
"update time",
"critical suspend",
"user standby",
"user suspend",
"system standby resume"
};
#define NR_APM_EVENT_NAME \
(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
#endif
 
static struct file_operations apm_bios_fops = {
NULL, /* lseek */
do_read,
NULL, /* write */
NULL, /* readdir */
do_select,
do_ioctl,
NULL, /* mmap */
do_open,
do_release,
NULL, /* fsync */
NULL /* fasync */
};
 
static struct miscdevice apm_device = {
APM_MINOR_DEV,
"apm",
&apm_bios_fops
};
 
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry apm_proc_entry = {
0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
};
#endif
 
typedef struct callback_list_t {
int (* callback)(apm_event_t);
struct callback_list_t * next;
} callback_list_t;
 
static callback_list_t * callback_list = NULL;
 
typedef struct lookup_t {
int key;
char * msg;
} lookup_t;
 
static const lookup_t error_table[] = {
/* N/A { APM_SUCCESS, "Operation succeeded" }, */
{ APM_DISABLED, "Power management disabled" },
{ APM_CONNECTED, "Real mode interface already connected" },
{ APM_NOT_CONNECTED, "Interface not connected" },
{ APM_16_CONNECTED, "16 bit interface already connected" },
/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */
{ APM_32_CONNECTED, "32 bit interface already connected" },
{ APM_32_UNSUPPORTED, "32 bit interface not supported" },
{ APM_BAD_DEVICE, "Unrecognized device ID" },
{ APM_BAD_PARAM, "Parameter out of range" },
{ APM_NOT_ENGAGED, "Interface not engaged" },
{ APM_BAD_STATE, "Unable to enter requested state" },
/* N/A { APM_NO_EVENTS, "No events pending" }, */
{ APM_NOT_PRESENT, "No APM present" }
};
#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
 
static int apm_driver_version(u_short *val)
{
u_short error;
 
APM_DRIVER_VERSION(*val, *val, error);
 
if (error & 0xff)
return (*val >> 8);
return APM_SUCCESS;
}
 
static int apm_get_event(apm_event_t *event)
{
u_short error;
 
APM_GET_EVENT(*event, error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
 
static int apm_set_power_state(u_short state)
{
u_short error;
APM_SET_POWER_STATE(state, error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
 
#ifdef CONFIG_APM_POWER_OFF
void apm_power_off(void)
{
if (apm_enabled)
(void) apm_set_power_state(APM_STATE_OFF);
}
#endif
 
#ifdef CONFIG_APM_DISPLAY_BLANK
/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
static int apm_set_display_power_state(u_short state)
{
u_short error;
 
APM_SET_DISPLAY_POWER_STATE(state, error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
#endif
 
#ifdef CONFIG_APM_DO_ENABLE
/* Called by apm_setup if apm_enabled will be true. */
static int apm_enable_power_management(void)
{
u_short error;
 
APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
? 0x0001 : 0xffff,
error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
#endif
 
static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
{
u_short error;
 
APM_GET_POWER_STATUS(*status, *bat, *life, error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
 
static int apm_engage_power_management(u_short device)
{
u_short error;
 
APM_ENGAGE_POWER_MANAGEMENT(device, error);
if (error & 0xff)
return (error >> 8);
return APM_SUCCESS;
}
 
static void apm_error(char *str, int err)
{
int i;
 
for (i = 0; i < ERROR_COUNT; i++)
if (error_table[i].key == err) break;
if (i < ERROR_COUNT)
printk("apm_bios: %s: %s\n", str, error_table[i].msg);
else
printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
}
 
/* Called from console driver -- must make sure apm_enabled. */
int apm_display_blank(void)
{
#ifdef CONFIG_APM_DISPLAY_BLANK
int error;
 
if (!apm_enabled)
return 0;
error = apm_set_display_power_state(APM_STATE_STANDBY);
if (error == APM_SUCCESS)
return 1;
apm_error("set display standby", error);
#endif
return 0;
}
 
/* Called from console driver -- must make sure apm_enabled. */
int apm_display_unblank(void)
{
#ifdef CONFIG_APM_DISPLAY_BLANK
int error;
 
if (!apm_enabled)
return 0;
error = apm_set_display_power_state(APM_STATE_READY);
if (error == APM_SUCCESS)
return 1;
apm_error("set display ready", error);
#endif
return 0;
}
 
int apm_register_callback(int (*callback)(apm_event_t))
{
callback_list_t * new;
 
new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
if (new == NULL)
return -ENOMEM;
new->callback = callback;
new->next = callback_list;
callback_list = new;
return 0;
}
 
void apm_unregister_callback(int (*callback)(apm_event_t))
{
callback_list_t ** ptr;
callback_list_t * old;
 
ptr = &callback_list;
for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
if ((*ptr)->callback == callback)
break;
old = *ptr;
*ptr = old->next;
kfree_s(old, sizeof(callback_list_t));
}
static int queue_empty(struct apm_bios_struct * as)
{
return as->event_head == as->event_tail;
}
 
static apm_event_t get_queued_event(struct apm_bios_struct * as)
{
as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
return as->events[as->event_tail];
}
 
static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
{
struct apm_bios_struct * as;
if (user_list == NULL)
return 0;
for (as = user_list; as != NULL; as = as->next) {
if (as == sender)
continue;
as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
if (as->event_head == as->event_tail) {
static int notified;
 
if (notified == 0) {
printk( "apm_bios: an event queue overflowed\n" );
notified = 1;
}
as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
}
as->events[as->event_head] = event;
if (!as->suser)
continue;
switch (event) {
case APM_SYS_SUSPEND:
case APM_USER_SUSPEND:
as->suspends_pending++;
suspends_pending++;
break;
 
case APM_SYS_STANDBY:
case APM_USER_STANDBY:
as->standbys_pending++;
standbys_pending++;
break;
}
}
wake_up_interruptible(&process_list);
return 1;
}
 
static void set_time(void)
{
unsigned long flags;
 
if (!got_clock_diff) /* Don't know time zone, can't set clock */
return;
 
save_flags(flags);
cli();
CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
restore_flags(flags);
}
 
static void suspend(void)
{
unsigned long flags;
int err;
 
/* Estimate time zone so that set_time can
update the clock */
save_flags(flags);
clock_cmos_diff = -get_cmos_time();
cli();
clock_cmos_diff += CURRENT_TIME;
got_clock_diff = 1;
restore_flags(flags);
err = apm_set_power_state(APM_STATE_SUSPEND);
if (err)
apm_error("suspend", err);
set_time();
}
 
static void standby(void)
{
int err;
 
err = apm_set_power_state(APM_STATE_STANDBY);
if (err)
apm_error("standby", err);
}
 
static apm_event_t get_event(void)
{
int error;
apm_event_t event;
 
static int notified = 0;
 
error = apm_get_event(&event);
if (error == APM_SUCCESS)
return event;
 
if ((error != APM_NO_EVENTS) && (notified++ == 0))
apm_error("get_event", error);
 
return 0;
}
 
static void send_event(apm_event_t event, apm_event_t undo,
struct apm_bios_struct *sender)
{
callback_list_t * call;
callback_list_t * fix;
for (call = callback_list; call != NULL; call = call->next) {
if (call->callback(event) && undo) {
for (fix = callback_list; fix != call; fix = fix->next)
fix->callback(undo);
if (apm_bios_info.version > 0x100)
apm_set_power_state(APM_STATE_REJECT);
return;
}
}
 
queue_event(event, sender);
}
 
static void check_events(void)
{
apm_event_t event;
 
while ((event = get_event()) != 0) {
#ifdef APM_DEBUG
if (event <= NR_APM_EVENT_NAME)
printk("APM BIOS received %s notify\n",
apm_event_name[event - 1]);
else
printk("APM BIOS received unknown event 0x%02x\n",
event);
#endif
switch (event) {
case APM_SYS_STANDBY:
case APM_USER_STANDBY:
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
if (waiting_for_resume) {
return;
}
waiting_for_resume = 1;
#endif
send_event(event, APM_STANDBY_RESUME, NULL);
if (standbys_pending <= 0)
standby();
break;
 
case APM_USER_SUSPEND:
#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
if (apm_bios_info.version > 0x100)
apm_set_power_state(APM_STATE_REJECT);
break;
#endif
case APM_SYS_SUSPEND:
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
if (waiting_for_resume) {
return;
}
waiting_for_resume = 1;
#endif
send_event(event, APM_NORMAL_RESUME, NULL);
if (suspends_pending <= 0)
suspend();
break;
 
case APM_NORMAL_RESUME:
case APM_CRITICAL_RESUME:
case APM_STANDBY_RESUME:
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
waiting_for_resume = 0;
#endif
set_time();
send_event(event, 0, NULL);
break;
 
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
send_event(event, 0, NULL);
break;
 
case APM_UPDATE_TIME:
set_time();
break;
 
case APM_CRITICAL_SUSPEND:
suspend();
break;
}
}
}
 
static void do_apm_timer(unsigned long unused)
{
int err;
 
static int pending_count = 0;
 
if (((standbys_pending > 0) || (suspends_pending > 0))
&& (apm_bios_info.version > 0x100)
&& (pending_count-- <= 0)) {
pending_count = 4;
 
err = apm_set_power_state(APM_STATE_BUSY);
if (err)
apm_error("busy", err);
}
 
if (!(((standbys_pending > 0) || (suspends_pending > 0))
&& (apm_bios_info.version == 0x100)))
check_events();
 
init_timer(&apm_timer);
apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
add_timer(&apm_timer);
}
 
/* Called from sys_idle, must make sure apm_enabled. */
int apm_do_idle(void)
{
#ifdef CONFIG_APM_CPU_IDLE
unsigned short error;
 
if (!apm_enabled)
return 0;
 
APM_SET_CPU_IDLE(error);
if (error & 0xff)
return 0;
 
clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
return 1;
#else
return 0;
#endif
}
 
/* Called from sys_idle, must make sure apm_enabled. */
void apm_do_busy(void)
{
#ifdef CONFIG_APM_CPU_IDLE
unsigned short error;
 
if (!apm_enabled)
return;
#ifndef ALWAYS_CALL_BUSY
if (!clock_slowed)
return;
#endif
 
APM_SET_CPU_BUSY(error);
 
clock_slowed = 0;
#endif
}
 
static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
{
if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
printk("apm_bios: %s passed bad filp", func);
return 1;
}
return 0;
}
 
static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
{
struct apm_bios_struct * as;
int i;
apm_event_t event;
struct wait_queue wait = { current, NULL };
 
as = fp->private_data;
if (check_apm_bios_struct(as, "read"))
return -EIO;
if (count < sizeof(apm_event_t))
return -EINVAL;
if (queue_empty(as)) {
if (fp->f_flags & O_NONBLOCK)
return -EAGAIN;
add_wait_queue(&process_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
if (queue_empty(as)
&& !(current->signal & ~current->blocked)) {
schedule();
goto repeat;
}
current->state = TASK_RUNNING;
remove_wait_queue(&process_list, &wait);
}
i = count;
while ((i >= sizeof(event)) && !queue_empty(as)) {
event = get_queued_event(as);
memcpy_tofs(buf, &event, sizeof(event));
switch (event) {
case APM_SYS_SUSPEND:
case APM_USER_SUSPEND:
as->suspends_read++;
break;
 
case APM_SYS_STANDBY:
case APM_USER_STANDBY:
as->standbys_read++;
break;
}
buf += sizeof(event);
i -= sizeof(event);
}
if (i < count)
return count - i;
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
return 0;
}
 
static int do_select(struct inode *inode, struct file *fp, int sel_type,
select_table * wait)
{
struct apm_bios_struct * as;
 
as = fp->private_data;
if (check_apm_bios_struct(as, "select"))
return 0;
if (sel_type != SEL_IN)
return 0;
if (!queue_empty(as))
return 1;
select_wait(&process_list, wait);
return 0;
}
 
static int do_ioctl(struct inode * inode, struct file *filp,
u_int cmd, u_long arg)
{
struct apm_bios_struct * as;
 
as = filp->private_data;
if (check_apm_bios_struct(as, "ioctl"))
return -EIO;
if (!as->suser)
return -EPERM;
switch (cmd) {
case APM_IOC_STANDBY:
if (as->standbys_read > 0) {
as->standbys_read--;
as->standbys_pending--;
standbys_pending--;
}
else
send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
if (standbys_pending <= 0)
standby();
break;
case APM_IOC_SUSPEND:
if (as->suspends_read > 0) {
as->suspends_read--;
as->suspends_pending--;
suspends_pending--;
}
else
send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
if (suspends_pending <= 0)
suspend();
break;
default:
return -EINVAL;
}
return 0;
}
 
static void do_release(struct inode * inode, struct file * filp)
{
struct apm_bios_struct * as;
 
as = filp->private_data;
filp->private_data = NULL;
if (check_apm_bios_struct(as, "release"))
return;
if (as->standbys_pending > 0) {
standbys_pending -= as->standbys_pending;
if (standbys_pending <= 0)
standby();
}
if (as->suspends_pending > 0) {
suspends_pending -= as->suspends_pending;
if (suspends_pending <= 0)
suspend();
}
if (user_list == as)
user_list = as->next;
else {
struct apm_bios_struct * as1;
 
for (as1 = user_list;
(as1 != NULL) && (as1->next != as);
as1 = as1->next)
;
if (as1 == NULL)
printk("apm_bios: filp not in user list");
else
as1->next = as->next;
}
kfree_s(as, sizeof(*as));
}
 
static int do_open(struct inode * inode, struct file * filp)
{
struct apm_bios_struct * as;
 
as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
printk("apm_bios: cannot allocate struct of size %d bytes",
sizeof(*as));
return -ENOMEM;
}
as->magic = APM_BIOS_MAGIC;
as->event_tail = as->event_head = 0;
as->suspends_pending = as->standbys_pending = 0;
as->suspends_read = as->standbys_read = 0;
as->suser = suser();
as->next = user_list;
user_list = as;
filp->private_data = as;
return 0;
}
 
#ifdef CONFIG_PROC_FS
int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
{
char * p;
unsigned short bx;
unsigned short cx;
unsigned short dx;
unsigned short error;
unsigned short ac_line_status = 0xff;
unsigned short battery_status = 0xff;
unsigned short battery_flag = 0xff;
int percentage = -1;
int time_units = -1;
char *units = "?";
 
if (!apm_enabled)
return 0;
p = buf;
 
if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
ac_line_status = (bx >> 8) & 0xff;
battery_status = bx & 0xff;
if ((cx & 0xff) != 0xff)
percentage = cx & 0xff;
 
if (apm_bios_info.version > 0x100) {
battery_flag = (cx >> 8) & 0xff;
if (dx != 0xffff) {
if ((dx & 0x8000) == 0x8000) {
units = "min";
time_units = dx & 0x7ffe;
} else {
units = "sec";
time_units = dx & 0x7fff;
}
}
}
}
/* Arguments, with symbols from linux/apm_bios.h. Information is
from the Get Power Status (0x0a) call unless otherwise noted.
 
0) Linux driver version (this will change if format changes)
1) APM BIOS Version. Usually 1.0 or 1.1.
2) APM flags from APM Installation Check (0x00):
bit 0: APM_16_BIT_SUPPORT
bit 1: APM_32_BIT_SUPPORT
bit 2: APM_IDLE_SLOWS_CLOCK
bit 3: APM_BIOS_DISABLED
bit 4: APM_BIOS_DISENGAGED
3) AC line status
0x00: Off-line
0x01: On-line
0x02: On backup power (APM BIOS 1.1 only)
0xff: Unknown
4) Battery status
0x00: High
0x01: Low
0x02: Critical
0x03: Charging
0xff: Unknown
5) Battery flag
bit 0: High
bit 1: Low
bit 2: Critical
bit 3: Charging
bit 7: No system battery
0xff: Unknown
6) Remaining battery life (percentage of charge):
0-100: valid
-1: Unknown
7) Remaining battery life (time units):
Number of remaining minutes or seconds
-1: Unknown
8) min = minutes; sec = seconds */
p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
driver_version,
(apm_bios_info.version >> 8) & 0xff,
apm_bios_info.version & 0xff,
apm_bios_info.flags,
ac_line_status,
battery_status,
battery_flag,
percentage,
time_units,
units);
 
return p - buf;
}
#endif
 
static int apm_disabled = 0;
 
void apm_setup(char *str, int *ints)
{
if(strcmp(str,"off")==0)
apm_disabled=1;
if(strcmp(str,"on")==0)
apm_disabled=0;
}
 
void apm_bios_init(void)
{
unsigned short bx;
unsigned short cx;
unsigned short dx;
unsigned short error;
char * power_stat;
char * bat_stat;
if (apm_disabled == 1)
{
printk("APM disabled.\n");
return;
}
 
if (apm_bios_info.version == 0) {
printk("APM BIOS not found.\n");
return;
}
printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
((apm_bios_info.version >> 8) & 0xff) + '0',
(apm_bios_info.version & 0xff) + '0',
apm_bios_info.flags,
driver_version);
if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
printk(" No 32 bit BIOS support\n");
return;
}
 
/*
* Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
* but is reportedly a 1.0 BIOS.
*/
if (apm_bios_info.version == 0x001)
apm_bios_info.version = 0x100;
 
printk(" Entry %x:%lx cseg16 %x dseg %x",
apm_bios_info.cseg, apm_bios_info.offset,
apm_bios_info.cseg_16, apm_bios_info.dseg);
if (apm_bios_info.version > 0x100)
printk(" cseg len %x, dseg len %x",
apm_bios_info.cseg_len, apm_bios_info.dseg_len);
printk("\n");
 
apm_bios_entry.offset = apm_bios_info.offset;
apm_bios_entry.segment = APM_CS;
set_base(gdt[APM_CS >> 3],
__PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg << 4));
set_base(gdt[APM_CS_16 >> 3],
__PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg_16 << 4));
set_base(gdt[APM_DS >> 3],
__PAGE_OFFSET + ((unsigned long)apm_bios_info.dseg << 4));
if (apm_bios_info.version == 0x100) {
set_limit(gdt[APM_CS >> 3], 64 * 1024);
set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
set_limit(gdt[APM_DS >> 3], 64 * 1024);
} else {
#ifdef APM_RELAX_SEGMENTS
/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
set_limit(gdt[APM_CS >> 3], 64 * 1024);
/* For some unknown machine. */
set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
/* For the DEC Hinote Ultra CT475 (and others?) */
set_limit(gdt[APM_DS >> 3], 64 * 1024);
#else
set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
#endif
apm_bios_info.version = 0x0101;
error = apm_driver_version(&apm_bios_info.version);
if (error != 0)
apm_bios_info.version = 0x100;
else {
apm_engage_power_management(0x0001);
printk( " Connection version %d.%d\n",
(apm_bios_info.version >> 8) & 0xff,
apm_bios_info.version & 0xff );
apm_bios_info.version = 0x0101;
}
}
 
error = apm_get_power_status(&bx, &cx, &dx);
if (error)
printk(" Power status not available\n");
else {
switch ((bx >> 8) & 0xff) {
case 0: power_stat = "off line"; break;
case 1: power_stat = "on line"; break;
case 2: power_stat = "on backup power"; break;
default: power_stat = "unknown"; break;
}
switch (bx & 0xff) {
case 0: bat_stat = "high"; break;
case 1: bat_stat = "low"; break;
case 2: bat_stat = "critical"; break;
case 3: bat_stat = "charging"; break;
default: bat_stat = "unknown"; break;
}
printk(" AC %s, battery status %s, battery life ",
power_stat, bat_stat);
if ((cx & 0xff) == 0xff)
printk("unknown\n");
else
printk("%d%%\n", cx & 0xff);
if (apm_bios_info.version > 0x100) {
printk(" battery flag 0x%02x, battery life ",
(cx >> 8) & 0xff);
if (dx == 0xffff)
printk("unknown\n");
else {
if ((dx & 0x8000))
printk("%d minutes\n", dx & 0x7ffe );
else
printk("%d seconds\n", dx & 0x7fff );
}
}
}
 
#ifdef CONFIG_APM_DO_ENABLE
/*
* This call causes my NEC UltraLite Versa 33/C to hang if it is
* booted with PM disabled but not in the docking station.
* Unfortunate ...
*/
error = apm_enable_power_management();
if (error)
apm_error("enable power management", error);
if (error == APM_DISABLED)
return;
#endif
 
init_timer(&apm_timer);
apm_timer.function = do_apm_timer;
apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
add_timer(&apm_timer);
 
register_symtab(&apm_syms);
 
#ifdef CONFIG_PROC_FS
proc_register_dynamic(&proc_root, &apm_proc_entry);
#endif
 
misc_register(&apm_device);
 
apm_enabled = 1;
}
/signal.c
0,0 → 1,359
/*
* linux/arch/i386/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
 
#include <linux/config.h>
 
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
 
#include <asm/segment.h>
 
#define _S(nr) (1<<((nr)-1))
 
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
 
asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
 
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
{
unsigned long mask;
struct pt_regs * regs = (struct pt_regs *) &restart;
 
mask = current->blocked;
current->blocked = set & _BLOCKABLE;
regs->eax = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(mask,regs))
return -EINTR;
}
}
 
static inline void restore_i387_hard(struct _fpstate *buf)
{
#ifdef __SMP__
if (current->flags & PF_USEDFPU) {
stts();
}
#else
if (current == last_task_used_math) {
last_task_used_math = NULL;
stts();
}
#endif
current->used_math = 1;
current->flags &= ~PF_USEDFPU;
memcpy_fromfs(&current->tss.i387.hard, buf, sizeof(*buf));
}
 
static void restore_i387(struct _fpstate *buf)
{
#ifndef CONFIG_MATH_EMULATION
restore_i387_hard(buf);
#else
if (hard_math) {
restore_i387_hard(buf);
return;
}
restore_i387_soft(buf);
#endif
}
 
/*
* This sets regs->esp even though we don't actually use sigstacks yet..
*/
asmlinkage int sys_sigreturn(unsigned long __unused)
{
#define COPY(x) regs->x = context.x
#define COPY_SEG(x) \
if ( (context.x & 0xfffc) /* not a NULL selectors */ \
&& (context.x & 0x4) != 0x4 /* not a LDT selector */ \
&& (context.x & 3) != 3 /* not a RPL3 GDT selector */ \
) goto badframe; COPY(x);
#define COPY_SEG_STRICT(x) \
if (!(context.x & 0xfffc) || (context.x & 3) != 3) goto badframe; COPY(x);
struct sigcontext_struct context;
struct pt_regs * regs;
 
regs = (struct pt_regs *) &__unused;
if (verify_area(VERIFY_READ, (void *) regs->esp, sizeof(context)))
goto badframe;
memcpy_fromfs(&context,(void *) regs->esp, sizeof(context));
current->blocked = context.oldmask & _BLOCKABLE;
COPY_SEG(ds);
COPY_SEG(es);
COPY_SEG(fs);
COPY_SEG(gs);
COPY_SEG_STRICT(ss);
COPY_SEG_STRICT(cs);
COPY(eip);
COPY(ecx); COPY(edx);
COPY(ebx);
COPY(esp); COPY(ebp);
COPY(edi); COPY(esi);
regs->eflags &= ~0x40DD5;
regs->eflags |= context.eflags & 0x40DD5;
regs->orig_eax = -1; /* disable syscall checks */
if (context.fpstate) {
struct _fpstate * buf = context.fpstate;
if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
restore_i387(buf);
}
return context.eax;
badframe:
do_exit(SIGSEGV);
}
 
static inline struct _fpstate * save_i387_hard(struct _fpstate * buf)
{
#ifdef __SMP__
if (current->flags & PF_USEDFPU) {
__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
stts();
current->flags &= ~PF_USEDFPU;
}
#else
if (current == last_task_used_math) {
__asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard));
last_task_used_math = NULL;
__asm__ __volatile__("fwait"); /* not needed on 486+ */
stts();
}
#endif
current->tss.i387.hard.status = current->tss.i387.hard.swd;
memcpy_tofs(buf, &current->tss.i387.hard, sizeof(*buf));
current->used_math = 0;
return buf;
}
 
static struct _fpstate * save_i387(struct _fpstate * buf)
{
if (!current->used_math)
return NULL;
 
#ifndef CONFIG_MATH_EMULATION
return save_i387_hard(buf);
#else
if (hard_math)
return save_i387_hard(buf);
return save_i387_soft(buf);
#endif
}
 
/*
* Set up a signal frame... Make the stack look the way iBCS2 expects
* it to look.
*/
static void setup_frame(struct sigaction * sa,
struct pt_regs * regs, int signr,
unsigned long oldmask)
{
unsigned long * frame;
 
frame = (unsigned long *) regs->esp;
if (regs->ss != USER_DS && sa->sa_restorer)
frame = (unsigned long *) sa->sa_restorer;
frame -= 64;
if (verify_area(VERIFY_WRITE,frame,64*4))
do_exit(SIGSEGV);
 
/* set up the "normal" stack seen by the signal handler (iBCS2) */
#define __CODE ((unsigned long)(frame+24))
#define CODE(x) ((unsigned long *) ((x)+__CODE))
put_user(__CODE,frame);
if (current->exec_domain && current->exec_domain->signal_invmap)
put_user(current->exec_domain->signal_invmap[signr], frame+1);
else
put_user(signr, frame+1);
put_user(regs->gs, frame+2);
put_user(regs->fs, frame+3);
put_user(regs->es, frame+4);
put_user(regs->ds, frame+5);
put_user(regs->edi, frame+6);
put_user(regs->esi, frame+7);
put_user(regs->ebp, frame+8);
put_user(regs->esp, frame+9);
put_user(regs->ebx, frame+10);
put_user(regs->edx, frame+11);
put_user(regs->ecx, frame+12);
put_user(regs->eax, frame+13);
put_user(current->tss.trap_no, frame+14);
put_user(current->tss.error_code, frame+15);
put_user(regs->eip, frame+16);
put_user(regs->cs, frame+17);
put_user(regs->eflags, frame+18);
put_user(regs->esp, frame+19);
put_user(regs->ss, frame+20);
put_user(save_i387((struct _fpstate *)(frame+32)),frame+21);
/* non-iBCS2 extensions.. */
put_user(oldmask, frame+22);
put_user(current->tss.cr2, frame+23);
/* set up the return code... */
put_user(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */
put_user(0x80cd0000, CODE(4)); /* int $0x80 */
put_user(__NR_sigreturn, CODE(2));
#undef __CODE
#undef CODE
 
/* Set up registers for signal handler */
regs->esp = (unsigned long) frame;
regs->eip = (unsigned long) sa->sa_handler;
regs->cs = USER_CS; regs->ss = USER_DS;
regs->ds = USER_DS; regs->es = USER_DS;
regs->gs = USER_DS; regs->fs = USER_DS;
regs->eflags &= ~TF_MASK;
}
 
/*
* OK, we're invoking a handler
*/
static void handle_signal(unsigned long signr, struct sigaction *sa,
unsigned long oldmask, struct pt_regs * regs)
{
/* are we from a system call? */
if (regs->orig_eax >= 0) {
/* If so, check system call restarting.. */
switch (regs->eax) {
case -ERESTARTNOHAND:
regs->eax = -EINTR;
break;
 
case -ERESTARTSYS:
if (!(sa->sa_flags & SA_RESTART)) {
regs->eax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
}
 
/* set up the stack frame */
setup_frame(sa, regs, signr, oldmask);
 
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
if (!(sa->sa_flags & SA_NOMASK))
current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
}
 
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Note that we go through the signals twice: once to check the signals that
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
{
unsigned long mask = ~current->blocked;
unsigned long signr;
struct sigaction * sa;
 
while ((signr = current->signal & mask)) {
/*
* This stops gcc flipping out. Otherwise the assembler
* including volatiles for the inline function to get
* current combined with this gets it confused.
*/
struct task_struct *t=current;
__asm__("bsf %3,%1\n\t"
"btrl %1,%0"
:"=m" (t->signal),"=r" (signr)
:"0" (t->signal), "1" (signr));
sa = current->sig->action + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
current->exit_code = signr;
current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD);
schedule();
if (!(signr = current->exit_code))
continue;
current->exit_code = 0;
if (signr == SIGSTOP)
continue;
if (_S(signr) & current->blocked) {
current->signal |= _S(signr);
continue;
}
sa = current->sig->action + signr - 1;
}
if (sa->sa_handler == SIG_IGN) {
if (signr != SIGCHLD)
continue;
/* check for SIGCHLD: it's special */
while (sys_waitpid(-1,NULL,WNOHANG) > 0)
/* nothing */;
continue;
}
if (sa->sa_handler == SIG_DFL) {
if (current->pid == 1)
continue;
switch (signr) {
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
 
case SIGTSTP: case SIGTTIN: case SIGTTOU:
if (is_orphaned_pgrp(current->pgrp))
continue;
case SIGSTOP:
if (current->flags & PF_PTRACED)
continue;
current->state = TASK_STOPPED;
current->exit_code = signr;
if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
notify_parent(current, SIGCHLD);
schedule();
continue;
 
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV:
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, regs))
signr |= 0x80;
}
/* fall through */
default:
current->signal |= _S(signr & 0x7f);
current->flags |= PF_SIGNALED;
do_exit(signr);
}
}
handle_signal(signr, sa, oldmask, regs);
return 1;
}
 
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
if (regs->eax == -ERESTARTNOHAND ||
regs->eax == -ERESTARTSYS ||
regs->eax == -ERESTARTNOINTR) {
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
}
return 0;
}
/process.c
0,0 → 1,617
/*
* linux/arch/i386/kernel/process.c
*
* Copyright (C) 1995 Linus Torvalds
*/
 
/*
* This file handles the architecture-dependent parts of process handling..
*/
 
#define __KERNEL_SYSCALLS__
#include <stdarg.h>
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/ldt.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/interrupt.h>
#include <linux/config.h>
#include <linux/unistd.h>
#include <linux/delay.h>
 
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/smp.h>
 
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
#ifdef CONFIG_APM
extern int apm_do_idle(void);
extern void apm_do_busy(void);
#endif
 
static int hlt_counter=0;
 
#define HARD_IDLE_TIMEOUT (HZ / 3)
 
void disable_hlt(void)
{
hlt_counter++;
}
 
void enable_hlt(void)
{
hlt_counter--;
}
 
#ifndef __SMP__
 
static void hard_idle(void)
{
while (!need_resched) {
if (hlt_works_ok && !hlt_counter) {
#ifdef CONFIG_APM
/* If the APM BIOS is not enabled, or there
is an error calling the idle routine, we
should hlt if possible. We need to check
need_resched again because an interrupt
may have occurred in apm_do_idle(). */
start_bh_atomic();
if (!apm_do_idle() && !need_resched)
__asm__("hlt");
end_bh_atomic();
#else
__asm__("hlt");
#endif
}
if (need_resched)
break;
schedule();
}
#ifdef CONFIG_APM
apm_do_busy();
#endif
}
 
/*
* The idle loop on a uniprocessor i386..
*/
asmlinkage int sys_idle(void)
{
unsigned long start_idle = 0;
 
if (current->pid != 0)
return -EPERM;
/* endless idle loop with no priority at all */
current->counter = -100;
for (;;)
{
/*
* We are locked at this point. So we can safely call
* the APM bios knowing only one CPU at a time will do
* so.
*/
if (!start_idle)
start_idle = jiffies;
if (jiffies - start_idle > HARD_IDLE_TIMEOUT)
{
hard_idle();
}
else
{
if (hlt_works_ok && !hlt_counter && !need_resched)
__asm__("hlt");
}
if (need_resched)
start_idle = 0;
schedule();
}
}
 
#else
 
/*
* In the SMP world we hlt outside of kernel syscall rather than within
* so as to get the right locking semantics.
*/
asmlinkage int sys_idle(void)
{
if(current->pid != 0)
return -EPERM;
#ifdef __SMP_PROF__
smp_spins_sys_idle[smp_processor_id()]+=
smp_spins_syscall_cur[smp_processor_id()];
#endif
current->counter= -100;
schedule();
return 0;
}
 
/*
* This is being executed in task 0 'user space'.
*/
 
int cpu_idle(void *unused)
{
while(1)
{
if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
__asm("hlt");
if(0==(0x7fffffff & smp_process_available))
continue;
while(0x80000000 & smp_process_available);
cli();
while(set_bit(31,&smp_process_available))
while(test_bit(31,&smp_process_available))
{
/*
* Oops.. This is kind of important in some cases...
*/
if(clear_bit(smp_processor_id(), &smp_invalidate_needed))
local_flush_tlb();
}
if (0==(0x7fffffff & smp_process_available)){
clear_bit(31,&smp_process_available);
sti();
continue;
}
smp_process_available--;
clear_bit(31,&smp_process_available);
sti();
idle();
}
}
 
#endif
 
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low. We try that for a while,
* and if it doesn't work, we do some other stupid things.
*/
static long no_idt[2] = {0, 0};
static int reboot_mode = 0;
static int reboot_thru_bios = 0;
 
void reboot_setup(char *str, int *ints)
{
while(1) {
switch (*str) {
case 'w': /* "warm" reboot (no memory testing etc) */
reboot_mode = 0x1234;
break;
case 'c': /* "cold" reboot (with memory testing etc) */
reboot_mode = 0x0;
break;
case 'b': /* "bios" reboot by jumping through the BIOS */
reboot_thru_bios = 1;
break;
case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */
reboot_thru_bios = 0;
break;
}
if((str = strchr(str,',')) != NULL)
str++;
else
break;
}
}
 
 
/* The following code and data reboots the machine by switching to real
mode and jumping to the BIOS reset entry point, as if the CPU has
really been reset. The previous version asked the keyboard
controller to pulse the CPU reset line, which is more thorough, but
doesn't work with at least one type of 486 motherboard. It is easy
to stop this code working; hence the copious comments. */
 
unsigned long long
real_mode_gdt_entries [3] =
{
0x0000000000000000ULL, /* Null descriptor */
0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
};
 
struct
{
unsigned short size __attribute__ ((packed));
unsigned long long * base __attribute__ ((packed));
}
real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
real_mode_idt = { 0x3ff, 0 };
 
/* This is 16-bit protected mode code to disable paging and the cache,
switch to real mode and jump to the BIOS reset code.
 
The instruction that switches to real mode by writing to CR0 must be
followed immediately by a far jump instruction, which set CS to a
valid value for real mode, and flushes the prefetch queue to avoid
running instructions that have already been decoded in protected
mode.
 
Clears all the flags except ET, especially PG (paging), PE
(protected-mode enable) and TS (task switch for coprocessor state
save). Flushes the TLB after paging has been disabled. Sets CD and
NW, to disable the cache on a 486, and invalidates the cache. This
is more like the state of a 486 after reset. I don't know if
something else should be done for other chips.
 
More could be done here to set up the registers as if a CPU reset had
occurred; hopefully real BIOSes don't assume much. */
 
unsigned char real_mode_switch [] =
{
0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
0x74, 0x02, /* jz f */
0x0f, 0x08, /* invd */
0x24, 0x10, /* f: andb $0x10,al */
0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
};
 
static inline void kb_wait(void)
{
int i;
for (i=0; i<0x10000; i++)
if ((inb_p(0x64) & 0x02) == 0)
break;
}
 
void hard_reset_now (void)
{
 
if(!reboot_thru_bios) {
sti();
/* rebooting needs to touch the page at absolute addr 0 */
pg0[0] = 7;
*((unsigned short *)0x472) = reboot_mode;
for (;;) {
int i;
for (i=0; i<100; i++) {
int j;
kb_wait();
for(j = 0; j < 100000 ; j++)
/* nothing */;
outb(0xfe,0x64); /* pulse reset low */
udelay(10);
}
__asm__ __volatile__("\tlidt %0": "=m" (no_idt));
}
}
 
cli ();
 
/* Write zero to CMOS register number 0x0f, which the BIOS POST
routine will recognize as telling it to do a proper reboot. (Well
that's what this book in front of me says -- it may only apply to
the Phoenix BIOS though, it's not clear). At the same time,
disable NMIs by setting the top bit in the CMOS address register,
as we're about to do peculiar things to the CPU. I'm not sure if
`outb_p' is needed instead of just `outb'. Use it to be on the
safe side. */
 
outb_p (0x8f, 0x70);
outb_p (0x00, 0x71);
 
/* Remap the kernel at virtual address zero, as well as offset zero
from the kernel segment. This assumes the kernel segment starts at
virtual address PAGE_OFFSET. */
 
memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
 
/* Make sure the first page is mapped to the start of physical memory.
It is normally not mapped, to trap kernel NULL pointer dereferences. */
 
pg0 [0] = 7;
 
/* Use `swapper_pg_dir' as our page directory. Don't bother with
`SET_PAGE_DIR' because interrupts are disabled and we're rebooting.
This instruction flushes the TLB. */
 
__asm__ __volatile__ ("movl %0,%%cr3" : : "a" (swapper_pg_dir) : "memory");
 
/* Write 0x1234 to absolute memory location 0x472. The BIOS reads
this on booting to tell it to "Bypass memory test (also warm
boot)". This seems like a fairly standard thing that gets set by
REBOOT.COM programs, and the previous reset routine did this
too. */
 
*((unsigned short *)0x472) = reboot_mode;
 
/* For the switch to real mode, copy some code to low memory. It has
to be in the first 64k because it is running in 16-bit mode, and it
has to have the same physical and virtual address, because it turns
off paging. Copy it near the end of the first page, out of the way
of BIOS variables. */
 
memcpy ((void *) (0x1000 - sizeof (real_mode_switch)),
real_mode_switch, sizeof (real_mode_switch));
 
/* Set up the IDT for real mode. */
 
__asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
 
/* Set up a GDT from which we can load segment descriptors for real
mode. The GDT is not used in real mode; it is just needed here to
prepare the descriptors. */
 
__asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
 
/* Load the data segment registers, and thus the descriptors ready for
real mode. The base address of each segment is 0x100, 16 times the
selector value being loaded here. This is so that the segment
registers don't have to be reloaded after switching to real mode:
the values are consistent for real mode operation already. */
 
__asm__ __volatile__ ("movw $0x0010,%%ax\n"
"\tmovw %%ax,%%ds\n"
"\tmovw %%ax,%%es\n"
"\tmovw %%ax,%%fs\n"
"\tmovw %%ax,%%gs\n"
"\tmovw %%ax,%%ss" : : : "eax");
 
/* Jump to the 16-bit code that we copied earlier. It disables paging
and the cache, switches to real mode, and jumps to the BIOS reset
entry point. */
 
__asm__ __volatile__ ("ljmp $0x0008,%0"
:
: "i" ((void *) (0x1000 - sizeof (real_mode_switch))));
}
 
void show_regs(struct pt_regs * regs)
{
printk("\n");
printk("EIP: %04x:[<%08lx>]",0xffff & regs->cs,regs->eip);
if (regs->cs & 3)
printk(" ESP: %04x:%08lx",0xffff & regs->ss,regs->esp);
printk(" EFLAGS: %08lx\n",regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->eax,regs->ebx,regs->ecx,regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
0xffff & regs->ds,0xffff & regs->es,
0xffff & regs->fs,0xffff & regs->gs);
}
 
/*
* Free current thread data structures etc..
*/
 
void exit_thread(void)
{
/* forget lazy i387 state */
if (last_task_used_math == current)
last_task_used_math = NULL;
/* forget local segments */
__asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0"
: /* no outputs */
: "r" (0));
current->tss.ldt = 0;
if (current->ldt) {
void * ldt = current->ldt;
current->ldt = NULL;
vfree(ldt);
}
}
 
void flush_thread(void)
{
int i;
 
if (current->ldt) {
void * ldt = current->ldt;
current->ldt = NULL;
vfree(ldt);
for (i=1 ; i<NR_TASKS ; i++) {
if (task[i] == current) {
set_ldt_desc(gdt+(i<<1)+
FIRST_LDT_ENTRY,&default_ldt, 1);
load_ldt(i);
}
}
}
 
for (i=0 ; i<8 ; i++)
current->debugreg[i] = 0;
 
/*
* Forget coprocessor state..
*/
#ifdef __SMP__
if (current->flags & PF_USEDFPU) {
stts();
}
#else
if (last_task_used_math == current) {
last_task_used_math = NULL;
stts();
}
#endif
current->used_math = 0;
current->flags &= ~PF_USEDFPU;
}
 
void release_thread(struct task_struct *dead_task)
{
}
 
void copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
struct task_struct * p, struct pt_regs * regs)
{
int i;
struct pt_regs * childregs;
 
p->tss.es = KERNEL_DS;
p->tss.cs = KERNEL_CS;
p->tss.ss = KERNEL_DS;
p->tss.ds = KERNEL_DS;
p->tss.fs = USER_DS;
p->tss.gs = KERNEL_DS;
p->tss.ss0 = KERNEL_DS;
p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
p->tss.tr = _TSS(nr);
childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
p->tss.esp = (unsigned long) childregs;
p->tss.eip = (unsigned long) ret_from_sys_call;
*childregs = *regs;
childregs->eax = 0;
childregs->esp = esp;
p->tss.back_link = 0;
p->tss.eflags = regs->eflags & 0xffffcfff; /* iopl is always 0 for a new process */
p->tss.ldt = _LDT(nr);
if (p->ldt) {
p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
if (p->ldt != NULL)
memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
}
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
if (p->ldt)
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, LDT_ENTRIES);
else
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
p->tss.bitmap = offsetof(struct thread_struct,io_bitmap);
for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */
p->tss.io_bitmap[i] = ~0;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387));
}
 
/*
* fill in the fpu structure for a core dump..
*/
int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu)
{
int fpvalid;
 
if (hard_math) {
if ((fpvalid = current->used_math) != 0) {
#ifdef __SMP__
if (current->flags & PF_USEDFPU)
#else
if (last_task_used_math == current)
#endif
__asm__("clts ; fnsave %0": :"m" (*fpu));
else
memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
}
} else {
/* We dump the emulator state here.
We convert it into standard 387 format first.. */
#ifdef CONFIG_MATH_EMULATION
int i;
unsigned long top;
char (*hardreg)[10];
struct i387_soft_struct *soft_fpu = &current->tss.i387.soft;
struct fpu_reg* softreg;
long int control_word = soft_fpu->cwd;
 
fpu->cwd = soft_fpu->cwd;
fpu->swd = soft_fpu->swd;
fpu->twd = soft_fpu->twd;
fpu->fip = soft_fpu->fip;
fpu->fcs = soft_fpu->fcs;
fpu->foo = soft_fpu->foo;
fpu->fos = soft_fpu->fos;
hardreg = (char (*)[10]) &fpu->st_space[0];
top = (unsigned long) soft_fpu->top % 8;
softreg = &soft_fpu->regs[top];
for (i = top ; i < 8; i ++) {
softreg_to_hardreg(softreg, *hardreg, control_word);
hardreg++;
softreg++;
}
softreg = &soft_fpu->regs[0];
for (i = 0; i < top; i++) {
softreg_to_hardreg(softreg, *hardreg, control_word);
hardreg++;
softreg++;
}
fpvalid = 1;
#else /* defined(CONFIG_MATH_EMULATION) */
fpvalid = 0;
#endif /* !defined(CONFIG_MATH_EMULATION) */
}
 
return fpvalid;
}
 
/*
* fill in the user structure for a core dump..
*/
void dump_thread(struct pt_regs * regs, struct user * dump)
{
int i;
 
/* changed the size calculations - should hopefully work better. lbt */
dump->magic = CMAGIC;
dump->start_code = 0;
dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
dump->u_dsize -= dump->u_tsize;
dump->u_ssize = 0;
for (i = 0; i < 8; i++)
dump->u_debugreg[i] = current->debugreg[i];
 
if (dump->start_stack < TASK_SIZE)
dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
 
dump->regs = *regs;
 
dump->u_fpvalid = dump_fpu (regs, &dump->i387);
}
 
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, &regs);
}
 
asmlinkage int sys_clone(struct pt_regs regs)
{
unsigned long clone_flags;
unsigned long newsp;
 
clone_flags = regs.ebx;
newsp = regs.ecx;
if (!newsp)
newsp = regs.esp;
return do_fork(clone_flags, newsp, &regs);
}
 
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(struct pt_regs regs)
{
int error;
char * filename;
 
error = getname((char *) regs.ebx, &filename);
if (error)
return error;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
putname(filename);
return error;
}
/ptrace.c
0,0 → 1,740
/* ptrace.c */
/* By Ross Biro 1/23/92 */
/* edited by Linus Torvalds */
 
#include <linux/config.h> /* CONFIG_MATH_EMULATION */
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/debugreg.h>
 
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/system.h>
 
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
*/
 
/* determines which flags the user has access to. */
/* 1 = access 0 = no access */
#define FLAG_MASK 0x00044dd5
 
/* set's the trap flag. */
#define TRAP_FLAG 0x100
 
/*
* this is the number to subtract from the top of the stack. To find
* the local frame.
*/
#define MAGICNUMBER 68
 
/* change a pid into a task struct. */
static inline struct task_struct * get_task(int pid)
{
int i;
 
for (i = 1; i < NR_TASKS; i++) {
if (task[i] != NULL && (task[i]->pid == pid))
return task[i];
}
return NULL;
}
 
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline int get_stack_long(struct task_struct *task, int offset)
{
unsigned char *stack;
 
stack = (unsigned char *)task->tss.esp0;
stack += offset;
return (*((int *)stack));
}
 
/*
* this routine will put a word on the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline int put_stack_long(struct task_struct *task, int offset,
unsigned long data)
{
unsigned char * stack;
 
stack = (unsigned char *) task->tss.esp0;
stack += offset;
*(unsigned long *) stack = data;
return 0;
}
 
/*
* This routine gets a long from any process space by following the page
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
*/
static unsigned long get_long(struct task_struct * tsk,
struct vm_area_struct * vma, unsigned long addr)
{
pgd_t * pgdir;
pmd_t * pgmiddle;
pte_t * pgtable;
unsigned long page;
 
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (pgd_none(*pgdir)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
if (pgd_bad(*pgdir)) {
printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
pgd_clear(pgdir);
return 0;
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
pmd_clear(pgmiddle);
return 0;
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
do_no_page(tsk, vma, addr, 0);
goto repeat;
}
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
if (page >= high_memory)
return 0;
page += addr & ~PAGE_MASK;
return *(unsigned long *) page;
}
 
/*
* This routine puts a long into any process space by following the page
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
*
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
unsigned long data)
{
pgd_t *pgdir;
pmd_t *pgmiddle;
pte_t *pgtable;
unsigned long page;
 
repeat:
pgdir = pgd_offset(vma->vm_mm, addr);
if (!pgd_present(*pgdir)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
if (pgd_bad(*pgdir)) {
printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
pgd_clear(pgdir);
return;
}
pgmiddle = pmd_offset(pgdir, addr);
if (pmd_none(*pgmiddle)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
if (pmd_bad(*pgmiddle)) {
printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
pmd_clear(pgmiddle);
return;
}
pgtable = pte_offset(pgmiddle, addr);
if (!pte_present(*pgtable)) {
do_no_page(tsk, vma, addr, 1);
goto repeat;
}
page = pte_page(*pgtable);
if (!pte_write(*pgtable)) {
do_wp_page(tsk, vma, addr, 1);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
if (page < high_memory)
*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
/* this should also re-instate whatever read-only mode there was before */
set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
flush_tlb();
}
 
static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
{
struct vm_area_struct * vma;
 
addr &= PAGE_MASK;
vma = find_vma(tsk->mm,addr);
if (!vma)
return NULL;
if (vma->vm_start <= addr)
return vma;
if (!(vma->vm_flags & VM_GROWSDOWN))
return NULL;
if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
return NULL;
vma->vm_offset -= vma->vm_start - addr;
vma->vm_start = addr;
return vma;
}
 
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
*/
static int read_long(struct task_struct * tsk, unsigned long addr,
unsigned long * result)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
unsigned long low,high;
struct vm_area_struct * vma_high = vma;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 1:
low >>= 8;
low |= high << 24;
break;
case 2:
low >>= 16;
low |= high << 16;
break;
case 3:
low >>= 24;
low |= high << 8;
break;
}
*result = low;
} else
*result = get_long(tsk, vma, addr);
return 0;
}
 
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls put_long() to write a long.
*/
static int write_long(struct task_struct * tsk, unsigned long addr,
unsigned long data)
{
struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
unsigned long low,high;
struct vm_area_struct * vma_high = vma;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
low = data;
break;
case 1:
low &= 0x000000ff;
low |= data << 8;
high &= ~0xff;
high |= data >> 24;
break;
case 2:
low &= 0x0000ffff;
low |= data << 16;
high &= ~0xffff;
high |= data >> 16;
break;
case 3:
low &= 0x00ffffff;
low |= data << 24;
high &= ~0xffffff;
high |= data >> 8;
break;
}
put_long(tsk, vma, addr & ~(sizeof(long)-1),low);
put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
put_long(tsk, vma, addr, data);
return 0;
}
#ifdef CONFIG_MATH_EMULATION
static void write_emulator_word(struct task_struct *child,
unsigned long register_offset,
long data)
{
int i, j;
struct i387_soft_struct *soft_fpu;
struct fpu_reg *this_fpreg, *next_fpreg;
char hard_reg[2][10];
int control_word;
unsigned long top;
i = register_offset / 10;
j = register_offset % 10;
soft_fpu = &child->tss.i387.soft;
top = i + (unsigned long) soft_fpu->top;
control_word = soft_fpu->cwd;
this_fpreg = &soft_fpu->regs[(top + i) % 8];
next_fpreg = &soft_fpu->regs[(top + i + 1) % 8];
softreg_to_hardreg(this_fpreg, hard_reg[0], control_word);
if (j > 6)
softreg_to_hardreg(next_fpreg, hard_reg[1], control_word);
*(long *) &hard_reg[0][j] = data;
hardreg_to_softreg(hard_reg[0], this_fpreg);
if (j > 6)
hardreg_to_softreg(hard_reg[1], next_fpreg);
}
#endif /* defined(CONFIG_MATH_EMULATION) */
 
/*
* Floating point support added to ptrace by Ramon Garcia,
* ramon@juguete.quim.ucm.es
*/
 
#ifdef CONFIG_MATH_EMULATION
 
static unsigned long get_emulator_word(struct task_struct *child,
unsigned long register_offset)
{
char hard_reg[2][10];
int i, j;
struct fpu_reg *this_fpreg, *next_fpreg;
struct i387_soft_struct *soft_fpu;
long int control_word;
unsigned long top;
unsigned long tmp;
i = register_offset / 10;
j = register_offset % 10;
soft_fpu = &child->tss.i387.soft;
top = (unsigned long) soft_fpu->top;
this_fpreg = &soft_fpu->regs[(top + i) % 8];
next_fpreg = &soft_fpu->regs[(top + i + 1) % 8];
control_word = soft_fpu->cwd;
softreg_to_hardreg(this_fpreg, hard_reg[0], control_word);
if (j > 6)
softreg_to_hardreg(next_fpreg, hard_reg[1], control_word);
tmp = *(long *)
&hard_reg[0][j];
return tmp;
}
 
#endif /* defined(CONFIG_MATH_EMULATION) */
 
static int putreg(struct task_struct *child,
unsigned long regno, unsigned long value)
{
switch (regno >> 2) {
case ORIG_EAX:
return -EIO;
case FS:
case GS:
case DS:
case ES:
if (value && (value & 3) != 3)
return -EIO;
value &= 0xffff;
break;
case SS:
case CS:
if ((value & 3) != 3)
return -EIO;
value &= 0xffff;
break;
case EFL:
value &= FLAG_MASK;
value |= get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~FLAG_MASK;
}
put_stack_long(child, regno - sizeof(struct pt_regs), value);
return 0;
}
 
static unsigned long getreg(struct task_struct *child,
unsigned long regno)
{
unsigned long retval = ~0UL;
 
switch (regno >> 2) {
case FS:
case GS:
case DS:
case ES:
case SS:
case CS:
retval = 0xffff;
/* fall through */
default:
regno = regno - sizeof(struct pt_regs);
retval &= get_stack_long(child, regno);
}
return retval;
}
 
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
struct user * dummy;
int i;
 
dummy = NULL;
 
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->flags & PF_PTRACED)
return -EPERM;
/* set the ptrace bit in the process flags. */
current->flags |= PF_PTRACED;
return 0;
}
if (pid == 1) /* you may not mess with init */
return -EPERM;
if (!(child = get_task(pid)))
return -ESRCH;
if (request == PTRACE_ATTACH) {
if (child == current)
return -EPERM;
if ((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->suid) ||
(current->uid != child->uid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
(current->gid != child->gid)) && !suser())
return -EPERM;
/* the same process cannot be attached many times */
if (child->flags & PF_PTRACED)
return -EPERM;
child->flags |= PF_PTRACED;
if (child->p_pptr != current) {
REMOVE_LINKS(child);
child->p_pptr = current;
SET_LINKS(child);
}
send_sig(SIGSTOP, child, 1);
return 0;
}
if (!(child->flags & PF_PTRACED))
return -ESRCH;
if (child->state != TASK_STOPPED) {
if (request != PTRACE_KILL)
return -ESRCH;
}
if (child->p_pptr != current)
return -ESRCH;
 
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
unsigned long tmp;
int res;
 
res = read_long(child, addr, &tmp);
if (res < 0)
return res;
res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
if (!res)
put_fs_long(tmp,(unsigned long *) data);
return res;
}
 
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
int res;
if ((addr & 3) || addr < 0
|| addr > sizeof(struct user) - 3)
return -EIO;
res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
if (res)
return res;
tmp = 0; /* Default return condition */
if(addr < 17*sizeof(long))
tmp = getreg(child, addr);
else if(addr >= (long) &dummy->u_debugreg[0]
&& addr <= (long) &dummy->u_debugreg[7])
{
addr -= (long) &dummy->u_debugreg[0];
addr = addr >> 2;
tmp = child->debugreg[addr];
}
put_fs_long(tmp,(unsigned long *) data);
return 0;
}
 
/* when I and D space are separate, this will have to be fixed. */
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
return write_long(child,addr,data);
 
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
if ((addr & 3) || addr < 0
|| addr > sizeof(struct user) - 3)
return -EIO;
 
if(addr < 17*sizeof(long))
return putreg(child, addr, data);
 
/* We need to be very careful here. We implicitly
want to modify a portion of the task_struct, and we
have to be selective about what portions we allow someone
to modify. */
 
if(addr >= (long) &dummy->u_debugreg[0] &&
addr <= (long) &dummy->u_debugreg[7]){
 
if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
if(addr < (long) &dummy->u_debugreg[4] &&
((unsigned long) data) >= 0xbffffffd) return -EIO;
if(addr == (long) &dummy->u_debugreg[7]) {
data &= ~DR_CONTROL_RESERVED;
for(i=0; i<4; i++)
if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
return -EIO;
};
 
addr -= (long) &dummy->u_debugreg;
addr = addr >> 2;
child->debugreg[addr] = data;
return 0;
};
return -EIO;
 
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: { /* restart after signal. */
long tmp;
 
if ((unsigned long) data > NSIG)
return -EIO;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
child->flags &= ~PF_TRACESYS;
child->exit_code = data;
wake_up_process(child);
/* make sure the single step bit is not set. */
tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
 
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL: {
long tmp;
 
if (child->state == TASK_ZOMBIE) /* already dead */
return 0;
wake_up_process(child);
child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
 
case PTRACE_SINGLESTEP: { /* set the trap flag. */
long tmp;
 
if ((unsigned long) data > NSIG)
return -EIO;
child->flags &= ~PF_TRACESYS;
tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
wake_up_process(child);
child->exit_code = data;
/* give it a chance to run. */
return 0;
}
 
case PTRACE_DETACH: { /* detach a process that was attached. */
long tmp;
 
if ((unsigned long) data > NSIG)
return -EIO;
child->flags &= ~(PF_PTRACED|PF_TRACESYS);
wake_up_process(child);
child->exit_code = data;
REMOVE_LINKS(child);
child->p_pptr = child->p_opptr;
SET_LINKS(child);
/* make sure the single step bit is not set. */
tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
 
case PTRACE_GETREGS: { /* Get all gp regs from the child. */
#ifdef CONFIG_MATH_EMULATION
if (!hard_math)
/* Not supported. */
return -EIO;
#endif
 
if (verify_area(VERIFY_WRITE, (void *) data,
17*sizeof(long)))
return -EIO;
for (i = 0; i < 17*sizeof(long);
i += sizeof(long), data += sizeof(long))
put_fs_long (getreg(child, i), (unsigned long *) data);
return 0;
};
 
case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp;
 
#ifdef CONFIG_MATH_EMULATION
if (!hard_math)
/* Not supported. */
return -EIO;
#endif
 
if (verify_area(VERIFY_READ, (void *) data,
17*sizeof(long)))
return -EIO;
for (i = 0; i < 17*sizeof(long);
i += sizeof(long), data += sizeof(long))
{
tmp = get_fs_long ((unsigned long *) data);
putreg(child, i, tmp);
}
return 0;
};
 
case PTRACE_GETFPREGS: { /* Get the child FPU state. */
unsigned long *tmp;
 
#ifdef CONFIG_MATH_EMULATION
if (!hard_math)
/* Not supported. */
return -EIO;
#endif
 
if (verify_area(VERIFY_WRITE, (void *) data,
sizeof(struct user_i387_struct)))
return -EIO;
if ( !child->used_math ) {
/* Simulate an empty FPU. */
child->tss.i387.hard.cwd = 0xffff037f;
child->tss.i387.hard.swd = 0xffff0000;
child->tss.i387.hard.twd = 0xffffffff;
}
if (last_task_used_math == child)
{
clts();
__asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard));
last_task_used_math = NULL;
stts();
}
tmp = (unsigned long *) &child->tss.i387.hard;
for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
{
put_fs_long (*tmp, (unsigned long *) data);
data += sizeof(long);
tmp++;
}
 
return 0;
};
 
case PTRACE_SETFPREGS: { /* Set the child FPU state. */
unsigned long *tmp;
 
#ifdef CONFIG_MATH_EMULATION
if (!hard_math)
/* Not supported. */
return -EIO;
#endif
 
if (verify_area(VERIFY_READ, (void *) data,
sizeof(struct user_i387_struct)))
return -EIO;
child->used_math = 1;
if (last_task_used_math == child)
{
/* Discard the state of the FPU */
last_task_used_math = NULL;
}
tmp = (unsigned long *) &child->tss.i387.hard;
for ( i = 0; i < sizeof(struct user_i387_struct); i += sizeof(long) )
{
*tmp = get_fs_long ((unsigned long *) data);
data += sizeof(long);
tmp++;
}
child->flags &= ~PF_USEDFPU;
return 0;
};
 
default:
return -EIO;
}
}
 
asmlinkage void syscall_trace(void)
{
if ((current->flags & (PF_PTRACED|PF_TRACESYS))
!= (PF_PTRACED|PF_TRACESYS))
return;
current->exit_code = SIGTRAP;
current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD);
schedule();
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code)
current->signal |= (1 << (current->exit_code - 1));
current->exit_code = 0;
}
 
void get_pt_regs_for_task(struct pt_regs *regs, struct task_struct *task)
{
*regs = *(struct pt_regs *) (((unsigned char *) task->tss.esp0) - MAGICNUMBER);
}
 
/entry.S
0,0 → 1,710
/*
* linux/arch/i386/entry.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
 
/*
* entry.S contains the system-call and fault low-level handling routines.
* This also contains the timer-interrupt handler, as well as all interrupts
* and faults that can result in a task-switch.
*
* NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call.
*
* I changed all the .align's to 4 (16 byte alignment), as that's faster
* on a 486.
*
* Stack layout in 'ret_from_system_call':
* ptrace needs to have all regs on the stack.
* if the order here is changed, it needs to be
* updated in fork.c:copy_process, signal.c:do_signal,
* ptrace.c and ptrace.h
*
* 0(%esp) - %ebx
* 4(%esp) - %ecx
* 8(%esp) - %edx
* C(%esp) - %esi
* 10(%esp) - %edi
* 14(%esp) - %ebp
* 18(%esp) - %eax
* 1C(%esp) - %ds
* 20(%esp) - %es
* 24(%esp) - %fs
* 28(%esp) - %gs
* 2C(%esp) - orig_eax
* 30(%esp) - %eip
* 34(%esp) - %cs
* 38(%esp) - %eflags
* 3C(%esp) - %oldesp
* 40(%esp) - %oldss
*/
 
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#define ASSEMBLY
#include <asm/smp.h>
 
EBX = 0x00
ECX = 0x04
EDX = 0x08
ESI = 0x0C
EDI = 0x10
EBP = 0x14
EAX = 0x18
DS = 0x1C
ES = 0x20
FS = 0x24
GS = 0x28
ORIG_EAX = 0x2C
EIP = 0x30
CS = 0x34
EFLAGS = 0x38
OLDESP = 0x3C
OLDSS = 0x40
 
CF_MASK = 0x00000001
IF_MASK = 0x00000200
NT_MASK = 0x00004000
VM_MASK = 0x00020000
 
/*
* these are offsets into the task-struct.
*/
state = 0
counter = 4
priority = 8
signal = 12
blocked = 16
flags = 20
dbgreg6 = 52
dbgreg7 = 56
exec_domain = 60
 
ENOSYS = 38
 
#define SAVE_ALL \
cld; \
push %gs; \
push %fs; \
push %es; \
push %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(KERNEL_DS),%edx; \
mov %dx,%ds; \
mov %dx,%es; \
movl $(USER_DS),%edx; \
mov %dx,%fs;
 
#ifdef __SMP__
 
#define GET_PROCESSOR_ID \
movl SYMBOL_NAME(apic_reg), %edx; \
movl 32(%edx), %eax;\
movl %eax,SYMBOL_NAME(apic_retval); \
shrl $24,%eax; \
andb $0x0F,%al;
 
/*
* Get the processor ID multiplied by 4
*/
 
#define GET_PROCESSOR_OFFSET(x) \
movl SYMBOL_NAME(apic_reg), x ; \
movl 32( x ), x ; \
shrl $22, x ; \
andl $0x3C, x ;
 
/* macro LEAVE_KERNEL decrements kernel_counter and resets kernel_flag and
saves processor variables if zero */
#define LEAVE_KERNEL \
pushfl; \
cli; \
GET_PROCESSOR_ID \
btrl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \
decl SYMBOL_NAME(syscall_count); \
decl SYMBOL_NAME(kernel_counter); \
jnz 1f; \
movb SYMBOL_NAME(saved_active_kernel_processor), %al; \
movb %al, SYMBOL_NAME(active_kernel_processor); \
cmpb $(NO_PROC_ID), %al; \
jnz 1f; \
lock; \
btrl $0, SYMBOL_NAME(kernel_flag); \
1: popfl;
 
/* macro ENTER_KERNEL waits for entering the kernel, increments
kernel_counter, and reloads the processor variables if necessary
uses : %eax, %edx (pushed and popped)
 
Note: We go to great pains to minimise the number of locked operations.
We want to spin without locking, and lock when we attempt an update.
The pentium has a MESI cache so the spin without lock will exit when
another CPU write invalidates our cache, and the lock is avoided when
possible so we don't play ping-pong games with the cache line.
 
*/
 
#ifndef __SMP_PROF__
 
#define SMP_PROF_A
#define SMP_PROF_B
 
#else
 
#define SMP_PROF_A movl $0,SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4);
#define SMP_PROF_B incl SYMBOL_NAME(smp_spins_syscall)(,%eax,4); \
incl SYMBOL_NAME(smp_spins_syscall_cur)(,%eax,4);
#endif
 
#define ENTER_KERNEL \
pushl %eax; \
pushl %ebx; \
pushl %ecx; \
pushl %edx; \
pushfl; \
cli; \
movl $6000, %ebx; \
movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
GET_PROCESSOR_ID \
btsl $ SMP_FROM_SYSCALL,SYMBOL_NAME(smp_proc_in_lock)(,%eax,4); \
SMP_PROF_A \
1: lock; \
btsl $0, SYMBOL_NAME(kernel_flag); \
jnc 3f; \
cmpb SYMBOL_NAME(active_kernel_processor), %al; \
je 4f; \
2: SMP_PROF_B \
btl %eax, SYMBOL_NAME(smp_invalidate_needed); \
jnc 5f; \
lock; \
btrl %eax, SYMBOL_NAME(smp_invalidate_needed); \
jnc 5f; \
movl %cr3,%edx; \
movl %edx,%cr3; \
5: sti; \
decl %ecx; \
cli; \
jne 7f; \
decl %ebx; \
jne 6f; \
call SYMBOL_NAME(non_irq_deadlock_detected); \
6: movl SYMBOL_NAME(smp_loops_per_tick), %ecx; \
cmpb SYMBOL_NAME(boot_cpu_id), %al; \
jne 7f; \
incl SYMBOL_NAME(jiffies); \
7: btl $0, SYMBOL_NAME(kernel_flag); \
jc 2b; \
jmp 1b; \
3: movb %al, SYMBOL_NAME(active_kernel_processor); \
4: incl SYMBOL_NAME(kernel_counter); \
incl SYMBOL_NAME(syscall_count); \
popfl; \
popl %edx; \
popl %ecx; \
popl %ebx; \
popl %eax;
 
 
#define RESTORE_ALL \
cmpw $(KERNEL_CS),CS(%esp); \
je 1f; \
GET_PROCESSOR_OFFSET(%edx) \
movl SYMBOL_NAME(current_set)(,%edx), %eax ; ; \
movl dbgreg7(%eax),%ebx; \
movl %ebx,%db7; \
1: LEAVE_KERNEL \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
pop %ds; \
pop %es; \
pop %fs; \
pop %gs; \
addl $4,%esp; \
iret
 
#else
 
#define RESTORE_ALL \
cmpw $(KERNEL_CS),CS(%esp); \
je 1f; \
movl SYMBOL_NAME(current_set),%eax; \
movl dbgreg7(%eax),%ebx; \
movl %ebx,%db7; \
1: \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
pop %ds; \
pop %es; \
pop %fs; \
pop %gs; \
addl $4,%esp; \
iret
#endif
 
ENTRY(lcall7)
pushfl # We get a different stack layout with call gates,
pushl %eax # which has to be cleaned up later..
SAVE_ALL
#ifdef __SMP__
ENTER_KERNEL
#endif
movl EIP(%esp),%eax # due to call gates, this is eflags, not eip..
movl CS(%esp),%edx # this is eip..
movl EFLAGS(%esp),%ecx # and this is cs..
movl %eax,EFLAGS(%esp) #
movl %edx,EIP(%esp) # Now we move them to their "normal" places
movl %ecx,CS(%esp) #
movl %esp,%eax
#ifdef __SMP__
GET_PROCESSOR_OFFSET(%edx) # Processor offset into edx
movl SYMBOL_NAME(current_set)(,%edx),%edx
#else
movl SYMBOL_NAME(current_set),%edx
#endif
pushl %eax
movl exec_domain(%edx),%edx # Get the execution domain
movl 4(%edx),%edx # Get the lcall7 handler for the domain
call *%edx
popl %eax
jmp ret_from_sys_call
 
ALIGN
handle_bottom_half:
incl SYMBOL_NAME(intr_count)
call SYMBOL_NAME(do_bottom_half)
decl SYMBOL_NAME(intr_count)
jmp 9f
ALIGN
reschedule:
pushl $ret_from_sys_call
jmp SYMBOL_NAME(schedule) # test
 
ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
#ifdef __SMP__
ENTER_KERNEL
#endif
movl $-ENOSYS,EAX(%esp)
cmpl $(NR_syscalls),%eax
jae ret_from_sys_call
movl SYMBOL_NAME(sys_call_table)(,%eax,4),%eax
testl %eax,%eax
je ret_from_sys_call
#ifdef __SMP__
GET_PROCESSOR_OFFSET(%edx)
movl SYMBOL_NAME(current_set)(,%edx),%ebx
#else
movl SYMBOL_NAME(current_set),%ebx
#endif
andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors
movl %db6,%edx
movl %edx,dbgreg6(%ebx) # save current hardware debugging status
testb $0x20,flags(%ebx) # PF_TRACESYS
jne 1f
call *%eax
movl %eax,EAX(%esp) # save the return value
jmp ret_from_sys_call
ALIGN
1: call SYMBOL_NAME(syscall_trace)
movl ORIG_EAX(%esp),%eax
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
#ifdef __SMP__
GET_PROCESSOR_OFFSET(%eax)
movl SYMBOL_NAME(current_set)(,%eax),%eax
#else
movl SYMBOL_NAME(current_set),%eax
#endif
call SYMBOL_NAME(syscall_trace)
 
ALIGN
.globl ret_from_sys_call
ret_from_sys_call:
cmpl $0,SYMBOL_NAME(intr_count)
jne 2f
9: movl SYMBOL_NAME(bh_mask),%eax
andl SYMBOL_NAME(bh_active),%eax
jne handle_bottom_half
#ifdef __SMP__
cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor)
jne 2f
#endif
movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $(VM_MASK),%eax # different then
jne 1f
cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
je 2f
1: sti
orl $(IF_MASK),%eax # these just try to make sure
andl $~NT_MASK,%eax # the program doesn't do anything
movl %eax,EFLAGS(%esp) # stupid
cmpl $0,SYMBOL_NAME(need_resched)
jne reschedule
#ifdef __SMP__
GET_PROCESSOR_OFFSET(%eax)
movl SYMBOL_NAME(current_set)(,%eax), %eax
#else
movl SYMBOL_NAME(current_set),%eax
#endif
cmpl SYMBOL_NAME(task),%eax # task[0] cannot have signals
je 2f
movl blocked(%eax),%ecx
movl %ecx,%ebx # save blocked in %ebx for signal handling
notl %ecx
andl signal(%eax),%ecx
jne signal_return
2: RESTORE_ALL
ALIGN
.globl signal_return
signal_return:
movl %esp,%ecx
pushl %ecx
testl $(VM_MASK),EFLAGS(%ecx)
jne v86_signal_return
pushl %ebx
call SYMBOL_NAME(do_signal)
popl %ebx
popl %ebx
RESTORE_ALL
ALIGN
v86_signal_return:
call SYMBOL_NAME(save_v86_state)
movl %eax,%esp
pushl %eax
pushl %ebx
call SYMBOL_NAME(do_signal)
popl %ebx
popl %ebx
RESTORE_ALL
 
ENTRY(divide_error)
pushl $0 # no error code
pushl $ SYMBOL_NAME(do_divide_error)
ALIGN
error_code:
push %fs
push %es
push %ds
pushl %eax
xorl %eax,%eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
decl %eax # eax = -1
pushl %ecx
pushl %ebx
cld
xorl %ebx,%ebx # zero ebx
xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. )
mov %gs,%bx # get the lower order bits of gs
movl %esp,%edx
xchgl %ebx, GS(%esp) # get the address and save gs.
pushl %eax # push the error code
pushl %edx
movl $(KERNEL_DS),%edx
mov %dx,%ds
mov %dx,%es
movl $(USER_DS),%edx
mov %dx,%fs
#ifdef __SMP__
ENTER_KERNEL
GET_PROCESSOR_OFFSET(%eax)
movl SYMBOL_NAME(current_set)(,%eax), %eax
#else
movl SYMBOL_NAME(current_set),%eax
#endif
movl %db6,%edx
movl %edx,dbgreg6(%eax) # save current hardware debugging status
call *%ebx
addl $8,%esp
jmp ret_from_sys_call
 
ENTRY(coprocessor_error)
pushl $0
pushl $ SYMBOL_NAME(do_coprocessor_error)
jmp error_code
 
ENTRY(device_not_available)
pushl $-1 # mark this as an int
SAVE_ALL
#ifdef __SMP__
ENTER_KERNEL
#endif
pushl $ret_from_sys_call
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je SYMBOL_NAME(math_state_restore)
pushl $0 # temporary storage for ORIG_EIP
call SYMBOL_NAME(math_emulate)
addl $4,%esp
ret
 
ENTRY(debug)
pushl $0
pushl $ SYMBOL_NAME(do_debug)
jmp error_code
 
ENTRY(nmi)
pushl $0
pushl $ SYMBOL_NAME(do_nmi)
jmp error_code
 
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
 
ENTRY(overflow)
pushl $0
pushl $ SYMBOL_NAME(do_overflow)
jmp error_code
 
ENTRY(bounds)
pushl $0
pushl $ SYMBOL_NAME(do_bounds)
jmp error_code
 
ENTRY(invalid_op)
pushl $0
pushl $ SYMBOL_NAME(do_invalid_op)
jmp error_code
 
ENTRY(coprocessor_segment_overrun)
pushl $0
pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun)
jmp error_code
 
ENTRY(reserved)
pushl $0
pushl $ SYMBOL_NAME(do_reserved)
jmp error_code
 
ENTRY(double_fault)
pushl $ SYMBOL_NAME(do_double_fault)
jmp error_code
 
ENTRY(invalid_TSS)
pushl $ SYMBOL_NAME(do_invalid_TSS)
jmp error_code
 
ENTRY(segment_not_present)
pushl $ SYMBOL_NAME(do_segment_not_present)
jmp error_code
 
ENTRY(stack_segment)
pushl $ SYMBOL_NAME(do_stack_segment)
jmp error_code
 
ENTRY(general_protection)
pushl $ SYMBOL_NAME(do_general_protection)
jmp error_code
 
ENTRY(alignment_check)
pushl $ SYMBOL_NAME(do_alignment_check)
jmp error_code
 
ENTRY(page_fault)
pushl $ SYMBOL_NAME(do_page_fault)
jmp error_code
 
ENTRY(spurious_interrupt_bug)
pushl $0
pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
jmp error_code
 
.data
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_setup) /* 0 */
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
.long SYMBOL_NAME(sys_waitpid)
.long SYMBOL_NAME(sys_creat)
.long SYMBOL_NAME(sys_link)
.long SYMBOL_NAME(sys_unlink) /* 10 */
.long SYMBOL_NAME(sys_execve)
.long SYMBOL_NAME(sys_chdir)
.long SYMBOL_NAME(sys_time)
.long SYMBOL_NAME(sys_mknod)
.long SYMBOL_NAME(sys_chmod) /* 15 */
.long SYMBOL_NAME(sys_chown)
.long SYMBOL_NAME(sys_break)
.long SYMBOL_NAME(sys_stat)
.long SYMBOL_NAME(sys_lseek)
.long SYMBOL_NAME(sys_getpid) /* 20 */
.long SYMBOL_NAME(sys_mount)
.long SYMBOL_NAME(sys_umount)
.long SYMBOL_NAME(sys_setuid)
.long SYMBOL_NAME(sys_getuid)
.long SYMBOL_NAME(sys_stime) /* 25 */
.long SYMBOL_NAME(sys_ptrace)
.long SYMBOL_NAME(sys_alarm)
.long SYMBOL_NAME(sys_fstat)
.long SYMBOL_NAME(sys_pause)
.long SYMBOL_NAME(sys_utime) /* 30 */
.long SYMBOL_NAME(sys_stty)
.long SYMBOL_NAME(sys_gtty)
.long SYMBOL_NAME(sys_access)
.long SYMBOL_NAME(sys_nice)
.long SYMBOL_NAME(sys_ftime) /* 35 */
.long SYMBOL_NAME(sys_sync)
.long SYMBOL_NAME(sys_kill)
.long SYMBOL_NAME(sys_rename)
.long SYMBOL_NAME(sys_mkdir)
.long SYMBOL_NAME(sys_rmdir) /* 40 */
.long SYMBOL_NAME(sys_dup)
.long SYMBOL_NAME(sys_pipe)
.long SYMBOL_NAME(sys_times)
.long SYMBOL_NAME(sys_prof)
.long SYMBOL_NAME(sys_brk) /* 45 */
.long SYMBOL_NAME(sys_setgid)
.long SYMBOL_NAME(sys_getgid)
.long SYMBOL_NAME(sys_signal)
.long SYMBOL_NAME(sys_geteuid)
.long SYMBOL_NAME(sys_getegid) /* 50 */
.long SYMBOL_NAME(sys_acct)
.long SYMBOL_NAME(sys_phys)
.long SYMBOL_NAME(sys_lock)
.long SYMBOL_NAME(sys_ioctl)
.long SYMBOL_NAME(sys_fcntl) /* 55 */
.long SYMBOL_NAME(sys_mpx)
.long SYMBOL_NAME(sys_setpgid)
.long SYMBOL_NAME(sys_ulimit)
.long SYMBOL_NAME(sys_olduname)
.long SYMBOL_NAME(sys_umask) /* 60 */
.long SYMBOL_NAME(sys_chroot)
.long SYMBOL_NAME(sys_ustat)
.long SYMBOL_NAME(sys_dup2)
.long SYMBOL_NAME(sys_getppid)
.long SYMBOL_NAME(sys_getpgrp) /* 65 */
.long SYMBOL_NAME(sys_setsid)
.long SYMBOL_NAME(sys_sigaction)
.long SYMBOL_NAME(sys_sgetmask)
.long SYMBOL_NAME(sys_ssetmask)
.long SYMBOL_NAME(sys_setreuid) /* 70 */
.long SYMBOL_NAME(sys_setregid)
.long SYMBOL_NAME(sys_sigsuspend)
.long SYMBOL_NAME(sys_sigpending)
.long SYMBOL_NAME(sys_sethostname)
.long SYMBOL_NAME(sys_setrlimit) /* 75 */
.long SYMBOL_NAME(sys_getrlimit)
.long SYMBOL_NAME(sys_getrusage)
.long SYMBOL_NAME(sys_gettimeofday)
.long SYMBOL_NAME(sys_settimeofday)
.long SYMBOL_NAME(sys_getgroups) /* 80 */
.long SYMBOL_NAME(sys_setgroups)
.long SYMBOL_NAME(old_select)
.long SYMBOL_NAME(sys_symlink)
.long SYMBOL_NAME(sys_lstat)
.long SYMBOL_NAME(sys_readlink) /* 85 */
.long SYMBOL_NAME(sys_uselib)
.long SYMBOL_NAME(sys_swapon)
.long SYMBOL_NAME(sys_reboot)
.long SYMBOL_NAME(old_readdir)
.long SYMBOL_NAME(old_mmap) /* 90 */
.long SYMBOL_NAME(sys_munmap)
.long SYMBOL_NAME(sys_truncate)
.long SYMBOL_NAME(sys_ftruncate)
.long SYMBOL_NAME(sys_fchmod)
.long SYMBOL_NAME(sys_fchown) /* 95 */
.long SYMBOL_NAME(sys_getpriority)
.long SYMBOL_NAME(sys_setpriority)
.long SYMBOL_NAME(sys_profil)
.long SYMBOL_NAME(sys_statfs)
.long SYMBOL_NAME(sys_fstatfs) /* 100 */
.long SYMBOL_NAME(sys_ioperm)
.long SYMBOL_NAME(sys_socketcall)
.long SYMBOL_NAME(sys_syslog)
.long SYMBOL_NAME(sys_setitimer)
.long SYMBOL_NAME(sys_getitimer) /* 105 */
.long SYMBOL_NAME(sys_newstat)
.long SYMBOL_NAME(sys_newlstat)
.long SYMBOL_NAME(sys_newfstat)
.long SYMBOL_NAME(sys_uname)
.long SYMBOL_NAME(sys_iopl) /* 110 */
.long SYMBOL_NAME(sys_vhangup)
.long SYMBOL_NAME(sys_idle)
.long SYMBOL_NAME(sys_vm86old)
.long SYMBOL_NAME(sys_wait4)
.long SYMBOL_NAME(sys_swapoff) /* 115 */
.long SYMBOL_NAME(sys_sysinfo)
.long SYMBOL_NAME(sys_ipc)
.long SYMBOL_NAME(sys_fsync)
.long SYMBOL_NAME(sys_sigreturn)
.long SYMBOL_NAME(sys_clone) /* 120 */
.long SYMBOL_NAME(sys_setdomainname)
.long SYMBOL_NAME(sys_newuname)
.long SYMBOL_NAME(sys_modify_ldt)
.long SYMBOL_NAME(sys_adjtimex)
.long SYMBOL_NAME(sys_mprotect) /* 125 */
.long SYMBOL_NAME(sys_sigprocmask)
.long SYMBOL_NAME(sys_create_module)
.long SYMBOL_NAME(sys_init_module)
.long SYMBOL_NAME(sys_delete_module)
.long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */
.long SYMBOL_NAME(sys_quotactl)
.long SYMBOL_NAME(sys_getpgid)
.long SYMBOL_NAME(sys_fchdir)
.long SYMBOL_NAME(sys_bdflush)
.long SYMBOL_NAME(sys_sysfs) /* 135 */
.long SYMBOL_NAME(sys_personality)
.long 0 /* for afs_syscall */
.long SYMBOL_NAME(sys_setfsuid)
.long SYMBOL_NAME(sys_setfsgid)
.long SYMBOL_NAME(sys_llseek) /* 140 */
.long SYMBOL_NAME(sys_getdents)
.long SYMBOL_NAME(sys_select)
.long SYMBOL_NAME(sys_flock)
.long SYMBOL_NAME(sys_msync)
.long SYMBOL_NAME(sys_readv) /* 145 */
.long SYMBOL_NAME(sys_writev)
.long SYMBOL_NAME(sys_getsid)
.long SYMBOL_NAME(sys_fdatasync)
.long SYMBOL_NAME(sys_sysctl)
.long SYMBOL_NAME(sys_mlock) /* 150 */
.long SYMBOL_NAME(sys_munlock)
.long SYMBOL_NAME(sys_mlockall)
.long SYMBOL_NAME(sys_munlockall)
.long SYMBOL_NAME(sys_sched_setparam)
.long SYMBOL_NAME(sys_sched_getparam) /* 155 */
.long SYMBOL_NAME(sys_sched_setscheduler)
.long SYMBOL_NAME(sys_sched_getscheduler)
.long SYMBOL_NAME(sys_sched_yield)
.long SYMBOL_NAME(sys_sched_get_priority_max)
.long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */
.long SYMBOL_NAME(sys_sched_rr_get_interval)
.long SYMBOL_NAME(sys_nanosleep)
.long SYMBOL_NAME(sys_mremap)
.long 0,0
.long SYMBOL_NAME(sys_vm86) /* 166 */
.long 0 /* 167 */
.long 0 /* 168 STREAMS poll */
.long 0 /* 169 */
.long 0,0,0,0,0,0,0,0,0,0 /* 170 - 179 */
.long 0,0,0,0,0,0,0,0 /* 180 - 187 */
.long 0 /* 188 STREAMS getpmsg */
.long 0 /* 189 STREAMS putpmsg */
.space (NR_syscalls-189)*4
/head.S
0,0 → 1,523
/*
* linux/arch/i386/head.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
 
/*
* head.S contains the 32-bit startup code.
*/
 
.text
#include <linux/config.h>
#include <linux/tasks.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <linux/config.h>
#include <asm/page.h>
#include <asm/pgtable.h>
 
 
#define CL_MAGIC_ADDR 0x90020
#define CL_MAGIC 0xA33F
#define CL_BASE_ADDR 0x90000
#define CL_OFFSET 0x90022
 
/*
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
* address 0x00101000 for a compressed boot).
*/
ENTRY(stext)
ENTRY(_stext)
startup_32:
cld
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
#ifdef __SMP__
orw %bx,%bx
jz 1f /* Initial CPU cleans BSS */
/*
* Set up the stack
*/
mov %ax,%ss
xorl %eax,%eax
movw %cx, %ax
movl %eax,%esp
pushl $0
popfl
jmp checkCPUtype
1:
lss stack_start,%esp
#endif __SMP__
/*
* Clear BSS first so that there are no surprises...
*/
xorl %eax,%eax
movl $ SYMBOL_NAME(_edata),%edi
movl $ SYMBOL_NAME(_end),%ecx
subl %edi,%ecx
cld
rep
stosb
/*
* start system 32-bit setup. We need to re-do some of the things done
* in 16-bit mode for the "real" operations.
*/
call setup_idt
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/
pushl $0
popfl
/*
* Copy bootup parameters out of the way. First 2kB of
* _empty_zero_page is for boot parameters, second 2kB
* is for the command line.
*/
movl $0x90000,%esi
movl $ SYMBOL_NAME(empty_zero_page),%edi
movl $512,%ecx
cld
rep
movsl
xorl %eax,%eax
movl $512,%ecx
rep
stosl
cmpw $(CL_MAGIC),CL_MAGIC_ADDR
jne 1f
movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
movzwl CL_OFFSET,%esi
addl $(CL_BASE_ADDR),%esi
movl $2048,%ecx
rep
movsb
1:
#ifdef __SMP__
checkCPUtype:
#endif
 
/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
 
movl $3, SYMBOL_NAME(x86)
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS in ecx
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386
movl $4,SYMBOL_NAME(x86)
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
popfl # if we are on a straight 486DX, SX, or
pushfl # 487SX we can't change it
popl %eax # Also if we are on a Cyrix 6x86(L)
xorl %ecx,%eax # OTOH 6x86MXs and MIIs check OK
andl $0x200000,%eax
je is486x
 
isnew: pushl %ecx # restore original EFLAGS
popfl
incl SYMBOL_NAME(have_cpuid) # we have CPUID
/*
* Technically we should use CPUID 0 to see if we have CPUID 1!
*/
/* get processor type */
movl $1, %eax # Use the CPUID instruction to
#ifdef GAS_KNOWS_CPUID
cpuid # check the processor type
#else
.byte 0x0f, 0xa2 # check the processor type
#endif
movb %al, %cl # save reg for future use
andb $0x0f,%ah # mask processor family
movb %ah,SYMBOL_NAME(x86)
andb $0xf0, %al # mask model
shrb $4, %al
movb %al,SYMBOL_NAME(x86_model)
andb $0x0f, %cl # mask mask revision
movb %cl,SYMBOL_NAME(x86_mask)
movl %edx,SYMBOL_NAME(x86_capability)
/* get vendor info */
xorl %eax, %eax # call CPUID with 0 -> return vendor ID
#ifdef GAS_KNOWS_CPUID
cpuid
#else
.byte 0x0f, 0xa2 # CPUID
#endif
movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars
 
movl %cr0,%eax # 486+
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
 
/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
* clobbering the new BX chipset used with the Pentium II, which has a register
* at the same addresses as those used to access the Cyrix special configuration
* registers (CCRs).
*/
/*
* A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
* (and it _must_ be 5 divided by 2) while other CPUs change
* them in undefined ways. We need to know this since we may
* need to enable the CPUID instruction at least.
* We couldn't use this test before since the PPro and PII behave
* like Cyrix chips in this respect.
*/
is486x: xor %ax,%ax
sahf
movb $5,%ax
movb $2,%bx
div %bl
lahf
cmpb $2,%ah
jne ncyrix
/*
* N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
* so do not try to "optimize" it! For the same reason we
* do all this with interrupts off.
*/
#define setCx86(reg, val) \
movb reg,%ax; \
outb %ax,$0x22; \
movb val,%ax; \
outb %ax,$0x23
 
#define getCx86(reg) \
movb reg,%ax; \
outb %ax,$0x22; \
inb $0x23,%ax
 
cli
getCx86($0xc3) # get CCR3
movb %ax,%cx # Save old value
movb %ax,%bx
andb $0x0f,%bx # Enable access to all config registers
orb $0x10,%bx # by setting bit 4
setCx86($0xc3,%bx)
 
getCx86($0xfe) # DIR0 : let's check this is a 6x86(L)
andb $0xf0,%ax # should be 3xh
cmpb $0x30,%ax #
jne n6x86
 
getCx86($0xe8) # now we can get CCR4
orb $0x80,%ax # and set bit 7 (CPUIDEN)
movb %ax,%bx # to enable CPUID execution
setCx86($0xe8,%bx)
 
getCx86($0xe9) # CCR5 : we reset the SLOP bit
andb $0xfd,%ax # so that udelay calculation
movb %ax,%bx # is correct on 6x86(L) CPUs
setCx86($0xe9,%bx)
setCx86($0xc3,%cx) # Restore old CCR3
sti
jmp isnew # We enabled CPUID now
 
n6x86: setCx86($0xc3,%cx) # Restore old CCR3
sti
ncyrix: pushl %ecx # restore original EFLAGS
popfl
movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is386: pushl %ecx # restore original EFLAGS
popfl
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
#ifdef __SMP__
movb ready,%al
orb %al,%al
jz 3f
movl $ SYMBOL_NAME(swapper_pg_dir), %eax
movl %eax, %cr3
#ifdef GAS_KNOWS_CR4
movl %cr4,%eax
orl $16,%eax
movl %eax,%cr4
#else
.byte 0x0f,0x20,0xe0
orl $16,%eax
.byte 0x0f,0x22,0xe0
#endif
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
jmp 4f
#endif
3:
call setup_paging
#ifdef __SMP__
incb ready
#endif
4:
lgdt gdt_descr
lidt idt_descr
ljmp $(KERNEL_CS),$1f
1: movl $(KERNEL_DS),%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
#ifdef __SMP__
movl $(KERNEL_DS), %eax
mov %ax,%ss # Reload the stack pointer (segment only)
#else
lss stack_start,%esp # Load processor stack
#endif
xorl %eax,%eax
lldt %ax
pushl %eax # These are the parameters to main :-)
pushl %eax
pushl %eax
cld # gcc2 wants the direction flag cleared at all times
call SYMBOL_NAME(start_kernel)
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.
 
#ifdef __SMP__
ready: .byte 0
#endif
 
/*
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
movb $0,SYMBOL_NAME(hard_math)
clts
fninit
fstsw %ax
cmpb $0,%al
je 1f
movl %cr0,%eax /* no coprocessor: have to set bits */
xorl $4,%eax /* set EM */
movl %eax,%cr0
ret
ALIGN
1: movb $1,SYMBOL_NAME(hard_math)
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
 
/*
* setup_idt
*
* sets up a idt with 256 entries pointing to
* ignore_int, interrupt gates. It doesn't actually load
* idt - that can be done only after paging has been enabled
* and the kernel moved to PAGE_OFFSET. Interrupts
* are enabled elsewhere, when we can be relatively
* sure everything is ok.
*/
setup_idt:
lea ignore_int,%edx
movl $(KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
 
lea SYMBOL_NAME(idt),%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
 
 
/*
* Setup_paging
*
* This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping
* the first 4MB. The rest are initialized later.
*
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
*/
ALIGN
setup_paging:
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
xorl %eax,%eax
movl $ SYMBOL_NAME(swapper_pg_dir),%edi /* swapper_pg_dir is at 0x1000 */
cld;rep;stosl
/* Identity-map the kernel in low 4MB memory for ease of transition */
/* set present bit/user r/w */
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)
/* But the real place is at PAGE_OFFSET */
/* set present bit/user r/w */
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+__USER_PGD_PTRS*4
movl $ SYMBOL_NAME(pg0)+4092,%edi
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
std
1: stosl /* fill the page backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
movl $ SYMBOL_NAME(swapper_pg_dir),%eax
movl %eax,%cr3 /* cr3 - page directory start */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* set paging (PG) bit */
ret /* this also flushes the prefetch-queue */
 
/*
* page 0 is made non-existent, so that kernel NULL pointer references get
* caught. Thus the swapper page directory has been moved to 0x1000
*
* XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
* with the introduction of the compressed boot code. Theoretically,
* the original design of overlaying the startup code with the swapper
* page directory is still possible --- it would reduce the size of the kernel
* by 2-3k. This would be a good thing to do at some point.....
*/
.org 0x1000
ENTRY(swapper_pg_dir)
/*
* The page tables are initialized to only 4MB here - the final page
* tables are set up later depending on memory size.
*/
.org 0x2000
ENTRY(pg0)
 
.org 0x3000
ENTRY(empty_bad_page)
 
.org 0x4000
ENTRY(empty_bad_page_table)
 
.org 0x5000
ENTRY(empty_zero_page)
 
.org 0x6000
 
stack_start:
.long SYMBOL_NAME(init_user_stack)+4096
.long KERNEL_DS
 
/* NOTE: keep the idt short behind the above '.org 0x6000'
It must fit completely within _one_ page */
ENTRY(idt)
.fill 256,8,0 # idt is uninitialized
 
/* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n"
ALIGN
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $(KERNEL_DS),%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call SYMBOL_NAME(printk)
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
 
/*
* The interrupt descriptor table has room for 256 idt's
*/
ALIGN
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long __PAGE_OFFSET+SYMBOL_NAME(idt)
 
ALIGN
.word 0
gdt_descr:
#ifdef CONFIG_APM
.word (11+2*NR_TASKS)*8-1
#else
.word (8+2*NR_TASKS)*8-1
#endif
.long __PAGE_OFFSET+SYMBOL_NAME(gdt)
 
/*
* This gdt setup gives the kernel a 1GB address space at virtual
* address PAGE_OFFSET - space enough for expansion, I hope.
*/
 
#define upper_seg(type,dpl,base,limit) \
((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
(((limit)>>12) & 0xf0000) | \
((dpl)<<13) | \
(0x00c09000) | \
((type)<<8)
 
#define lower_seg(type,dpl,base,limit) \
(((base) & 0x0000ffff)<<16) | \
(((limit)>>12) & 0x0ffff)
 
#define x86_seg(type,dpl,base,limit) \
.long lower_seg(type,dpl,base,limit); \
.long upper_seg(type,dpl,base,limit)
 
ENTRY(gdt)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
 
/* 0x10 kernel 1GB code at 0xC0000000: */
x86_seg(0xa,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
 
/* 0x18 kernel 1GB data at 0xC0000000: */
x86_seg(0x2,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
 
/* 0x23 user 3GB code at 0x00000000: */
x86_seg(0xa,3,0,__PAGE_OFFSET-1)
 
/* 0x2b user 3GB data at 0x00000000: */
x86_seg(0x2,3,0,__PAGE_OFFSET-1)
 
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
#ifdef CONFIG_APM
.quad 0x00c09a0000000000 /* APM CS code */
.quad 0x00809a0000000000 /* APM CS 16 code (16 bit) */
.quad 0x00c0920000000000 /* APM DS data */
#endif
/ldt.c
0,0 → 1,136
/*
* linux/kernel/ldt.c
*
* Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/ldt.h>
#include <asm/ptrace.h>
 
static int read_ldt(void * ptr, unsigned long bytecount)
{
int error;
void * address = current->ldt;
unsigned long size;
 
if (!ptr)
return -EINVAL;
size = LDT_ENTRIES*LDT_ENTRY_SIZE;
if (!address) {
address = &default_ldt;
size = sizeof(default_ldt);
}
if (size > bytecount)
size = bytecount;
error = verify_area(VERIFY_WRITE, ptr, size);
if (error)
return error;
memcpy_tofs(ptr, address, size);
return size;
}
 
static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info)
{
unsigned long base, limit;
/* linear address of first and last accessible byte */
unsigned long first, last;
 
base = ldt_info->base_addr;
limit = ldt_info->limit;
if (ldt_info->limit_in_pages)
limit = limit * PAGE_SIZE + PAGE_SIZE - 1;
 
first = base;
last = limit + base;
 
/* segment grows down? */
if (ldt_info->contents == 1) {
/* data segment grows down */
first = base+limit+1;
last = base+65535;
if (ldt_info->seg_32bit)
last = base-1;
}
return (last >= first && last < TASK_SIZE);
}
 
static int write_ldt(struct pt_regs * regs, void * ptr, unsigned long bytecount, int oldmode)
{
struct modify_ldt_ldt_s ldt_info;
unsigned long *lp;
int error, i;
 
if (bytecount != sizeof(ldt_info))
return -EINVAL;
error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info));
if (error)
return error;
 
memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info));
 
if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
return -EINVAL;
 
if (!limits_ok(&ldt_info) && (oldmode || ldt_info.seg_not_present == 0))
return -EINVAL;
 
if (!current->ldt) {
for (i=1 ; i<NR_TASKS ; i++) {
if (task[i] == current) {
if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
return -ENOMEM;
memset(current->ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES);
load_ldt(i);
}
}
}
lp = (unsigned long *) &current->ldt[ldt_info.entry_number];
/* Allow LDTs to be cleared by the user. */
if (ldt_info.base_addr == 0 && ldt_info.limit == 0
&& (oldmode ||
( ldt_info.contents == 0
&& ldt_info.read_exec_only == 1
&& ldt_info.seg_32bit == 0
&& ldt_info.limit_in_pages == 0
&& ldt_info.seg_not_present == 1
&& ldt_info.useable == 0 )) ) {
unsigned short sel =(ldt_info.entry_number <<3) | 7;
if (regs->fs == sel || regs->gs == sel)
return -EBUSY;
*lp = 0;
*(lp+1) = 0;
return 0;
}
*lp = ((ldt_info.base_addr & 0x0000ffff) << 16) |
(ldt_info.limit & 0x0ffff);
*(lp+1) = (ldt_info.base_addr & 0xff000000) |
((ldt_info.base_addr & 0x00ff0000)>>16) |
(ldt_info.limit & 0xf0000) |
(ldt_info.contents << 10) |
((ldt_info.read_exec_only ^ 1) << 9) |
(ldt_info.seg_32bit << 22) |
(ldt_info.limit_in_pages << 23) |
((ldt_info.seg_not_present ^1) << 15) |
0x7000;
if (!oldmode) *(lp+1) |= (ldt_info.useable << 20);
return 0;
}
 
asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
{
if (func == 0)
return read_ldt(ptr, bytecount);
if (func == 1)
return write_ldt((struct pt_regs *) &func, ptr, bytecount, 1);
if (func == 0x11)
return write_ldt((struct pt_regs *) &func, ptr, bytecount, 0);
return -ENOSYS;
}
/mtrr.c
0,0 → 1,602
/*
* Read and write Memory Type Range Registers (MTRRs)
*
* These machine specific registers contain information about
* caching of memory regions on Intel processors.
*
* This code has been derived from pform_mod.c by M. Tisch"auser
* (email martin@ikcbarka.fzk.de). Special thanks to mingo for
* his hint.
*
* (c) 1997 M. Ohlenroth <moh@informatik.tu-chemnitz.de>
* NO WARRANTY: use this code at your own risk!
*
* This code is released under the GNU public license version 2 or
* later.
*
* modified to have a /proc/mtrr interface by M. Fr"ohlich, Jan. 1998
* <frohlich@na.uni-tuebingen.de>
* the user Interface is partly taken form mtrr-patch-v1.5
* Richard Gooch may be reached by email at rgooch@atnf.csiro.au
* The postal address is:
* Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*
*/
 
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/processor.h>
#include <asm/mtrr.h>
 
#define MTRR_CAP 0x0fe /* MTRRcap register */
#define MTRR_VARIABLE 0x200 /* variable length registers */
#define MTRR_FIXED64K 0x250 /* fixed size registers 64k */
#define MTRR_FIXED16K 0x258 /* fixed size registers 16K */
#define MTRR_FIXED4K 0x268 /* fixed size registers 4K */
#define MTRR_DEFTYPE 0x2ff /* MTRRdefType register */
 
/*
* data type for the MTRRcap register
*/
typedef struct {
__u64 VCNT : 8 __attribute__ ((packed)),
FIX : 1 __attribute__ ((packed)),
__reserved_2 : 1 __attribute__ ((packed)),
WC : 1 __attribute__ ((packed)),
__reserved_1 : 53 __attribute__ ((packed));
} MTRRcap_t __attribute__ ((packed));
 
 
/*
* data type for the MTRRdefType register
*/
typedef struct {
__u64 Type : 8 __attribute__ ((packed)),
__reserved_1 : 2 __attribute__ ((packed)),
FE : 1 __attribute__ ((packed)),
E : 1 __attribute__ ((packed)),
__reserved_2 : 52 __attribute__ ((packed));
} MTRRdefType_t __attribute__ ((packed));
 
/* FIXME implement the entry struct */
typedef struct MTRRfix64K_t {
__u64 raw;
} MTRRfix64K_t __attribute__ ((packed));
 
typedef struct MTRRfix16K_t {
__u64 raw;
} MTRRfix16K_t __attribute__ ((packed));
 
typedef struct MTRRfix4K_t {
__u64 raw;
} MTRRfix4K_t __attribute__ ((packed));
 
/*
* data type for a pair of variable MTRR registers
*/
typedef struct {
struct {
__u64 Type : 8 __attribute__ ((packed)),
__reserved_1 : 4 __attribute__ ((packed)),
PhysBase : 24 __attribute__ ((packed)),
__reserved_2 : 28 __attribute__ ((packed));
} MTRRphysBase __attribute__ ((packed));
 
struct {
__u64 __reserved_3 : 11 __attribute__ ((packed)),
V : 1 __attribute__ ((packed)),
PhysMask : 24 __attribute__ ((packed)),
__reserved_4 : 28 __attribute__ ((packed));
} MTRRphysMask __attribute__ ((packed));
} MTRRvar_t __attribute__ ((packed));
 
#define RAW_ACCESS64(data) (*(unsigned long long *)(&(data)))
 
/*
* MTRR configuration struct
*/
struct mtrr_cntl_t {
MTRRcap_t MTRRcap; /* MTRR capability register */
MTRRdefType_t MTRRdefType; /* MTRR default type register */
MTRRfix64K_t fixed64; /* fixed length entries (raw data) */
MTRRfix16K_t fixed16[2];
MTRRfix4K_t fixed4[8];
MTRRvar_t variable[0]; /* variable type entries */
};
 
static struct mtrr_cntl_t *mtrrcntl = NULL;
 
/*
* Deletes the variable MTRR *MTRRvar
*/
static inline void MTRRvar_delete(MTRRvar_t *MTRRvar)
{
RAW_ACCESS64(MTRRvar->MTRRphysBase) = 0;
RAW_ACCESS64(MTRRvar->MTRRphysMask) = 0;
}
 
 
/*
* Sets the variable MTRR *MTRRvar
*/
static inline void MTRRvar_set(MTRRvar_t *MTRRvar, unsigned int type,
unsigned long base, unsigned long size)
{
unsigned long val;
base >>= 12;
size >>= 12;
MTRRvar->MTRRphysBase.Type = type;
MTRRvar->MTRRphysBase.PhysBase = base;
MTRRvar->MTRRphysMask.V = 1;
val = 1<<25;
while (0 == (val & size)) val |= (val>>1);
MTRRvar->MTRRphysMask.PhysMask = val;
}
 
 
/*
* returns 1 if the variable MTRR entry *MTRRvar is valid, 0 otherwise
*/
static inline int MTRRvar_is_valid(const MTRRvar_t *MTRRvar)
{
return MTRRvar->MTRRphysMask.V;
}
 
/*
* returns the type of the variable MTRR entry *MTRRvar
*/
static inline int MTRRvar_get_type(const MTRRvar_t *MTRRvar)
{
return MTRRvar->MTRRphysBase.Type;
}
 
/*
* returns the base of the variable MTRR entry *MTRRvar
*/
static inline unsigned long long MTRRvar_get_base(const MTRRvar_t *MTRRvar)
{
return ((unsigned long long)MTRRvar->MTRRphysBase.PhysBase) << 12;
}
 
/*
* returns the size of the variable MTRR entry *MTRRvar
*/
static inline unsigned long long MTRRvar_get_size(const MTRRvar_t *MTRRvar)
{
if (MTRRvar->MTRRphysMask.PhysMask == 0) {
return 0;
} else {
unsigned long size = 1;
const unsigned long Mask = MTRRvar->MTRRphysMask.PhysMask;
while (0 == (Mask & size)) size <<= 1;
return ((unsigned long long)size) << 12;
}
}
 
/*
* returns the eflags register
*/
static inline int read_eflags(void)
{
int ret;
asm volatile (
"pushfl\n\t"
"popl %%eax\n\t"
:"=a" (ret)
:
);
return ret;
}
 
/*
* writes the eflags register
*/
static inline void write_eflags(int flag)
{
asm volatile (
"pushl %%eax\n\t"
"popfl\n\t"
:
:"a" (flag)
);
}
 
/*
* returns 1 if the mtrr's are supported by the current processor, 0 otherwise
*/
static inline int mtrr_detect(void) {
unsigned long flags;
int eflags;
int val;
#define MSR_MASK 0x20
#define MTRR_MASK 0x1000
#define CPUID_MASK 0x200000
/* this function may be called before the cpu_data array has
been initialized */
save_flags(flags); sti();
eflags = read_eflags();
write_eflags(eflags ^ CPUID_MASK);
if (!((eflags ^ read_eflags()) & CPUID_MASK)) {
write_eflags(eflags);
restore_flags(flags);
return 0;
}
write_eflags(eflags);
restore_flags(flags);
 
/* get the cpuid level */
asm volatile (
"xorl %%eax,%%eax\n\t"
"cpuid"
:"=a"(val)::"ebx","ecx","edx"
);
if (val < 1) return 0;
/* get the x86_capability value */
asm volatile (
"movl $1,%%eax\n\t"
"cpuid"
:"=d"(val)::"ebx","ecx","eax"
);
if (!(val & MSR_MASK)) return 0;
if (!(val & MTRR_MASK)) return 0;
 
return 1;
#undef MSR_MASK
#undef MTRR_MASK
#undef CPUID_MASK
}
 
 
/*
* reads the mtrr configuration of the actual processor and returns
* this configuration on sucess. returns NULL if an error occured or
* if mtrr's are not supported.
*/
static struct mtrr_cntl_t *read_mtrr_configuration (void) {
struct mtrr_cntl_t *mtrrcntl;
int i;
size_t size;
MTRRcap_t MTRRcap;
 
if (!mtrr_detect()) {
printk("/proc/mtrr: MTRR's are NOT supported\n");
return NULL;
}
 
RAW_ACCESS64(MTRRcap) = rdmsr(MTRR_CAP);
 
/* #define DUMP_MTRR */
#ifdef DUMP_MTRR
{
/* Written for a bugreport to Gigabyte ... */
inline void print_msr(int num) {
unsigned long long tmp = rdmsr(num);
printk("MSR #%#06x: 0x%08lx%08lx\n",
num , (unsigned long)(tmp >> 32),
(unsigned long)tmp);
}
print_msr(MTRR_CAP);
/* all variable type */
for (i=0;i < MTRRcap.VCNT;i++) {
print_msr(MTRR_VARIABLE+2*i);
print_msr(MTRR_VARIABLE+2*i+1);
}
/* all fixed type */
print_msr(MTRR_FIXED64K);
print_msr(MTRR_FIXED16K);
print_msr(MTRR_FIXED16K+1);
for (i=0;i<8;i++)
print_msr(MTRR_FIXED4K+i);
print_msr(MTRR_DEFTYPE);
}
#endif
size = sizeof(struct mtrr_cntl_t) + sizeof(MTRRvar_t)*MTRRcap.VCNT;
if (NULL == (mtrrcntl = kmalloc(size, GFP_KERNEL))) return NULL;
memset(mtrrcntl, 0, size);
 
/* read MTRRcap register */
mtrrcntl->MTRRcap = MTRRcap;
 
/* read MTRRdefType register */
RAW_ACCESS64(mtrrcntl->MTRRdefType) = rdmsr(MTRR_DEFTYPE);
 
/* read fixed length entries */
if (mtrrcntl->MTRRdefType.E && mtrrcntl->MTRRdefType.FE) {
mtrrcntl->fixed64.raw = rdmsr(MTRR_FIXED64K);
mtrrcntl->fixed16[0].raw = rdmsr(MTRR_FIXED16K);
mtrrcntl->fixed16[1].raw = rdmsr(MTRR_FIXED16K+1);
for (i=0;i<8;i++)
mtrrcntl->fixed4[i].raw = rdmsr(MTRR_FIXED4K+i);
}
 
/* read variable length entries */
if (mtrrcntl->MTRRdefType.E) {
const int vcnt = mtrrcntl->MTRRcap.VCNT;
for (i = 0 ; i < vcnt ; i++) {
RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) =
rdmsr(MTRR_VARIABLE + 2*i);
RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) =
rdmsr(MTRR_VARIABLE + 2*i + 1);
}
}
 
return mtrrcntl;
}
 
/*
* initializes the global mtrr configuration
*/
/*__init_function(void init_mtrr_config(void)) FIXME*/
void init_mtrr_config(void)
{
mtrrcntl = read_mtrr_configuration();
}
 
 
/* write back and invalidate cache */
static inline void wbinvd(void)
{
asm volatile("wbinvd");
}
 
/* flush tlb's */
static inline void flush__tlb(void)
{
asm volatile (
"movl %%cr3, %%eax\n\t"
"movl %%eax, %%cr3\n\t"
:
:
: "memory", "eax");
}
 
/* clear page global enable and return previous value */
static inline unsigned long clear_pge(void)
{
unsigned long ret;
asm volatile (
"movl %%cr4, %%eax\n\t"
"movl %%eax, %%edx\n\t"
"andl $0x7f, %%edx\n\t"
"movl %%edx, %%cr4\n\t"
: "=a" (ret)
:
: "memory", "cc", "eax", "edx");
return ret;
}
 
/* restores page global enable bit */
static inline void restore_pge(unsigned long cr4)
{
asm volatile (
"movl %0, %%cr4\n\t"
:
: "r" (cr4)
: "memory");
}
 
/* ... */
static inline void disable_cache(void)
{
asm volatile (
"movl %%cr0, %%eax\n\t"
"orl $0x40000000, %%eax\n\t"
"movl %%eax, %%cr0\n\t"
:
:
:"memory", "cc", "eax");
}
 
/* ... */
static inline void enable_cache(void)
{
asm volatile (
"movl %%cr0, %%eax\n\t"
"andl $0xbfffffff, %%eax\n\t"
"movl %%eax, %%cr0"
:
:
:"memory", "cc", "eax");
}
 
/* clear the MTRRdefType.E and MTRRdefType.FE flag to disable these MTRR's */
static inline void disable_mtrr(void)
{
MTRRdefType_t MTRRdefType;
 
RAW_ACCESS64(MTRRdefType) = rdmsr(MTRR_DEFTYPE);
MTRRdefType.E = 0;
MTRRdefType.FE = 0;
wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
}
 
/*
* written from pseudocode from intel
* (PentiumPro Family Developers manual Volume 3, P 322)
*
*/
static inline unsigned long pre_mtrr_change(void)
{
unsigned long cr4;
 
cr4 = clear_pge();
 
wbinvd();
 
disable_cache();
 
wbinvd();
 
flush__tlb();
disable_mtrr();
 
return cr4;
}
 
/*
* written from pseudocode from intel
* (PentiumPro Family Developers manual Volume 3, P 322)
*/
static inline void post_mtrr_change(MTRRdefType_t MTRRdefType,unsigned long cr4)
{
wbinvd();
 
flush__tlb();
 
wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
 
enable_cache();
 
restore_pge(cr4);
}
 
/*
* writes all fixed mtrr's
*/
static inline void set_mtrr_fixed(void) {
int i;
 
wrmsr(MTRR_FIXED64K,mtrrcntl->fixed64.raw);
wrmsr(MTRR_FIXED16K+0,mtrrcntl->fixed16[0].raw);
wrmsr(MTRR_FIXED16K+1,mtrrcntl->fixed16[1].raw);
for (i=0;i<8;i++)
wrmsr(MTRR_FIXED4K+i,mtrrcntl->fixed4[i].raw);
}
 
/*
* writes all variable mtrr's
*/
static inline void set_mtrr_variable(void) {
int i;
const int vcnt = mtrrcntl->MTRRcap.VCNT;
 
for (i = 0 ; i < vcnt ; i++ ) {
wrmsr(MTRR_VARIABLE +2*i,
RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase));
wrmsr(MTRR_VARIABLE +2*i+1,
RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask));
}
}
 
 
/*
* compares the mtrr_cntl_t structure second with that set by
* the boot processor,
* returns 0 if equal,
* -1 if the structs are not initialized,
* 1 if they are different
*
* the *second struct is assumed to be local, it is not locked!
*/
static inline int compare_mtrr_configuration(struct mtrr_cntl_t *second) {
int i, result = 0;
 
if (NULL == mtrrcntl) { result = -1; goto end; }
if (NULL == second) { result = -1; goto end; }
if (RAW_ACCESS64(mtrrcntl->MTRRcap)
!= RAW_ACCESS64(second->MTRRcap)) {
result = 1; goto end;
}
 
if (RAW_ACCESS64(mtrrcntl->MTRRdefType)
!= RAW_ACCESS64(second->MTRRdefType)) {
result = 1; goto end;
}
 
if (mtrrcntl->fixed64.raw != second->fixed64.raw) {
result = 1; goto end;
}
if (mtrrcntl->fixed16[0].raw != second->fixed16[0].raw) {
result = 1; goto end;
}
if (mtrrcntl->fixed16[1].raw != second->fixed16[1].raw) {
result = 1; goto end;
}
 
for (i=0;i<8;i++)
if (mtrrcntl->fixed4[i].raw != second->fixed4[i].raw) {
result = 1; goto end;
}
{
const int vcnt = mtrrcntl->MTRRcap.VCNT;
for (i = 0; i < vcnt; i++) {
if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) !=
RAW_ACCESS64(second->variable[i].MTRRphysBase)) {
result = 1; goto end;
}
if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) !=
RAW_ACCESS64(second->variable[i].MTRRphysMask)) {
result = 1; goto end;
}
}
}
 
end:
 
return result;
}
 
/*
* compares the mtrr configuration of the current processor with the
* main configuration and overwrites the mtrr's in the processor if they
* differ. (fixes a bug in the GA-686DX mainboard BIOS)
*/
void check_mtrr_config(void) {
unsigned long cr4;
unsigned long flags;
struct mtrr_cntl_t *this_cpu_setting;
int result;
 
save_flags(flags); sti();
 
/* if global struct is not initialized return */
if (mtrrcntl == NULL) {
restore_flags(flags);
return;
}
 
/* disable MTRR feature if this_cpu_setting == NULL */
/* read mtrr configuration of this cpu */
this_cpu_setting = read_mtrr_configuration();
if (this_cpu_setting == NULL) {
printk("/proc/mtrr: MTRR's are NOT supported by cpu %i.\n",
smp_processor_id());
restore_flags(flags);
 
return;
}
/* compare mtrr configuration */
result = compare_mtrr_configuration(this_cpu_setting);
kfree(this_cpu_setting);
/* return if mtrr setting is correct */
if (0 >= result) {
restore_flags(flags);
return;
}
/* prepare cpu's for setting mtrr's */
cr4 = pre_mtrr_change();
 
/* set all mtrr's */
set_mtrr_fixed();
set_mtrr_variable();
 
/* prepare cpu's for running */
post_mtrr_change(mtrrcntl->MTRRdefType, cr4);
 
restore_flags(flags);
 
printk("\nBIOS bug workaround: MTRR configuration changed on cpu "
"%i.\n", smp_processor_id());
}
 
/sys_i386.c
0,0 → 1,176
/*
* linux/arch/i386/kernel/sys_i386.c
*
* This file contains various random system calls that
* have a non-standard calling sequence on the Linux/i386
* platform.
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/mman.h>
 
#include <asm/segment.h>
 
/*
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way unix traditionally does this, though.
*/
asmlinkage int sys_pipe(unsigned long * fildes)
{
int fd[2];
int error;
 
error = verify_area(VERIFY_WRITE,fildes,8);
if (error)
return error;
error = do_pipe(fd);
if (error)
return error;
put_fs_long(fd[0],0+fildes);
put_fs_long(fd[1],1+fildes);
return 0;
}
 
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux/i386 didn't use to be able to handle more than
* 4 system call parameters, so these system calls used a memory
* block for parameter passing..
*/
asmlinkage int old_mmap(unsigned long *buffer)
{
int error;
unsigned long flags;
struct file * file = NULL;
 
error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
if (error)
return error;
flags = get_user(buffer+3);
if (!(flags & MAP_ANONYMOUS)) {
unsigned long fd = get_user(buffer+4);
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
}
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
return do_mmap(file, get_user(buffer), get_user(buffer+1),
get_user(buffer+2), flags, get_user(buffer+5));
}
 
extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
 
asmlinkage int old_select(unsigned long *buffer)
{
int n;
fd_set *inp;
fd_set *outp;
fd_set *exp;
struct timeval *tvp;
 
n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long));
if (n)
return n;
n = get_user(buffer);
inp = (fd_set *) get_user(buffer+1);
outp = (fd_set *) get_user(buffer+2);
exp = (fd_set *) get_user(buffer+3);
tvp = (struct timeval *) get_user(buffer+4);
return sys_select(n, inp, outp, exp, tvp);
}
 
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
*
* This is really horribly ugly.
*/
asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
{
int version;
 
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff;
 
if (call <= SEMCTL)
switch (call) {
case SEMOP:
return sys_semop (first, (struct sembuf *)ptr, second);
case SEMGET:
return sys_semget (first, second, third);
case SEMCTL: {
union semun fourth;
int err;
if (!ptr)
return -EINVAL;
if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
return err;
fourth.__pad = (void *) get_fs_long(ptr);
return sys_semctl (first, second, third, fourth);
}
default:
return -EINVAL;
}
if (call <= MSGCTL)
switch (call) {
case MSGSND:
return sys_msgsnd (first, (struct msgbuf *) ptr,
second, third);
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
int err;
if (!ptr)
return -EINVAL;
if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
return err;
memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
sizeof (tmp));
return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
}
case 1: default:
return sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third);
}
case MSGGET:
return sys_msgget ((key_t) first, second);
case MSGCTL:
return sys_msgctl (first, second, (struct msqid_ds *) ptr);
default:
return -EINVAL;
}
if (call <= SHMCTL)
switch (call) {
case SHMAT:
switch (version) {
case 0: default: {
ulong raddr;
int err;
if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
return err;
err = sys_shmat (first, (char *) ptr, second, &raddr);
if (err)
return err;
put_fs_long (raddr, (ulong *) third);
return 0;
}
case 1: /* iBCS2 emulator entry point */
if (get_fs() != get_ds())
return -EINVAL;
return sys_shmat (first, (char *) ptr, second, (ulong *) third);
}
case SHMDT:
return sys_shmdt ((char *)ptr);
case SHMGET:
return sys_shmget (first, second, third);
case SHMCTL:
return sys_shmctl (first, second, (struct shmid_ds *) ptr);
default:
return -EINVAL;
}
return -EINVAL;
}
/traps.c
0,0 → 1,460
/*
* linux/arch/i386/traps.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
 
/*
* 'Traps.c' handles hardware traps and faults after we have saved some
* state in 'asm.s'. Currently mostly a debugging-aid, will be extended
* to mainly kill the offending process (probably by giving it a signal,
* but possibly by killing it outright if necessary).
*/
#include <linux/config.h>
#include <linux/head.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/config.h>
#include <linux/timer.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/pgtable.h>
 
asmlinkage int system_call(void);
asmlinkage void lcall7(void);
struct desc_struct default_ldt = { 0, 0 };
 
static inline void console_verbose(void)
{
extern int console_loglevel;
console_loglevel = 15;
}
 
#define DO_ERROR(trapnr, signr, str, name, tsk) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
tsk->tss.error_code = error_code; \
tsk->tss.trap_no = trapnr; \
force_sig(signr, tsk); \
die_if_kernel(str,regs,error_code); \
}
 
#define DO_VM86_ERROR(trapnr, signr, str, name, tsk) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
if (regs->eflags & VM_MASK) { \
if (!handle_vm86_trap((struct vm86_regs *) regs, error_code, trapnr)) \
return; \
/* else fall through */ \
} \
tsk->tss.error_code = error_code; \
tsk->tss.trap_no = trapnr; \
force_sig(signr, tsk); \
die_if_kernel(str,regs,error_code); \
}
 
#define get_seg_byte(seg,addr) ({ \
register unsigned char __res; \
__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
 
#define get_seg_long(seg,addr) ({ \
register unsigned long __res; \
__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
 
#define _fs() ({ \
register unsigned short __res; \
__asm__("mov %%fs,%%ax":"=a" (__res):); \
__res;})
 
void page_exception(void);
 
asmlinkage void divide_error(void);
asmlinkage void debug(void);
asmlinkage void nmi(void);
asmlinkage void int3(void);
asmlinkage void overflow(void);
asmlinkage void bounds(void);
asmlinkage void invalid_op(void);
asmlinkage void device_not_available(void);
asmlinkage void double_fault(void);
asmlinkage void coprocessor_segment_overrun(void);
asmlinkage void invalid_TSS(void);
asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
asmlinkage void coprocessor_error(void);
asmlinkage void reserved(void);
asmlinkage void alignment_check(void);
asmlinkage void spurious_interrupt_bug(void);
 
int kstack_depth_to_print = 24;
 
/*
* These constants are for searching for possible module text
* segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
* a guess of how much space is likely to be vmalloced.
*/
#define VMALLOC_OFFSET (8*1024*1024)
#define MODULE_RANGE (8*1024*1024)
 
/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err)
{
int i;
unsigned long esp;
unsigned short ss;
unsigned long *stack, addr, module_start, module_end;
extern char start_kernel, _etext;
 
esp = (unsigned long) &regs->esp;
ss = KERNEL_DS;
if ((regs->eflags & VM_MASK) || (3 & regs->cs) == 3)
return;
if (regs->cs & 3) {
esp = regs->esp;
ss = regs->ss;
}
console_verbose();
printk("%s: %04lx\n", str, err & 0xffff);
printk("CPU: %d\n", smp_processor_id());
printk("EIP: %04x:[<%08lx>]\nEFLAGS: %08lx\n", 0xffff & regs->cs,regs->eip,regs->eflags);
printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n",
regs->eax, regs->ebx, regs->ecx, regs->edx);
printk("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n",
regs->esi, regs->edi, regs->ebp, esp);
printk("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n",
regs->ds, regs->es, regs->fs, regs->gs, ss);
store_TR(i);
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page)
printk("Corrupted stack page\n");
printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ",
current->comm, current->pid, 0xffff & i, current->kernel_stack_page);
stack = (unsigned long *) esp;
for(i=0; i < kstack_depth_to_print; i++) {
if (((long) stack & 4095) == 0)
break;
if (i && ((i % 8) == 0))
printk("\n ");
printk("%08lx ", get_seg_long(ss,stack++));
}
printk("\nCall Trace: ");
stack = (unsigned long *) esp;
i = 1;
module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
module_end = module_start + MODULE_RANGE;
while (((long) stack & 4095) != 0) {
addr = get_seg_long(ss, stack++);
/*
* If the address is either in the text segment of the
* kernel, or in the region which contains vmalloc'ed
* memory, it *may* be the address of a calling
* routine; if so, print it so that someone tracing
* down the cause of the crash will be able to figure
* out the call path that was taken.
*/
if (((addr >= (unsigned long) &start_kernel) &&
(addr <= (unsigned long) &_etext)) ||
((addr >= module_start) && (addr <= module_end))) {
if (i && ((i % 8) == 0))
printk("\n ");
printk("[<%08lx>] ", addr);
i++;
}
}
printk("\nCode: ");
for(i=0;i<20;i++)
printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip)));
printk("\n");
do_exit(SIGSEGV);
}
 
DO_VM86_ERROR( 0, SIGFPE, "divide error", divide_error, current)
DO_VM86_ERROR( 3, SIGTRAP, "int3", int3, current)
DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow, current)
DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds, current)
DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current)
DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available, current)
DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current)
DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math)
DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current)
DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current)
DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current)
DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current)
DO_ERROR(18, SIGSEGV, "reserved", reserved, current)
 
/* divide_error is after ret_from_sys_call in entry.S */
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
asmlinkage void divide_error(void) __asm__("divide_error");
 
asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
if (regs->eflags & VM_MASK) {
handle_vm86_fault((struct vm86_regs *) regs, error_code);
return;
}
 
/*
* HACK HACK HACK :) Fixing the segment invalid on syscall return
* barfage for 2.0 has been put into the too-hard basket but having
* a user producing endless GPFs is unacceptable as well. - Paul G.
*/
if ((regs->cs & 3) != 3) {
if (regs->eip >= (unsigned long)ret_from_sys_call &&
regs->eip < (unsigned long)divide_error) {
static int moancount = 0;
if (moancount < 5) {
printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n",
current->comm, current->pid);
moancount++;
}
do_exit(SIGSEGV);
}
else
die_if_kernel("general protection",regs,error_code);
}
current->tss.error_code = error_code;
current->tss.trap_no = 13;
force_sig(SIGSEGV, current);
}
 
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
#ifdef CONFIG_SMP_NMI_INVAL
smp_flush_tlb_rcv();
#else
#ifndef CONFIG_IGNORE_NMI
printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
printk("You probably have a hardware problem with your RAM chips or a\n");
printk("power saving mode enabled.\n");
#endif
#endif
}
 
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
{
if (regs->eflags & VM_MASK) {
handle_vm86_trap((struct vm86_regs *) regs, error_code, 1);
return;
}
force_sig(SIGTRAP, current);
current->tss.trap_no = 1;
current->tss.error_code = error_code;
if ((regs->cs & 3) == 0) {
/* If this is a kernel mode trap, then reset db7 and allow us to continue */
__asm__("movl %0,%%db7"
: /* no output */
: "r" (0));
return;
}
die_if_kernel("debug",regs,error_code);
}
 
/*
* Note that we play around with the 'TS' bit to hopefully get
* the correct behaviour even in the presence of the asynchronous
* IRQ13 behaviour
*/
void math_error(void)
{
struct task_struct * task;
 
clts();
#ifdef __SMP__
task = current;
#else
task = last_task_used_math;
last_task_used_math = NULL;
if (!task) {
__asm__("fnclex");
return;
}
#endif
/*
* Save the info for the exception handler
*/
__asm__ __volatile__("fnsave %0":"=m" (task->tss.i387.hard));
task->flags&=~PF_USEDFPU;
stts();
 
force_sig(SIGFPE, task);
task->tss.trap_no = 16;
task->tss.error_code = 0;
}
 
asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
{
ignore_irq13 = 1;
math_error();
}
 
asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
long error_code)
{
#if 0
/* No need to warn about this any longer. */
printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
#endif
}
 
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*
* Careful.. There are problems with IBM-designed IRQ13 behaviour.
* Don't touch unless you *really* know how it works.
*/
asmlinkage void math_state_restore(void)
{
__asm__ __volatile__("clts"); /* Allow maths ops (or we recurse) */
 
/*
* SMP is actually simpler than uniprocessor for once. Because
* we can't pull the delayed FPU switching trick Linus does
* we simply have to do the restore each context switch and
* set the flag. switch_to() will always save the state in
* case we swap processors. We also don't use the coprocessor
* timer - IRQ 13 mode isn't used with SMP machines (thank god).
*/
#ifndef __SMP__
if (last_task_used_math == current)
return;
if (last_task_used_math)
__asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
else
__asm__("fnclex");
last_task_used_math = current;
#endif
 
if(current->used_math)
__asm__("frstor %0": :"m" (current->tss.i387));
else
{
/*
* Our first FPU usage, clean the chip.
*/
__asm__("fninit");
current->used_math = 1;
}
current->flags|=PF_USEDFPU; /* So we fnsave on switch_to() */
}
 
#ifndef CONFIG_MATH_EMULATION
 
asmlinkage void math_emulate(long arg)
{
printk("math-emulation not enabled and no coprocessor found.\n");
printk("killing %s.\n",current->comm);
force_sig(SIGFPE,current);
schedule();
}
 
#endif /* CONFIG_MATH_EMULATION */
 
struct {
unsigned short limit;
unsigned long addr __attribute__((packed));
} idt_descriptor;
 
void trap_init_f00f_bug(void)
{
pgd_t * pgd;
pmd_t * pmd;
pte_t * pte;
unsigned long page;
unsigned long idtpage = (unsigned long)idt;
struct desc_struct *alias_idt;
 
printk("alias mapping IDT readonly ... ");
 
/* just to get free address space */
page = (unsigned long) vmalloc (PAGE_SIZE);
 
alias_idt = (void *)(page + (idtpage & ~PAGE_MASK));
idt_descriptor.limit = 256*8-1;
idt_descriptor.addr = VMALLOC_VMADDR(alias_idt);
 
/*
* alias map the original idt to the alias page:
*/
page = VMALLOC_VMADDR(page);
pgd = pgd_offset(&init_mm, page);
pmd = pmd_offset(pgd, page);
pte = pte_offset(pmd, page);
/* give memory back to the pool, don't need it */
free_page(pte_page(*pte));
/* ... and set the readonly alias */
set_pte(pte, mk_pte(idtpage & PAGE_MASK, PAGE_KERNEL));
*pte = pte_wrprotect(*pte);
flush_tlb_all();
 
/* now we have the mapping ok, we can do LIDT */
__asm__ __volatile__("\tlidt %0": "=m" (idt_descriptor));
 
printk(" ... done\n");
}
 
 
void trap_init(void)
{
int i;
struct desc_struct * p;
static int smptrap=0;
if(smptrap)
{
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
load_ldt(0);
return;
}
smptrap++;
if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0)
EISA_bus = 1;
set_call_gate(&default_ldt,lcall7);
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
for (i=18;i<48;i++)
set_trap_gate(i,&reserved);
set_system_gate(0x80,&system_call);
/* set up GDT task & ldt entries */
p = gdt+FIRST_TSS_ENTRY;
set_tss_desc(p, &init_task.tss);
p++;
set_ldt_desc(p, &default_ldt, 1);
p++;
for(i=1 ; i<NR_TASKS ; i++) {
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
load_TR(0);
load_ldt(0);
}
/trampoline.S
0,0 → 1,74
!
! Trampoline.S Derived from Setup.S by Linus Torvalds
!
! Entry: CS:IP point to the start of our code, we are
! in real mode with no stack, but the rest of the
! trampoline page to make our stack and everything else
! is a mystery.
!
! In fact we don't actually need a stack so we don't
! set one up.
!
! We jump into the boot/compressed/head.S code. So you'd
! better be running a compressed kernel image or you
! won't get very far.
!
#define __ASSEMBLY__
#include <asm/segment.h>
 
.text
extrn startup32
 
entry start
start:
! nop
! jmp start ! Test
mov ax,cs ! Code and data in the same place
mov ds,ax !
mov cx,ax ! Pass stack info to the 32bit boot
add cx,cx
add cx,cx
add cx,cx
add cx,cx ! Segment -> Offset
add cx, #4096 ! End of page is wanted
mov bx,#1 ! Flag an SMP trampoline
cli ! We should be safe anyway
 
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever is appropriate
 
xor ax,ax
inc ax ! protected mode (PE) bit
lmsw ax ! Into protected mode
jmp flush_instr
flush_instr:
jmpi 8192+startup32,KERNEL_CS ! Jump to the 32bit trampoline code
! jmpi 0x100000,KERNEL_CS ! Jump into the 32bit startup
! .byte 0x66,0x67 ! 32bit
! .byte 0xea,0x00,0x00,0x10,0x00,0x10,0x00 !jmpi .0x100000,KERNEL_CS
 
gdt:
.word 0,0,0,0 ! dummy
 
.word 0,0,0,0 ! unused
 
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
 
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
 
idt_48:
.word 0 ! idt limit=0
.word 0,0 ! idt base=0L
 
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 8192+gdt,0x0 ! gdt base = 8192+gdt (first SMP CPU)
! we load the others with the first table
! saves rewriting gdt_48 for each
 
/irq.c
0,0 → 1,582
/*
* linux/arch/i386/kernel/irq.c
*
* Copyright (C) 1992 Linus Torvalds
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
*/
 
/*
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
* Naturally it's not a 1:1 relation, but there are similarities.
*/
 
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/malloc.h>
#include <linux/random.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/smp.h>
 
#define CR0_NE 32
 
static unsigned char cache_21 = 0xff;
static unsigned char cache_A1 = 0xff;
 
#ifdef __SMP_PROF__
static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},};
#endif
 
static inline void mask_irq(unsigned int irq_nr)
{
unsigned char mask;
 
mask = 1 << (irq_nr & 7);
if (irq_nr < 8) {
cache_21 |= mask;
outb(cache_21,0x21);
} else {
cache_A1 |= mask;
outb(cache_A1,0xA1);
}
}
 
static inline void unmask_irq(unsigned int irq_nr)
{
unsigned char mask;
 
mask = ~(1 << (irq_nr & 7));
if (irq_nr < 8) {
cache_21 &= mask;
outb(cache_21,0x21);
} else {
cache_A1 &= mask;
outb(cache_A1,0xA1);
}
}
 
void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
 
save_flags(flags);
cli();
mask_irq(irq_nr);
restore_flags(flags);
}
 
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
save_flags(flags);
cli();
unmask_irq(irq_nr);
restore_flags(flags);
}
 
/*
* This builds up the IRQ handler stubs using some ugly macros in irq.h
*
* These macros create the low-level assembly IRQ routines that do all
* the operations that are needed to keep the AT interrupt-controller
* happy. They are also written to be fast - and to disable interrupts
* as little as humanly possible.
*
* NOTE! These macros expand to three different handlers for each line: one
* complete handler that does all the fancy stuff (including signal handling),
* and one fast handler that is meant for simple IRQ's that want to be
* atomic. The specific handler is chosen depending on the SA_INTERRUPT
* flag when installing a handler. Finally, one "bad interrupt" handler, that
* is used when no handler is present.
*
* The timer interrupt is handled specially to insure that the jiffies
* variable is updated at all times. Specifically, the timer interrupt is
* just like the complete handlers except that it is invoked with interrupts
* disabled and should never re-enable them. If other interrupts were
* allowed to be processed while the timer interrupt is active, then the
* other interrupts would have to avoid using the jiffies variable for delay
* and interval timing operations to avoid hanging the system.
*/
BUILD_TIMER_IRQ(FIRST,0,0x01)
BUILD_IRQ(FIRST,1,0x02)
BUILD_IRQ(FIRST,2,0x04)
BUILD_IRQ(FIRST,3,0x08)
BUILD_IRQ(FIRST,4,0x10)
BUILD_IRQ(FIRST,5,0x20)
BUILD_IRQ(FIRST,6,0x40)
BUILD_IRQ(FIRST,7,0x80)
BUILD_IRQ(SECOND,8,0x01)
BUILD_IRQ(SECOND,9,0x02)
BUILD_IRQ(SECOND,10,0x04)
BUILD_IRQ(SECOND,11,0x08)
BUILD_IRQ(SECOND,12,0x10)
#ifdef __SMP__
BUILD_MSGIRQ(SECOND,13,0x20)
#else
BUILD_IRQ(SECOND,13,0x20)
#endif
BUILD_IRQ(SECOND,14,0x40)
BUILD_IRQ(SECOND,15,0x80)
#ifdef __SMP__
BUILD_RESCHEDIRQ(16)
#endif
 
/*
* Pointers to the low-level handlers: first the general ones, then the
* fast ones, then the bad ones.
*/
static void (*interrupt[17])(void) = {
IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
#ifdef __SMP__
,IRQ16_interrupt
#endif
};
 
static void (*fast_interrupt[16])(void) = {
fast_IRQ0_interrupt, fast_IRQ1_interrupt,
fast_IRQ2_interrupt, fast_IRQ3_interrupt,
fast_IRQ4_interrupt, fast_IRQ5_interrupt,
fast_IRQ6_interrupt, fast_IRQ7_interrupt,
fast_IRQ8_interrupt, fast_IRQ9_interrupt,
fast_IRQ10_interrupt, fast_IRQ11_interrupt,
fast_IRQ12_interrupt, fast_IRQ13_interrupt,
fast_IRQ14_interrupt, fast_IRQ15_interrupt
};
 
static void (*bad_interrupt[16])(void) = {
bad_IRQ0_interrupt, bad_IRQ1_interrupt,
bad_IRQ2_interrupt, bad_IRQ3_interrupt,
bad_IRQ4_interrupt, bad_IRQ5_interrupt,
bad_IRQ6_interrupt, bad_IRQ7_interrupt,
bad_IRQ8_interrupt, bad_IRQ9_interrupt,
bad_IRQ10_interrupt, bad_IRQ11_interrupt,
bad_IRQ12_interrupt, bad_IRQ13_interrupt,
bad_IRQ14_interrupt, bad_IRQ15_interrupt
};
 
/*
* Initial irq handlers.
*/
 
static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
 
#ifdef __SMP__
 
/*
* On SMP boards, irq13 is used for interprocessor interrupts (IPI's).
*/
static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL };
 
#else
 
/*
* Note that on a 486, we don't want to do a SIGFPE on a irq13
* as the irq is unreliable, and exception 16 works correctly
* (ie as explained in the intel literature). On a 386, you
* can't use exception 16 due to bad IBM design, so we have to
* rely on the less exact irq13.
*
* Careful.. Not only is IRQ13 unreliable, but it is also
* leads to races. IBM designers who came up with it should
* be shot.
*/
 
static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs)
{
outb(0,0xF0);
if (ignore_irq13 || !hard_math)
return;
math_error();
}
 
static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL };
 
#endif
 
/*
* IRQ2 is cascade interrupt to second interrupt controller
*/
static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL};
 
static struct irqaction *irq_action[16] = {
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
 
int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction * action;
 
for (i = 0 ; i < 16 ; i++) {
action = irq_action[i];
if (!action)
continue;
len += sprintf(buf+len, "%2d: %10u %c %s",
i, kstat.interrupts[i],
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ",%s %s",
(action->flags & SA_INTERRUPT) ? " +" : "",
action->name);
}
len += sprintf(buf+len, "\n");
}
/*
* Linus - should you add NMI counts here ?????
*/
#ifdef __SMP_PROF__
len+=sprintf(buf+len, "IPI: %8lu received\n",
ipi_count);
#endif
return len;
}
 
#ifdef __SMP_PROF__
 
int get_smp_prof_list(char *buf) {
int i,j, len = 0;
struct irqaction * action;
unsigned long sum_spins = 0;
unsigned long sum_spins_syscall = 0;
unsigned long sum_spins_sys_idle = 0;
unsigned long sum_smp_idle_count = 0;
 
for (i=0;i<smp_num_cpus;i++) {
int cpunum = cpu_logical_map[i];
sum_spins+=smp_spins[cpunum];
sum_spins_syscall+=smp_spins_syscall[cpunum];
sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
sum_smp_idle_count+=smp_idle_count[cpunum];
}
 
len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
len += sprintf(buf+len," SUM ");
for (i=0;i<smp_num_cpus;i++)
len += sprintf(buf+len," P%1d ",cpu_logical_map[i]);
len += sprintf(buf+len,"\n");
for (i = 0 ; i < NR_IRQS ; i++) {
action = *(i + irq_action);
if (!action || !action->handler)
continue;
len += sprintf(buf+len, "%3d: %10d ",
i, kstat.interrupts[i]);
for (j=0;j<smp_num_cpus;j++)
len+=sprintf(buf+len, "%10d ",
int_count[cpu_logical_map[j]][i]);
len += sprintf(buf+len, "%c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ",%s %s",
(action->flags & SA_INTERRUPT) ? " +" : "",
action->name);
}
len += sprintf(buf+len, "\n");
}
len+=sprintf(buf+len, "LCK: %10lu",
sum_spins);
 
for (i=0;i<smp_num_cpus;i++)
len+=sprintf(buf+len," %10lu",smp_spins[cpu_logical_map[i]]);
 
len +=sprintf(buf+len," spins from int\n");
 
len+=sprintf(buf+len, "LCK: %10lu",
sum_spins_syscall);
 
for (i=0;i<smp_num_cpus;i++)
len+=sprintf(buf+len," %10lu",smp_spins_syscall[cpu_logical_map[i]]);
 
len +=sprintf(buf+len," spins from syscall\n");
 
len+=sprintf(buf+len, "LCK: %10lu",
sum_spins_sys_idle);
 
for (i=0;i<smp_num_cpus;i++)
len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[cpu_logical_map[i]]);
 
len +=sprintf(buf+len," spins from sysidle\n");
len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count);
 
for (i=0;i<smp_num_cpus;i++)
len+=sprintf(buf+len," %10lu",smp_idle_count[cpu_logical_map[i]]);
 
len +=sprintf(buf+len," idle ticks\n");
 
len+=sprintf(buf+len, "IPI: %10lu received\n",
ipi_count);
 
return len;
}
#endif
 
 
 
/*
* do_IRQ handles IRQ's that have been installed without the
* SA_INTERRUPT flag: it uses the full signal-handling return
* and runs with other interrupts enabled. All relatively slow
* IRQ's should use this format: notably the keyboard/timer
* routines.
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
struct irqaction * action = *(irq + irq_action);
int do_random = 0;
int c,intm,mask;
#ifdef IRQ_DEBUG
static int count;
if (smp_processor_id() != 0 && count++ < 1000)
printk("IRQ %d: done by CPU %d\n",irq,smp_processor_id());
#endif
if (irq >= 8) {
c = cache_A1;
intm = inb(0xA1);
mask = 1 << (irq - 8);
} else {
c = cache_21;
intm = inb(0x21);
mask = 1 << irq;
}
if (!(c & mask) || !(intm & mask)) {
#ifdef IRQ_DEBUG
printk("IRQ %d (proc %d):cache_x1=0x%x,INT mask=0x%x\n", irq, smp_processor_id(),c,intm);
#endif
/* better to return because the interrupt may be asserted again,
the bad thing is that we may loose some interrupts */
return;
}
#ifdef __SMP__
if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
#endif
 
kstat.interrupts[irq]++;
#ifdef __SMP_PROF__
int_count[smp_processor_id()][irq]++;
#endif
while (action) {
do_random |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
}
if (do_random & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
}
 
/*
* do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
* stuff - the handler is also running with interrupts disabled unless
* it explicitly enables them later.
*/
asmlinkage void do_fast_IRQ(int irq)
{
struct irqaction * action = *(irq + irq_action);
int do_random = 0;
#ifdef __SMP__
/* IRQ 13 is allowed - that's a flush tlb */
if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13)
panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
#endif
 
kstat.interrupts[irq]++;
#ifdef __SMP_PROF__
int_count[smp_processor_id()][irq]++;
#endif
while (action) {
do_random |= action->flags;
action->handler(irq, action->dev_id, NULL);
action = action->next;
}
if (do_random & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
}
 
int setup_x86_irq(int irq, struct irqaction * new)
{
int shared = 0;
struct irqaction *old, **p;
unsigned long flags;
 
p = irq_action + irq;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ))
return -EBUSY;
 
/* Can't share interrupts unless both are same type */
if ((old->flags ^ new->flags) & SA_INTERRUPT)
return -EBUSY;
 
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
 
if (new->flags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
 
save_flags(flags);
cli();
*p = new;
 
if (!shared) {
if (new->flags & SA_INTERRUPT)
set_intr_gate(0x20+irq,fast_interrupt[irq]);
else
set_intr_gate(0x20+irq,interrupt[irq]);
unmask_irq(irq);
}
restore_flags(flags);
return 0;
}
 
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
int retval;
struct irqaction * action;
 
if (irq > 15)
return -EINVAL;
if (!handler)
return -EINVAL;
 
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
 
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
 
retval = setup_x86_irq(irq, action);
 
if (retval)
kfree(action);
return retval;
}
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action, **p;
unsigned long flags;
 
if (irq > 15) {
printk("Trying to free IRQ%d\n",irq);
return;
}
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
 
/* Found it - now free it */
save_flags(flags);
cli();
*p = action->next;
if (!irq[irq_action]) {
mask_irq(irq);
set_intr_gate(0x20+irq,bad_interrupt[irq]);
}
restore_flags(flags);
kfree(action);
return;
}
printk("Trying to free free IRQ%d\n",irq);
}
 
unsigned long probe_irq_on (void)
{
unsigned int i, irqs = 0, irqmask;
unsigned long delay;
 
/* first, enable any unassigned irqs */
for (i = 15; i > 0; i--) {
if (!irq_action[i]) {
enable_irq(i);
irqs |= (1 << i);
}
}
 
/* wait for spurious interrupts to mask themselves out again */
for (delay = jiffies + HZ/10; delay > jiffies; )
/* about 100ms delay */;
 
/* now filter out any obviously spurious interrupts */
irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
return irqs & ~irqmask;
}
 
int probe_irq_off (unsigned long irqs)
{
unsigned int i, irqmask;
 
irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
#ifdef DEBUG
printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask);
#endif
irqs &= irqmask;
if (!irqs)
return 0;
i = ffz(~irqs);
if (irqs != (irqs & (1 << i)))
i = -i;
return i;
}
 
void init_IRQ(void)
{
int i;
static unsigned char smptrap=0;
if(smptrap)
return;
smptrap=1;
 
/* set the clock to 100 Hz */
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
for (i = 0; i < 16 ; i++)
set_intr_gate(0x20+i,bad_interrupt[i]);
/* This bit is a hack because we don't send timer messages to all processors yet */
/* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */
#ifdef __SMP__
set_intr_gate(0x20+i, interrupt[i]); /* IRQ '16' - IPI for rescheduling */
#endif
request_region(0x20,0x20,"pic1");
request_region(0xa0,0x20,"pic2");
setup_x86_irq(2, &irq2);
setup_x86_irq(13, &irq13);
}
/hexify.c
0,0 → 1,29
#include <stdio.h>
 
 
int main()
{
int c;
int comma=0;
int count=0;
while((c=getchar())!=EOF)
{
unsigned char x=c;
if(comma)
printf(",");
else
comma=1;
if(count==8)
{
count=0;
printf("\n");
}
if(count==0)
printf("\t");
printf("0x%02X",c);
count++;
}
if(count)
printf("\n");
return 0;
}
/Makefile
0,0 → 1,76
#
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
 
#.S.s:
# $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
 
ifdef SMP
.S.o:
$(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
else
.S.o:
$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
endif
 
all: kernel.o head.o
 
O_TARGET := kernel.o
O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \
ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o ksyms.o
OX_OBJS :=
 
ifdef CONFIG_APM
OX_OBJS += apm.o
endif
 
ifdef SMP
 
ifdef CONFIG_MTRR
O_OBJS += mtrr.o
endif
 
O_OBJS += smp.o
 
head.o: head.S $(TOPDIR)/include/linux/tasks.h
$(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o
 
else
 
head.o: head.S $(TOPDIR)/include/linux/tasks.h
$(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o
 
endif
 
hexify:
$(HOSTCC) hexify.c -o hexify
 
smp.c: trampoline.hex
 
trampoline.hex: trampoline hexify
(dd if=trampoline bs=1 skip=32 | ./hexify >trampoline.hex )
 
trampoline: trampoline.o trampoline32.o
$(LD86) -s -o $@ trampoline.o trampoline32.o
 
trampoline.o: trampoline.s
$(AS86) -o $@ $<
 
trampoline32.o: trampoline32.s
$(AS386) -o $@ $<
 
trampoline.s: trampoline.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
$(CPP) -D__SMP__ -traditional $< -o $@
 
trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
$(CPP) -D__SMP__ -traditional $< -o $@
 
clean:
rm -f trampoline trampoline.hex hexify
 
include $(TOPDIR)/Rules.make

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.