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/m68k/kernel
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/process.c
0,0 → 1,200
/*
* linux/arch/m68k/kernel/process.c
*
* Copyright (C) 1995 Hamish Macdonald
*/
 
/*
* This file handles the architecture-dependent parts of process handling..
*/
 
#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/user.h>
#include <linux/a.out.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/traps.h>
#include <asm/machdep.h>
 
asmlinkage void ret_from_exception(void);
 
/*
* The idle loop on an m68k..
*/
asmlinkage int sys_idle(void)
{
if (current->pid != 0)
return -EPERM;
 
/* endless idle loop with no priority at all */
current->counter = -100;
for (;;)
schedule();
}
 
void hard_reset_now(void)
{
if (mach_reset)
mach_reset();
}
 
void show_regs(struct pt_regs * regs)
{
printk("\n");
printk("Format %02x Vector: %04x PC: %08lx Status: %04x\n",
regs->format, regs->vector, regs->pc, regs->sr);
printk("ORIG_D0: %08lx D0: %08lx A1: %08lx\n",
regs->orig_d0, regs->d0, regs->a1);
printk("A0: %08lx D5: %08lx D4: %08lx\n",
regs->a0, regs->d5, regs->d4);
printk("D3: %08lx D2: %08lx D1: %08lx\n",
regs->d3, regs->d2, regs->d1);
if (!(regs->sr & PS_S))
printk("USP: %08lx\n", rdusp());
}
 
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
}
 
void flush_thread(void)
{
set_fs(USER_DS);
current->tss.fs = USER_DS;
}
 
/*
* "m68k_fork()".. By the time we get here, the
* non-volatile registers have also been saved on the
* stack. We do some ugly pointer stuff here.. (see
* also copy_thread)
*/
 
asmlinkage int m68k_fork(struct pt_regs *regs)
{
return do_fork(SIGCHLD, rdusp(), regs);
}
 
asmlinkage int m68k_clone(struct pt_regs *regs)
{
unsigned long clone_flags;
unsigned long newsp;
 
/* syscall2 puts clone_flags in d1 and usp in d2 */
clone_flags = regs->d1;
newsp = regs->d2;
if (!newsp)
newsp = rdusp();
return do_fork(clone_flags, newsp, regs);
}
 
void release_thread(struct task_struct *dead_task)
{
}
 
void copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct switch_stack * childstack, *stack;
unsigned long stack_offset, *retp;
 
stack_offset = PAGE_SIZE - sizeof(struct pt_regs);
childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset);
 
*childregs = *regs;
childregs->d0 = 0;
 
retp = ((unsigned long *) regs);
stack = ((struct switch_stack *) retp) - 1;
 
childstack = ((struct switch_stack *) childregs) - 1;
*childstack = *stack;
childstack->retpc = (unsigned long) ret_from_exception;
 
p->tss.usp = usp;
p->tss.ksp = (unsigned long)childstack;
 
/* Copy the current fpu state */
asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory");
if (p->tss.fpstate[0])
asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
"fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
: : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0])
: "memory");
/* Restore the state in case the fpu was busy */
asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0]));
}
 
/* Fill in the fpu structure for a core dump. */
 
int dump_fpu (struct user_m68kfp_struct *fpu)
{
char fpustate[216];
 
/* First dump the fpu context to avoid protocol violation. */
asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
if (!fpustate[0])
return 0;
 
asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
:: "m" (fpu->fpcntl[0])
: "memory");
asm volatile ("fmovemx %/fp0-%/fp7,%0"
:: "m" (fpu->fpregs[0])
: "memory");
return 1;
}
 
/*
* fill in the user structure for a core dump..
*/
void dump_thread(struct pt_regs * regs, struct user * dump)
{
/* changed the size calculations - should hopefully work better. lbt */
dump->magic = CMAGIC;
dump->start_code = 0;
dump->start_stack = rdusp() & ~(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;
 
if (dump->start_stack < TASK_SIZE)
dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
 
dump->u_ar0 = (struct pt_regs *)(((int)(&dump->regs)) -((int)(dump)));
dump->regs = *regs;
dump->regs2 = ((struct switch_stack *)regs)[-1];
/* dump floating point stuff */
dump->u_fpvalid = dump_fpu (&dump->m68kfp);
}
 
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(char *name, char **argv, char **envp)
{
int error;
char * filename;
struct pt_regs *regs = (struct pt_regs *) &name;
 
error = getname(name, &filename);
if (error)
return error;
error = do_execve(filename, argv, envp, regs);
putname(filename);
return error;
}
/ptrace.c
0,0 → 1,536
/*
* linux/arch/m68k/kernel/ptrace.c
*
* Copyright (C) 1994 by Hamish Macdonald
* Taken from linux/kernel/ptrace.c and modified for M680x0.
* linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of
* this archive for more details.
*/
 
#include <stddef.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 <asm/segment.h>
#include <asm/page.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 bits in the SR the user has access to. */
/* 1 = access 0 = no access */
#define SR_MASK 0x001f
 
/* sets the trace bits. */
#define TRACE_BITS 0x8000
 
/* Find the stack offset for a register, relative to tss.esp0. */
#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg)
#define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \
- sizeof(struct switch_stack))
/* Mapping from PT_xxx to the stack offset at which the register is
saved. Notice that usp has no stack-slot and needs to be treated
specially (see get_reg/put_reg below). */
static int regoff[] = {
PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4),
PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0),
PT_REG(a1), SW_REG(a2), SW_REG(a3), SW_REG(a4),
SW_REG(a5), SW_REG(a6), PT_REG(d0), -1,
PT_REG(orig_d0), PT_REG(sr), PT_REG(pc),
};
 
/* 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;
}
 
/*
* Get contents of register REGNO in task TASK.
*/
static inline long get_reg(struct task_struct *task, int regno)
{
unsigned long *addr;
 
if (regno == PT_USP)
addr = &task->tss.usp;
else if (regno < sizeof(regoff)/sizeof(regoff[0]))
addr = (unsigned long *)(task->tss.esp0 + regoff[regno]);
else
return 0;
return *addr;
}
 
/*
* Write contents of register REGNO in task TASK.
*/
static inline int put_reg(struct task_struct *task, int regno,
unsigned long data)
{
unsigned long *addr;
 
if (regno == PT_USP)
addr = &task->tss.usp;
else if (regno < sizeof(regoff)/sizeof(regoff[0]))
addr = (unsigned long *) (task->tss.esp0 + regoff[regno]);
else
return -1;
*addr = 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 directory %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 directory %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, 2);
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 */
*pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
flush_tlb_all();
}
 
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,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_low = vma;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_low = vma->vm_next;
if (!vma_low || vma_low->vm_start != vma->vm_end)
return -EIO;
}
high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 3:
low >>= 8;
low |= high << 24;
break;
case 2:
low >>= 16;
low |= high << 16;
break;
case 1:
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_low = vma;
 
if (addr + sizeof(long) >= vma->vm_end) {
vma_low = vma->vm_next;
if (!vma_low || vma_low->vm_start != vma->vm_end)
return -EIO;
}
high = get_long(tsk, vma,addr & ~(sizeof(long)-1));
low = get_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
high = data;
break;
case 3:
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 1:
low &= 0x00ffffff;
low |= data << 24;
high &= ~0xffffff;
high |= data >> 8;
break;
}
put_long(tsk, vma,addr & ~(sizeof(long)-1),high);
put_long(tsk, vma_low,(addr+sizeof(long)) & ~(sizeof(long)-1),low);
} else
put_long(tsk, vma,addr,data);
return 0;
}
 
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
struct user * dummy;
 
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_user(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))
return -EIO;
res = verify_area(VERIFY_WRITE, (void *) data,
sizeof(long));
if (res)
return res;
tmp = 0; /* Default return condition */
addr = addr >> 2; /* temporary hack. */
if (addr < 19) {
tmp = get_reg(child, addr);
if (addr == PT_SR)
tmp >>= 16;
}
else if (addr >= 21 && addr < 49)
tmp = child->tss.fp[addr - 21];
else
return -EIO;
put_user(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))
return -EIO;
 
addr = addr >> 2; /* temporary hack. */
if (addr == PT_ORIG_D0)
return -EIO;
if (addr == PT_SR) {
data &= SR_MASK;
data <<= 16;
data |= get_reg(child, PT_SR) & ~(SR_MASK << 16);
}
if (addr < 19) {
if (put_reg(child, addr, data))
return -EIO;
return 0;
}
if (addr >= 21 && addr < 48)
{
child->tss.fp[addr - 21] = 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_reg(child, PT_SR) & ~(TRACE_BITS << 16);
put_reg(child, PT_SR, 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_reg(child, PT_SR) & ~(TRACE_BITS << 16);
put_reg(child, PT_SR, 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_reg(child, PT_SR) | (TRACE_BITS << 16);
put_reg(child, PT_SR, 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_reg(child, PT_SR) & ~(TRACE_BITS << 16);
put_reg(child, PT_SR, tmp);
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);
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;
return;
}
/time.c
0,0 → 1,143
/*
* linux/arch/m68k/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*
* This file contains the m68k-specific time handling details.
* Most of the stuff is located in the machine specific files.
*/
 
#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 <asm/machdep.h>
#include <asm/segment.h>
#include <asm/io.h>
 
#include <linux/timex.h>
 
 
static inline int set_rtc_mmss(unsigned long nowtime)
{
if (mach_set_clock_mmss)
return mach_set_clock_mmss (nowtime);
return -1;
}
 
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
static void timer_interrupt(int irq, struct pt_regs * regs, void *dummy)
{
/* last time the cmos clock got updated */
static long last_rtc_update=0;
 
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_state != TIME_BAD && 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 */
}
 
/* 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 */
}
 
void time_init(void)
{
unsigned int year, mon, day, hour, min, sec;
 
extern void arch_gettod(int *year, int *mon, int *day, int *hour,
int *min, int *sec);
 
arch_gettod (&year, &mon, &day, &hour, &min, &sec);
 
if ((year += 1900) < 1970)
year += 100;
xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0;
 
mach_sched_init(timer_interrupt);
}
 
/*
* 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 += mach_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 -= mach_gettimeoffset();
 
if (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
tv->tv_sec--;
}
 
xtime = *tv;
time_state = TIME_BAD;
time_maxerror = MAXPHASE;
time_esterror = MAXPHASE;
sti();
}
/setup.c
0,0 → 1,348
/*
* linux/arch/m68k/kernel/setup.c
*
* Copyright (C) 1995 Hamish Macdonald
*/
 
/*
* This file handles the architecture-dependent parts of system setup
*/
 
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/console.h>
#include <linux/genhd.h>
#include <linux/errno.h>
#include <linux/string.h>
 
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/machdep.h>
#include <asm/amigatypes.h>
#include <asm/amigahw.h>
 
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h>
#include <asm/pgtable.h>
#endif
 
struct bootinfo boot_info = {0,};
int bisize = sizeof boot_info;
 
int m68k_is040or060 = 0;
 
char m68k_debug_device[6] = "";
 
extern int end;
extern unsigned long availmem;
 
char saved_command_line[CL_SIZE];
 
/* setup some dummy routines */
static void dummy_waitbut(void)
{
}
 
void (*mach_sched_init) (isrfunc);
int (*mach_keyb_init) (void);
int (*mach_kbdrate) (struct kbd_repeat *) = NULL;
void (*mach_kbd_leds) (unsigned int) = NULL;
void (*mach_init_INTS) (void);
int (*mach_add_isr) (unsigned long, isrfunc, int, void *, char *);
int (*mach_remove_isr) (unsigned long, isrfunc, void *);
void (*mach_process_int) (int, struct pt_regs *) = NULL;
void (*mach_enable_irq) (unsigned) = NULL;
void (*mach_disable_irq) (unsigned) = NULL;
int (*mach_get_irq_list) (char *, int) = NULL;
unsigned long (*mach_gettimeoffset) (void);
void (*mach_gettod) (int*, int*, int*, int*, int*, int*);
int (*mach_hwclk) (int, struct hwclk_time*) = NULL;
int (*mach_set_clock_mmss) (unsigned long) = NULL;
void (*mach_mksound)( unsigned int count, unsigned int ticks );
void (*mach_reset)( void );
void (*waitbut)(void) = dummy_waitbut;
struct fb_info *(*mach_fb_init)(long *);
long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */
void (*mach_debug_init)(void);
void (*mach_video_setup) (char *, int *);
#ifdef CONFIG_BLK_DEV_FD
int (*mach_floppy_init) (void) = NULL;
void (*mach_floppy_setup) (char *, int *) = NULL;
void (*mach_floppy_eject) (void) = NULL;
#endif
 
extern void config_amiga(void);
extern void config_atari(void);
extern void config_mac(void);
 
extern void register_console(void (*proc)(const char *));
extern void ami_serial_print (const char *str);
extern void ata_serial_print (const char *str);
 
extern void (*kd_mksound)(unsigned int, unsigned int);
 
extern void amiga_get_model(char *model);
extern void atari_get_model(char *model);
extern void mac_get_model(char *model);
extern int amiga_get_hardware_list(char *buffer);
extern int atari_get_hardware_list(char *buffer);
extern int mac_get_hardware_list(char *buffer);
 
#define MASK_256K 0xfffc0000
 
void setup_arch(char **cmdline_p,
unsigned long * memory_start_p, unsigned long * memory_end_p)
{
unsigned long memory_start, memory_end;
extern int _etext, _edata, _end;
int i;
char *p, *q;
 
#ifdef CONFIG_AMIGA
if (MACH_IS_AMIGA)
register_console (ami_serial_print);
#endif
#ifdef CONFIG_ATARI
if (MACH_IS_ATARI)
register_console (ata_serial_print);
#endif
 
if (boot_info.cputype & CPU_68040)
m68k_is040or060 = 4;
else if (boot_info.cputype & CPU_68060)
m68k_is040or060 = 6;
 
/* clear the fpu if we have one */
if (boot_info.cputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) {
volatile int zero = 0;
asm __volatile__ ("frestore %0" : : "m" (zero));
}
 
memory_start = availmem;
memory_end = 0;
 
for (i = 0; i < boot_info.num_memory; i++)
memory_end += boot_info.memory[i].size & MASK_256K;
 
init_task.mm->start_code = 0;
init_task.mm->end_code = (unsigned long) &_etext;
init_task.mm->end_data = (unsigned long) &_edata;
init_task.mm->brk = (unsigned long) &_end;
 
*cmdline_p = boot_info.command_line;
memcpy(saved_command_line, *cmdline_p, CL_SIZE);
 
/* Parse the command line for arch-specific options.
* For the m68k, this is currently only "debug=xxx" to enable printing
* certain kernel messages to some machine-specific device.
*/
for( p = *cmdline_p; p && *p; ) {
i = 0;
if (!strncmp( p, "debug=", 6 )) {
strncpy( m68k_debug_device, p+6, sizeof(m68k_debug_device)-1 );
m68k_debug_device[sizeof(m68k_debug_device)-1] = 0;
if ((q = strchr( m68k_debug_device, ' ' ))) *q = 0;
i = 1;
}
 
if (i) {
/* option processed, delete it */
if ((q = strchr( p, ' ' )))
strcpy( p, q+1 );
else
*p = 0;
} else {
if ((p = strchr( p, ' ' ))) ++p;
}
}
 
*memory_start_p = memory_start;
*memory_end_p = memory_end;
 
switch (boot_info.machtype) {
#ifdef CONFIG_AMIGA
case MACH_AMIGA:
config_amiga();
break;
#endif
#ifdef CONFIG_ATARI
case MACH_ATARI:
config_atari();
break;
#endif
#ifdef CONFIG_MAC
case MACH_MAC:
config_mac();
break;
#endif
default:
panic ("No configuration setup");
}
 
#ifdef CONFIG_BLK_DEV_INITRD
if (boot_info.ramdisk_size) {
initrd_start = PTOV (boot_info.ramdisk_addr);
initrd_end = initrd_start + boot_info.ramdisk_size * 1024;
}
#endif
}
 
int setkeycode(unsigned int scancode, unsigned int keycode)
{
return -EOPNOTSUPP;
}
 
int getkeycode(unsigned int scancode)
{
return -EOPNOTSUPP;
}
 
int get_cpuinfo(char * buffer)
{
char *cpu, *mmu, *fpu;
u_long clockfreq, clockfactor;
 
#define CLOCK_FACTOR_68020 (8046) /* 3107016 loops/s @ 25 MHz (Sun-3) */
#define CLOCK_FACTOR_68030 (8010) /* 3994575 loops/s @ 32 MHz */
#define CLOCK_FACTOR_68040 (3010) /* 8305552 loops/s @ 25 MHz */
#define CLOCK_FACTOR_68060 (998) /* 50081241 loops/s @ 50 MHz */
 
if (boot_info.cputype & CPU_68020) {
cpu = "68020";
mmu = "68851";
clockfactor = CLOCK_FACTOR_68020;
} else if (boot_info.cputype & CPU_68030) {
cpu = mmu = "68030";
clockfactor = CLOCK_FACTOR_68030;
} else if (boot_info.cputype & CPU_68040) {
cpu = mmu = "68040";
clockfactor = CLOCK_FACTOR_68040;
} else if (boot_info.cputype & CPU_68060) {
cpu = mmu = "68060";
clockfactor = CLOCK_FACTOR_68060;
} else {
cpu = mmu = "680x0";
clockfactor = 0;
}
 
if (boot_info.cputype & FPU_68881)
fpu = "68881";
else if (boot_info.cputype & FPU_68882)
fpu = "68882";
else if (boot_info.cputype & FPU_68040)
fpu = "68040";
else if (boot_info.cputype & FPU_68060)
fpu = "68060";
else
fpu = "none";
 
clockfreq = loops_per_sec/1000*clockfactor;
 
return(sprintf(buffer, "CPU:\t\t%s\n"
"MMU:\t\t%s\n"
"FPU:\t\t%s\n"
"Clockspeed:\t%lu.%1luMHz\n"
"BogoMips:\t%lu.%02lu\n",
cpu, mmu, fpu, (clockfreq+50000)/1000000,
((clockfreq+50000)/100000)%10, loops_per_sec/500000,
(loops_per_sec/5000)%100));
}
 
int get_hardware_list(char *buffer)
{
int len = 0;
char model[80];
u_long mem;
int i;
 
switch (boot_info.machtype) {
#ifdef CONFIG_AMIGA
case MACH_AMIGA:
amiga_get_model(model);
break;
#endif
#ifdef CONFIG_ATARI
case MACH_ATARI:
atari_get_model(model);
break;
#endif
#ifdef CONFIG_MAC
case MACH_MAC:
mac_get_model(model);
break;
#endif
default:
strcpy(model, "Unknown m68k");
} /* boot_info.machtype */
 
len += sprintf(buffer+len, "Model:\t\t%s\n", model);
len += get_cpuinfo(buffer+len);
for (mem = 0, i = 0; i < boot_info.num_memory; i++)
mem += boot_info.memory[i].size;
len += sprintf(buffer+len, "System Memory:\t%ldK\n", mem>>10);
 
switch (boot_info.machtype) {
#ifdef CONFIG_AMIGA
case MACH_AMIGA:
len += amiga_get_hardware_list(buffer+len);
break;
#endif
#ifdef CONFIG_ATARI
case MACH_ATARI:
len += atari_get_hardware_list(buffer+len);
break;
#endif
#ifdef CONFIG_MAC
case MACH_MAC:
break;
#endif
} /* boot_info.machtype */
 
return(len);
}
 
#ifdef CONFIG_BLK_DEV_FD
int floppy_init(void)
{
if (mach_floppy_init)
return mach_floppy_init();
else
return 0;
}
 
void floppy_setup(char *str, int *ints)
{
if (mach_floppy_setup)
mach_floppy_setup (str, ints);
}
 
void floppy_eject(void)
{
if (mach_floppy_eject)
mach_floppy_eject();
}
#endif
 
unsigned long arch_kbd_init(void)
{
return mach_keyb_init();
}
 
void arch_gettod(int *year, int *mon, int *day, int *hour,
int *min, int *sec)
{
if (mach_gettod)
mach_gettod(year, mon, day, hour, min, sec);
else
*year = *mon = *day = *hour = *min = *sec = 0;
}
 
void video_setup (char *options, int *ints)
{
if (mach_video_setup)
mach_video_setup (options, ints);
}
/bios32.c
0,0 → 1,7
/*
* bios 32 replacement
*/
unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
{
return memory_start;
}
/entry.S
0,0 → 1,637
/* -*- mode: asm -*-
*
* linux/arch/m68k/kernel/entry.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file README.legal in the main directory of this archive
* for more details.
*
* 680x0 support by Hamish Macdonald
*
*/
 
/*
* 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.
*
* Stack layout in 'ret_from_exception':
*
* This allows access to the syscall arguments in registers d1-d5
*
* 0(sp) - d1
* 4(sp) - d2
* 8(sp) - d3
* C(sp) - d4
* 10(sp) - d5
* 14(sp) - a0
* 18(sp) - a1
* 1C(sp) - d0
* 20(sp) - orig_d0
* 24(sp) - stack adjustment
* 28(sp) - sr
* 2A(sp) - pc
* 2E(sp) - format & vector
*/
 
/*
* 12/03/96 Jes: Currently we only support m68k single-cpu systems, so
* all pointers that used to be 'current' are now entry
* number 0 in the 'current_set' list.
*/
 
#include <linux/sys.h>
 
#include <asm/segment.h>
 
LCF_MASK = 0x0001
 
LENOSYS = 38
 
/*
* these are offsets into the task-struct
*/
LTASK_STATE = 0
LTASK_COUNTER = 4
LTASK_PRIORITY = 8
LTASK_SIGNAL = 12
LTASK_BLOCKED = 16
LTASK_FLAGS = 20
LTASK_ERRNO = 24
 
#include <linux/config.h>
#include <linux/linkage.h>
 
/* the following macro is used when enabling interrupts */
#if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC)
/* block out HSYNC on the atari */
#define ALLOWINT 0xfbff
#else
/* portable version */
#define ALLOWINT 0xf8ff
#endif /* machine compilation types */
 
LD0 = 0x1C
LORIG_D0 = 0x20
LSR = 0x28
LFORMATVEC = 0x2E
 
/*
* This defines the normal kernel pt-regs layout.
*
* regs are a2-a6 and d6-d7 preserved by C code
* the kernel doesn't mess with usp unless it needs to
*/
#define SAVE_ALL \
clrl %sp@-; /* stk_adj */ \
movel %d0,%sp@-; /* orig d0 */ \
movel %d0,%sp@-; /* d0 */ \
moveml %d1-%d5/%a0-%a1,%sp@-
 
#define RESTORE_ALL \
moveml %sp@+,%a0-%a1/%d1-%d5; \
movel %sp@+,%d0; \
addql #4,%sp; /* orig d0 */ \
addl %sp@+,%sp; /* stk adj */ \
rte
 
#define SWITCH_STACK_SIZE (7*4+4) /* includes return address */
 
#define SAVE_SWITCH_STACK \
moveml %a2-%a6/%d6-%d7,%sp@-
 
#define RESTORE_SWITCH_STACK \
moveml %sp@+,%a2-%a6/%d6-%d7
 
.globl SYMBOL_NAME(system_call), SYMBOL_NAME(buserr), SYMBOL_NAME(trap)
.globl SYMBOL_NAME(resume), SYMBOL_NAME(ret_from_exception)
.globl SYMBOL_NAME(ret_from_signal)
.globl SYMBOL_NAME(inthandler), SYMBOL_NAME(sys_call_table)
.globl SYMBOL_NAME(sys_fork), SYMBOL_NAME(sys_clone)
.globl SYMBOL_NAME(ret_from_interrupt), SYMBOL_NAME(bad_interrupt)
 
.text
ENTRY(buserr)
SAVE_ALL
moveq #-1,%d0
movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field
| signifies that the stack frame
| is NOT for syscall
 
movel %sp,%sp@- | stack frame pointer argument
bsrl SYMBOL_NAME(buserr_c)
addql #4,%sp
jra SYMBOL_NAME(ret_from_exception)
 
ENTRY(trap)
SAVE_ALL
moveq #-1,%d0
movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field
| signifies that the stack frame
| is NOT for syscall
movel %sp,%sp@- | stack frame pointer argument
bsrl SYMBOL_NAME(trap_c)
addql #4,%sp
jra SYMBOL_NAME(ret_from_exception)
 
ENTRY(reschedule)
| save top of frame
pea %sp@
jbsr SYMBOL_NAME(set_esp0)
addql #4,%sp
 
pea SYMBOL_NAME(ret_from_exception)
jmp SYMBOL_NAME(schedule)
 
ENTRY(system_call)
SAVE_ALL
movel #-LENOSYS,LD0(%sp) | default return value in d0
| original D0 is in orig_d0
movel %d0,%d2
 
| save top of frame
pea %sp@
jbsr SYMBOL_NAME(set_esp0)
addql #4,%sp
 
cmpl #NR_syscalls,%d2
jcc SYMBOL_NAME(ret_from_exception)
lea SYMBOL_NAME(sys_call_table),%a0
movel %a0@(%d2:l:4),%d3
jeq SYMBOL_NAME(ret_from_exception)
andw #~LCF_MASK,%sp@(LSR) | assume syscall success
movel SYMBOL_NAME(current_set),%a0
clrl %a0@(LTASK_ERRNO)
btst #5,%a0@(LTASK_FLAGS+3) | PF_TRACESYS
bnes 1f
movel %d3,%a0
jbsr %a0@
movel %d0,%sp@(LD0) | save the return value
jpl 2f
orw #LCF_MASK,%sp@(LSR) | set carry to indicate error
2:
movel SYMBOL_NAME(current_set),%a0
movel %a0@(LTASK_ERRNO),%d1
negl %d1
jeq SYMBOL_NAME(ret_from_exception)
movel %d1,%sp@(LD0)
orw #LCF_MASK,%sp@(LSR) | set carry to indicate error
jra SYMBOL_NAME(ret_from_exception)
1:
subql #4,%sp
SAVE_SWITCH_STACK
jbsr SYMBOL_NAME(syscall_trace)
RESTORE_SWITCH_STACK
addql #4,%sp
movel %d3,%a0
jbsr %a0@
movel %d0,%sp@(LD0) | save the return value
jpl 2f
orw #LCF_MASK,%sp@(LSR) | set carry to indicate error
2:
movel SYMBOL_NAME(current_set),%a0
movel %a0@(LTASK_ERRNO),%d1
negl %d1
jeq 2f
movel %d1,%sp@(LD0)
orw #LCF_MASK,%sp@(LSR) | set carry to indicate error
2: subql #4,%sp | dummy return address
SAVE_SWITCH_STACK
jbsr SYMBOL_NAME(syscall_trace)
 
SYMBOL_NAME_LABEL(ret_from_signal)
RESTORE_SWITCH_STACK
addql #4,%sp
 
SYMBOL_NAME_LABEL(ret_from_exception)
btst #5,%sp@(LSR) | check if returning to kernel
bnes 2f | if so, skip resched, signals
tstl SYMBOL_NAME(need_resched)
jne SYMBOL_NAME(reschedule)
movel SYMBOL_NAME(current_set),%a0
cmpl #SYMBOL_NAME(task),%a0 | task[0] cannot have signals
jeq 2f
bclr #5,%a0@(LTASK_FLAGS+1) | check for delayed trace
jeq 1f
bclr #7,%sp@(LSR) | clear trace bit in SR
pea 1 | send SIGTRAP
movel %a0,%sp@-
pea 5
jbsr SYMBOL_NAME(send_sig)
addql #8,%sp
addql #4,%sp
 
1:
moveq #0,%d0
movel SYMBOL_NAME(current_set),%a0
cmpl %a0@(LTASK_STATE),%d0 | state
jne SYMBOL_NAME(reschedule)
cmpl %a0@(LTASK_COUNTER),%d0 | counter
jeq SYMBOL_NAME(reschedule)
 
movel %a0@(LTASK_BLOCKED),%d0
movel %d0,%d1 | save blocked in d1 for sig handling
notl %d0
andl %a0@(LTASK_SIGNAL),%d0
jne Lsignal_return
2: RESTORE_ALL
 
Lsignal_return:
subql #4,%sp | dummy return address
SAVE_SWITCH_STACK
pea %sp@(SWITCH_STACK_SIZE)
movel %d1,%sp@-
bsrl SYMBOL_NAME(do_signal)
addql #8,%sp
RESTORE_SWITCH_STACK
addql #4,%sp
RESTORE_ALL
 
/*
** This is the main interrupt handler, responsible for calling process_int()
*/
SYMBOL_NAME_LABEL(inthandler)
SAVE_ALL
moveq #-1,%d0
movel %d0,%sp@(LORIG_D0) | a -1 in the ORIG_D0 field
| signifies that the stack frame
| is NOT for syscall
addql #1,SYMBOL_NAME(intr_count)
 
movew %sp@(LFORMATVEC),%d0 | put exception # in d0
andil #0xfff,%d0 | mask out format nybble
 
movel %sp,%sp@-
movel %d0,%sp@- | put vector # on stack
jbsr SYMBOL_NAME(process_int)| process the IRQ
addql #8,%sp | pop parameters off stack
 
SYMBOL_NAME_LABEL(ret_from_interrupt)
/* check if we need to do software interrupts */
1:
movel SYMBOL_NAME(intr_count),%d2
subql #1,%d2
jne 2f
 
movel SYMBOL_NAME(bh_active),%d0
andl SYMBOL_NAME(bh_mask),%d0
jne 3f
 
movel %d2,SYMBOL_NAME(intr_count)
 
jra SYMBOL_NAME(ret_from_exception)
| deliver signals, reschedule etc..
 
2: movel %d2,SYMBOL_NAME(intr_count)
RESTORE_ALL
3:
movew %sr,%sp@-
andiw #(ALLOWINT),%sr | allow interrupts
jbsr SYMBOL_NAME(do_bottom_half)
movew %sp@+,%sr
jra 1b
 
 
/* Handler for uninitialized and spurious interrupts */
 
SYMBOL_NAME_LABEL(bad_interrupt)
addql #1,SYMBOL_NAME(num_spurious)
rte
 
ENTRY(sys_fork)
SAVE_SWITCH_STACK
pea %sp@(SWITCH_STACK_SIZE)
jbsr SYMBOL_NAME(m68k_fork)
addql #4,%sp
RESTORE_SWITCH_STACK
rts
 
ENTRY(sys_clone)
SAVE_SWITCH_STACK
pea %sp@(SWITCH_STACK_SIZE)
jbsr SYMBOL_NAME(m68k_clone)
addql #4,%sp
RESTORE_SWITCH_STACK
rts
 
ENTRY(sys_sigsuspend)
SAVE_SWITCH_STACK
pea %sp@(SWITCH_STACK_SIZE)
jbsr SYMBOL_NAME(do_sigsuspend)
addql #4,%sp
RESTORE_SWITCH_STACK
rts
 
ENTRY(sys_sigreturn)
SAVE_SWITCH_STACK
jbsr SYMBOL_NAME(do_sigreturn)
RESTORE_SWITCH_STACK
rts
 
LFLUSH_I_AND_D = 0x00000808
LBI_CPU = 4
LTSS_KSP = 0
LTSS_USP = 4
LTSS_SR = 8
LTSS_FS = 10
LTSS_CRP = 20
LTSS_FPCTXT = 32
 
SYMBOL_NAME_LABEL(resume)
/*
* Beware - when entering resume, offset of tss is in a1 and
* next (the new task) is in d1, so don't chance these
* registers before their contents have been used.
*/
/* current tasks task_struct */
movel SYMBOL_NAME(current_set),%a0
 
/* offset of tss struct (processor state) from beginning
of task struct */
addl %a1,%a0
 
/* save sr */
movew %sr,%a0@(LTSS_SR)
 
/* disable interrupts */
oriw #0x0700,%sr
 
/* save fs (sfc,%dfc) (may be pointing to kernel memory) */
movec %sfc,%d0
movew %d0,%a0@(LTSS_FS)
 
/* save usp */
/* it is better to use a movel here instead of a movew 8*) */
movec %usp,%d0
movel %d0,%a0@(LTSS_USP)
 
/* load new task (before adjusting stack) */
/* The task has already been put in d1 by switch_to (Jes) */
/*
movel %sp@(4),%d1
*/
/* save non-scratch registers on stack */
SAVE_SWITCH_STACK
 
/* save current kernel stack pointer */
movel %sp,%a0@(LTSS_KSP)
 
/* save floating point context */
fsave %a0@(LTSS_FPCTXT+27*4)
tstb %a0@(LTSS_FPCTXT+27*4)
jeq 1f
fmovemx %fp0-%fp7,%a0@(LTSS_FPCTXT)
fmoveml %fpcr/%fpsr/%fpiar,%a0@(LTSS_FPCTXT+24*4)
1:
 
/* get pointer to tss struct (d1 contains new task) */
movel %d1,SYMBOL_NAME(current_set)
movel %d1,%a0
addl %a1,%a0
 
/* 68040 or 68060 ? */
btst #2,SYMBOL_NAME(boot_info)+LBI_CPU+3
bnes 1f
btst #3,SYMBOL_NAME(boot_info)+LBI_CPU+3
bnes 1f
 
/*
* switch address space
*/
 
/* flush MC68030/MC68020 caches (they are virtually addressed) */
movec %cacr,%d0
oril #LFLUSH_I_AND_D,%d0
movec %d0,%cacr
 
/* switch the root pointer */
pmove %a0@(LTSS_CRP),%crp
 
/* flush address translation cache (probably not needed */
pflusha
 
jra 2f /* skip m68040 stuff */
 
1:
/*
* switch address space
*/
 
/* flush address translation cache (user entries) */
.word 0xf510 /* pflushan */
 
/* switch the root pointer */
movel %a0@(LTSS_CRP+4),%d0
.long 0x4e7b0806 /* movec d0,urp */
 
/* is it a '060 ? */
btst #3,SYMBOL_NAME(boot_info)+LBI_CPU+3
beqs 2f
/* clear user entries in the branch cache */
movec %cacr,%d0
orl #0x00200000,%d0
movec %d0,%cacr
 
2:
/* restore floating point context */
tstb %a0@(LTSS_FPCTXT+27*4)
jeq 1f
fmovemx %a0@(LTSS_FPCTXT),%fp0-%fp7
fmoveml %a0@(LTSS_FPCTXT+24*4),%fpcr/%fpsr/%fpiar
1: frestore %a0@(LTSS_FPCTXT+27*4)
 
/* restore the kernel stack pointer */
movel %a0@(LTSS_KSP),%sp
 
/* restore non-scratch registers */
RESTORE_SWITCH_STACK
 
/* restore user stack pointer */
movel %a0@(LTSS_USP),%d0
movec %d0,%usp
 
/* restore fs (sfc,%dfc) */
movew %a0@(LTSS_FS),%a1
movec %a1,%sfc
movec %a1,%dfc
 
/* restore status register */
movew %a0@(LTSS_SR),%sr
 
rts
 
.data
ALIGN
SYMBOL_NAME_LABEL(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_ni_syscall) /* iopl for i386 */ /* 110 */
.long SYMBOL_NAME(sys_vhangup)
.long SYMBOL_NAME(sys_idle)
.long SYMBOL_NAME(sys_ni_syscall) /* vm86 for i386 */
.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_cacheflush) /* modify_ldt for i386 */
.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 SYMBOL_NAME(sys_ni_syscall) /* 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)
.space (NR_syscalls-163)*4
/head.S
0,0 → 1,1140
/* -*- mode: asm -*-
**
** head.S -- This file contains the initial boot code for the
** Linux/68k kernel.
**
** Copyright 1993 by Hamish Macdonald
**
** 68040 fixes by Michael Rausch
** 68060 fixes by Roman Hodek
**
** Atari support by Andreas Schwab, using ideas of Robert de Vries
** and Bjoern Brauel
**
** 94/11/14 Andreas Schwab: put kernel at PAGESIZE
** 94/11/18 Andreas Schwab: remove identity mapping of STRAM for Atari
** ++ Bjoern & Roman: ATARI-68040 support for the Medusa
** 96/04/26 G|nther Kelleter: fixed identity mapping for Falcon with
** Magnum- and FX-alternate ram
**
** This file is subject to the terms and conditions of the GNU General Public
** License. See the file README.legal in the main directory of this archive
** for more details.
**
*/
 
/*
* Linux startup code.
*
* At this point, the boot loader has:
* Disabled interrupts
* Disabled caches
* Put us in supervisor state.
*
* The kernel setup code takes the following steps:
* Raise interrupt level
* Set up initial kernel memory mapping.
* This sets up a mapping of the 4M of memory the kernel
* is located in. It also does a mapping of any initial
* machine specific areas.
* Note that the kernel is located at virtual address 0x1000 == _start
* Enable cache memories
* Jump to kernel startup
*
* Register d6 contains the CPU flags and d4 the machine type
* from the boot_info information for most of this file.
* The upper word of d6 contains a bit for '040 or '060, since these two
* are quite similar for initial mm setup. Another bit in d6 allows
* distinction of the '060. The lower word of d6 contains the cache mode
* that should be applied to pages containing descriptors. This mode is
* non-cached/non-serialized for the '040 and cacheable/write-through for
* the '060.
*
* General register usage:
* a6 - start of unused memory
* new pages can be allocated from here
* a5 - mmu root table
* a4 - mmu pointer table
* a3 - mmu page tables
* a2 - points to the page table entry for a6
* cache status can be changed (used for '0[46]0)
* you must increase a2 if alloc a new page
* d7 - used for debug output and some macros
* d6 - cpu type and cache mode
* d5 - physical start address of kernel
* d4 - machine type
*/
 
#include <linux/config.h>
#include <linux/autoconf.h>
#include <linux/linkage.h>
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
 
.globl SYMBOL_NAME(kernel_pg_dir), SYMBOL_NAME(kpt)
.globl SYMBOL_NAME(availmem), SYMBOL_NAME(is_medusa)
.globl SYMBOL_NAME(m68k_pgtable_cachemode)
.globl SYMBOL_NAME(kernel_pmd_table), SYMBOL_NAME(swapper_pg_dir)
 
D6B_0460 = 16 /* indicates 680[46]0 in d6 */
D6B_060 = 17 /* indicates 68060 in d6 */
D6F_040 = 1<<D6B_0460
D6F_060 = (1<<D6B_0460)+(1<<D6B_060)
 
/* Translation control register */
TC_ENABLE = 0x8000
TC_PAGE8K = 0x4000
TC_PAGE4K = 0x0000
 
/* Transparent translation registers */
TTR_ENABLE = 0x8000 /* enable transparent translation */
TTR_ANYMODE = 0x4000 /* user and kernel mode access */
TTR_KERNELMODE = 0x2000 /* only kernel mode access */
TTR_USERMODE = 0x0000 /* only user mode access */
TTR_CI = 0x0400 /* inhibit cache */
TTR_RW = 0x0200 /* read/write mode */
TTR_RWM = 0x0100 /* read/write mask */
TTR_FCB2 = 0x0040 /* function code base bit 2 */
TTR_FCB1 = 0x0020 /* function code base bit 1 */
TTR_FCB0 = 0x0010 /* function code base bit 0 */
TTR_FCM2 = 0x0004 /* function code mask bit 2 */
TTR_FCM1 = 0x0002 /* function code mask bit 1 */
TTR_FCM0 = 0x0001 /* function code mask bit 0 */
 
/* Cache Control registers */
CC6_ENABLE_D = 0x80000000 /* enable data cache (680[46]0) */
CC6_FREEZE_D = 0x40000000 /* freeze data cache (68060) */
CC6_ENABLE_SB = 0x20000000 /* enable store buffer (68060) */
CC6_PUSH_DPI = 0x10000000 /* disable CPUSH invalidation (68060) */
CC6_HALF_D = 0x08000000 /* half-cache mode for data cache (68060) */
CC6_ENABLE_B = 0x00800000 /* enable branch cache (68060) */
CC6_CLRA_B = 0x00400000 /* clear all entries in branch cache (68060) */
CC6_CLRU_B = 0x00200000 /* clear user entries in branch cache (68060) */
CC6_ENABLE_I = 0x00008000 /* enable instruction cache (680[46]0) */
CC6_FREEZE_I = 0x00004000 /* freeze instruction cache (68060) */
CC6_HALF_I = 0x00002000 /* half-cache mode for instruction cache (68060) */
CC3_ALLOC_WRITE = 0x00002000 /* write allocate mode(68030) */
CC3_ENABLE_DB = 0x00001000 /* enable data burst (68030) */
CC3_CLR_D = 0x00000800 /* clear data cache (68030) */
CC3_CLRE_D = 0x00000400 /* clear entry in data cache (68030) */
CC3_FREEZE_D = 0x00000200 /* freeze data cache (68030) */
CC3_ENABLE_D = 0x00000100 /* enable data cache (68030) */
CC3_ENABLE_IB = 0x00000010 /* enable instruction burst (68030) */
CC3_CLR_I = 0x00000008 /* clear instruction cache (68030) */
CC3_CLRE_I = 0x00000004 /* clear entry in instruction cache (68030) */
CC3_FREEZE_I = 0x00000002 /* freeze instruction cache (68030) */
CC3_ENABLE_I = 0x00000001 /* enable instruction cache (68030) */
 
/* Miscellaneous definitions */
PAGESIZE = 4096
 
ROOT_TABLE_SIZE = 128
PTR_TABLE_SIZE = 128
PAGE_TABLE_SIZE = 64
ROOT_INDEX_SHIFT = 25
PTR_INDEX_SHIFT = 18
PAGE_INDEX_SHIFT = 12
 
TABLENR_4MB = 16 /* # of page tables needed to page 4 MB */
TABLENR_16MB = 64 /* same for 16 MB */
 
#define putc(ch) moveq &ch,%d7; jbsr Lserial_putc
#define putr() putc(13); putc(10)
#define putn(nr) movel nr,%d7; jbsr Lserial_putnum
 
#define is_not_amiga(lab) moveq &MACH_AMIGA,%d7; cmpl %d4,%d7; jne lab
#define is_not_atari(lab) moveq &MACH_ATARI,%d7; cmpl %d4,%d7; jne lab
 
#define is_040_or_060(lab) btst &D6B_0460,%d6; jne lab
#define is_not_040_or_060(lab) btst &D6B_0460,%d6; jeq lab
#define is_060(lab) btst &D6B_060,%d6; jne lab
#define is_not_060(lab) btst &D6B_060,%d6; jeq lab
 
.text
ENTRY(_stext)
/*
* Version numbers of the bootinfo interface
* The area from _stext to _start will later be used as kernel pointer table
*/
bras 1f /* Jump over bootinfo version numbers */
 
.long BOOTINFOV_MAGIC
.long MACH_AMIGA, AMIGA_BOOTI_VERSION
.long MACH_ATARI, ATARI_BOOTI_VERSION
.long 0
1: jra SYMBOL_NAME(_start)
 
.equ SYMBOL_NAME(kernel_pmd_table),SYMBOL_NAME(_stext)
.equ SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(kernel_pmd_table)
.equ SYMBOL_NAME(swapper_pg_dir),SYMBOL_NAME(kernel_pg_dir)+(ROOT_TABLE_SIZE<<2)
.equ Lavail_pmd_table,SYMBOL_NAME(swapper_pg_dir)+(ROOT_TABLE_SIZE<<2)
 
.equ .,SYMBOL_NAME(_stext)+PAGESIZE
 
ENTRY(_start)
 
/*
* Setup initial stack pointer
*/
lea %pc@(SYMBOL_NAME(_stext):w),%sp
 
/*
* Copy bootinfo from position after BSS to final resting place
*/
lea %pc@(SYMBOL_NAME(_end)),%a0
lea %pc@(SYMBOL_NAME(boot_info)),%a1
movel %pc@(SYMBOL_NAME(bisize)),%d0
subql #1,%d0
1: moveb %a0@+,%a1@+
dbra %d0,1b
 
/*
* Record the CPU and machine type.
*/
lea %pc@(SYMBOL_NAME(boot_info)),%a0
movel %a0@(BI_machtype),%d4
movel %a0@(BI_cputype),%d0
 
btst #CPUB_68060,%d0
jeq 1f
/* '060: d6 := BIT0460|BIT060, cache mode 0x60 (no-cache/non-ser) */
movel #D6F_060+_PAGE_NOCACHE,%d6
jra 2f
1: btst #CPUB_68040,%d0
jeq 1f
/* '040: d6 := BIT0460, cache mode 0x00 (write-through) */
movel #D6F_040+_PAGE_CACHE040W,%d6
jra 2f
1: /* '020 or '030: d6 := no CPU bit, cache mode unused */
moveq #0,%d6
 
2: lea %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%a0
moveq #0,%d0
movew %d6,%d0
movel %d0,%a0@ /* save cache mode for page tables */
 
/*
* raise interrupt level with MASTER bit set, copy isp to msp (if not 68060)
*/
#ifdef FROM_PL9
movew #0x3700,%sr
is_060(1f)
movec %isp,%d0
movel %d0,%sp
1:
#else
movew #0x2700,%sr
#endif
 
/*
* Initialize serial port
*/
jbsr Lserial_init
 
putr()
putc('A')
 
/*
* Get address at end of kernel code/data/bss and
* mask off at a page boundary.
*/
lea %pc@(SYMBOL_NAME(_end)),%a0
addw #PAGESIZE-1,%a0
movel %a0,%d0
andl #-PAGESIZE,%d0
movel %d0,%a6
 
putc('B')
 
/*
* Save physical start address of kernel
*/
lea %pc@(SYMBOL_NAME(_stext)-PAGESIZE:w),%a0
movel %a0,%d5
#ifdef HACKER_KERNEL
lea %pc@(Lkernel_start),%a0
movel %d5,%a0@
#endif
 
/*
* initialize the kernel root table.
*/
lea %pc@(SYMBOL_NAME(kernel_pg_dir):w),%a5
movel %a5,%a0
moveq #ROOT_TABLE_SIZE-1,%d1
1: clrl %a0@+
dbra %d1,1b
 
/*
* Initialize root table descriptor pointing to the kernel pointer
* table.
*/
lea %pc@(Lavail_pmd_table:w),%a4
moveq #_PAGE_TABLE,%d0
addl %a4,%d0
movel %d0,%a5@
 
putc('C')
 
/*
* Initialize the pointer tables referred to above. They either point
* to page tables in the case of the 680[46]0 or contain early
* termination page descriptors in the case of the 68851 or 68030.
*
* Each pointer table entry points to a 64 entry page table. 16 of these
* page tables are grouped to form a single 1024 entry page table which
* fits in a single 4096 byte page.
*
* Some register usages:
* a0 -> pointer table descriptor address
* a1 -> pointer table descriptor
* d1 -> counter
* d2 -> pointer table descriptor increment (varies according to CPU)
*/
 
/* clear the kernel pointer table */
movel %a4,%a0
moveq #PTR_TABLE_SIZE-1,%d1
1: clrl %a0@+
dbra %d1,1b
 
movel %a4,%a0
moveq #15,%d1
 
/*
* base value of pointer table descriptor is either
* the address of the first page table (680[46]0)
* or the base address of physical memory (68030).
*/
is_040_or_060(1f)
 
/* 680[23]0 */
movel %d5,%a1 /* base address */
addql #_PAGE_PRESENT,%a1 /* descriptor type */
movel #PAGE_TABLE_SIZE*PAGESIZE,%d2 /* increment */
jra 2f
 
1: /* 680[46]0 */
movel %a6,%a3 /* base address */
addw #PAGESIZE,%a6 /* allocate page for 16 page tables */
lea %pc@(SYMBOL_NAME(kpt)),%a1
movel %a3,%a1@ /* save address of page table */
movel %a3,%a1
addql #_PAGE_TABLE,%a1 /* descriptor type */
movel #PAGE_TABLE_SIZE<<2,%d2 /* increment */
 
2: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,2b
 
putc('D')
 
/*
* If we are running on a 680[46]0, we have a kernel page table and
* must initialize it. Make the entries point to the first
* 4M of physical memory (the memory we are residing in).
* Set the cache mode bits to Cacheable, Copyback. Set the Global bits
* in the descriptors also.
*/
is_not_040_or_060(Lnot040)
 
putc('F')
 
movel %a3,%a0
movel %d5,%a1
addw #_PAGE_GLOBAL040+_PAGE_CACHE040+_PAGE_PRESENT,%a1
movew #(PAGE_TABLE_SIZE*TABLENR_4MB)-1,%d1
movel #PAGESIZE,%d2
1: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,1b
 
/*
* on the 68040, pages used to hold mmu tables should
* be initialized as noncachable; the '060 allows write-through.
* Do this for the root table page (which also contains
* all pointer tables utilized thus far) and the
* kernel page table.
*/
movel %a5,%d0
subl %d5,%d0
moveq #PAGE_INDEX_SHIFT,%d2
lsrl %d2,%d0
lea %a3@(%d0:l:4),%a2
movel %a2@,%d1
andw #_CACHEMASK040,%d1
orw %d6,%d1
movel %d1,%a2@
 
movel %a3,%d0
subl %d5,%d0
lsrl %d2,%d0
lea %a3@(%d0:l:4),%a2
movel %a2@,%d1
andw #_CACHEMASK040,%d1
orw %d6,%d1
movel %d1,%a2@+
/*
* %a2 points now to the page table entry for available pages at %a6,
* hence caching modes for new pages can easily set unless increasing
* of %a2 are forgotten.
*/
Lnot040:
/*
* Do any machine specific page table initializations.
*/
#ifdef CONFIG_AMIGA
is_not_amiga(Lnotami)
 
/*
* Setup a mapping of the first 16M of physical address space at virtual
* address 0x80000000, using early termination page descriptors for the
* 68030, and proper page tables for the 680[46]0. Set this area as
* non-cacheable.
*/
 
putc('H')
 
is_040_or_060(Lspami68040)
 
/*
* for the 68030, just setup a translation to map in the first
* 32M of physical address space at virtual address 0x80000000
* using an early termination page descriptor.
*/
 
putc('I')
 
moveq #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
movel %d0,%a5@(0x40<<2)
 
jra Lmapphys
 
Lspami68040:
 
/*
* for the 680[46]0, use another pointer table, and allocate 4 more
* page tables. Initialize the pointer table to point to the
* page tables. Then initialize the page tables to point to
* the first 16M of memory, with no caching (noncachable/serialized).
*/
 
/* clear the amiga pointer table */
lea %a4@(PTR_TABLE_SIZE<<2),%a4
moveq #PTR_TABLE_SIZE-1,%d1
1: clrl %a0@+
dbra %d1,1b
 
/* allocate 4 pages for 64 page tables */
movel %a6,%a3
addw #4*PAGESIZE,%a6
 
/* initialize the pointer table */
movel %a4,%a0
movel %a3,%a1
addql #_PAGE_TABLE,%a1 /* base descriptor */
movel #PAGE_TABLE_SIZE<<2,%d2 /* increment */
moveq #TABLENR_16MB-1,%d1
 
1: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,1b
 
/* ensure that the root table points to the pointer table */
movel %a4,%a0
addql #_PAGE_TABLE,%a0
movel %a0,%a5@(0x40<<2)
 
/*
* initialize the page tables
* descriptor bits include noncachable/serialized and global bits.
*/
movel %a3,%a0
movew #_PAGE_GLOBAL040+_PAGE_NOCACHE_S+_PAGE_PRESENT,%a1
movel #PAGESIZE,%d2
movew #(PAGE_TABLE_SIZE*TABLENR_16MB)-1,%d1
 
1: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,1b
 
/*
* Finally, since we just allocated 4 page tables, make sure that
* the virtual mapping of the 4 page tables indicates
* noncachable/serialized.
*/
moveq #3,%d0
1: movel %a2@,%d1 /* a2 already points to root table offset */
andw #_CACHEMASK040,%d1
orw %d6,%d1
movel %d1,%a2@+
dbra %d0,1b
 
jra Lmapphys
 
Lnotami:
#endif
 
#ifdef CONFIG_ATARI
is_not_atari(Lnotatari)
 
move.w #PAGESIZE,%sp
 
/* On the Atari, we map the I/O region (phys. 0x00ffxxxx) by mapping
the last 16 MB of virtual address space to the first 16 MB (i.e.
0xffxxxxxx -> 0x00xxxxxx). For this, an additional pointer table is
needed. I/O ranges are marked non-cachable.
 
For the Medusa it is better to map the I/O region transparently
(i.e. 0xffxxxxxx -> 0xffxxxxxx), because some I/O registers are
accessible only in the high area. The test whether it is a Medusa
is done by writing to the byte at phys. 0x0. This should result
in a bus error on all other machines.
 
...should, but doesn't. The Afterburner040 for the Falcon has the
same behaviour (0x0..0x7 are no ROM shadow). So we have to do
another test to distinguish Medusa and AB040. This is a
read attempt for 0x00ff82fe phys. that should bus error on a Falcon
(+AB040), but is in the range where the Medusa always asserts DTACK.
*/
 
moveq #0,%d3 /* base addr for others: 0x00000000 */
movec %d3,%vbr
lea %pc@(Ltest_berr),%a0
movel %a0,0x8
movel %sp,%a0
moveb 0x0,%d1
clrb 0x0
nop
moveb %d1,0x0
nop
tstb 0x00ff82fe
nop
movel #0xff000000,%d3 /* Medusa base addr: 0xff000000 */
Ltest_berr:
movel %a0,%sp
lea %pc@(SYMBOL_NAME(is_medusa)),%a0
movel %d3,%a0@
 
/* Let the root table point to the new pointer table */
lea %a4@(PTR_TABLE_SIZE<<2),%a4
movel %a4,%a0
addl #_PAGE_TABLE,%a0
movel %a0,%a5@(0x7f<<2) /* 0xFE000000 - 0xFFFFFFFF */
 
/* clear lower half of the pointer table (0xfexxxxxx) */
movel %a4,%a0
movel #(PTR_TABLE_SIZE/2)-1,%d2
1: clrl %a0@+
dbra %d2,1b
 
is_040_or_060(Lspata68040)
 
/* Mapping of the last 16M of virtual address space to the first 16M
for efficient addressing of hardware registers */
movel #PAGE_TABLE_SIZE*PAGESIZE,%d1
movel #(PTR_TABLE_SIZE/2)-1,%d2
movel %d3,%d0
addl #_PAGE_PRESENT,%d0
1: movel %d0,%a0@+
addl %d1,%d0
dbra %d2,1b
moveq #_PAGE_NOCACHE030,%d0 /* make non-cachable */
addl %d0,%a4@(0x7f<<2) /* 0xFFFC0000-0xFFFFFFFF (I/O space) */
/* GK: 0xFFF00000-0xFFF3FFFF (IDE-bus) has to be non-cachable too */
addl %d0,%a4@(0x7c<<2)
 
jra Lmapphys
 
Lspata68040:
/* allocate 4 page tables */
movel %a6,%a3
addw #4*PAGESIZE,%a6
 
/* Initialize the upper half of the pointer table (a0 is still valid) */
movel %a3,%a1
addql #_PAGE_TABLE,%a1
movel #PAGE_TABLE_SIZE<<2,%d2
moveq #TABLENR_16MB-1,%d1
1: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,1b
 
/* Initialize the page tables as noncacheable/serialized! */
movel %a3,%a0
movel %d3,%a1
addw #_PAGE_GLOBAL040+_PAGE_NOCACHE_S+_PAGE_PRESENT,%a1
movel #PAGESIZE,%d2
movew #(PAGE_TABLE_SIZE*TABLENR_16MB)-1,%d1
1: movel %a1,%a0@+
addl %d2,%a1
dbra %d1,1b
 
/*
* Finally, since we just allocated 4 page tables, make sure that
* the virtual mapping of the 4 page tables indicates
* noncachable or write-through.
*/
moveq #3,%d0
1: movel %a2@,%d1 /* a2 already points to root table offset */
andw #_CACHEMASK040,%d1
orw %d6,%d1
movel %d1,%a2@+
dbra %d0,1b
 
Lnotatari:
#endif
 
/*
* Setup a transparent mapping of the physical memory we are executing in.
*
* Only do this if the physical memory is not in the first 16M Meg, or not on
* an Amiga since the first 16M is already identity mapped on the Amiga.
*/
Lmapphys:
putc('J')
 
#ifdef CONFIG_AMIGA
is_not_amiga(Lmapphysnotamiga)
 
/*
* The virtual address of the start of the kernel is 0x1000. We transparently
* translate the memory where we running in and can enable then the MMU. Hence
* we have now two locations of the kernel in memory and can jump to the final
* place. Except if the physical location is in the first 16MB, translation
* will overlap later virtual location, but as we already mapped the first
* 16MB to 0x80000000, we can jump there after translation and MMU is enabled
* and then we can switch off translation and go to the final place.
* When MMU is enabled, stack pointer and Lcustom will become again valid and
* points to the unused first page.
*/
 
/*
* Setup Supervisor Root Pointer register to point to page directory,
* setup translation register contents and enable translation.
*/
putc('K')
 
movew #PAGESIZE,%sp
 
/* fixup the Amiga custom register location before printing */
lea %pc@(Lcustom),%a0
movel #0x80000000,%a0@
 
is_040_or_060(Lamimmu68040)
 
lea 2f:w,%a0
movel %d5,%d0
andl #0xff000000,%d0
jne 1f
lea %pc@(2f+0x80000000),%a0
1: orw #TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0
lea %pc@(Lmmu),%a3
movel %d0,%a3@
.long 0xf0130800 /* pmove %a3@,%tt0 */
/* no limit, 4byte descriptors */
movel #0x80000002,%a3@
movel %a5,%a3@(4)
.long 0xf0134800 /* pmove %a3@,%srp */
.long 0xf0134c00 /* pmove %a3@,%crp */
.long 0xf0002400 /* pflusha */
/*
* enable,super root enable,4096 byte pages,7 bit root index,
* 7 bit pointer index, 6 bit page table index.
*/
movel #0x82c07760,%a3@
.long 0xf0134000 /* pmove %a3@,%tc (enable the MMU) */
jmp %a0@
2: clrl %a3@
.long 0xf0130800 /* pmove %a3@,%tt0 */
jmp LdoneMMUenable:w
 
Lamimmu68040:
 
lea 2f:w,%a0
movel %d5,%d0
andl #0xff000000,%d0
jne 1f
lea %pc@(2f+0x80000000),%a0
1: orw #TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0
.long 0x4e7b0004 /* movec %d0,%itt0 */
.long 0x4e7bd806 /* movec %a5,%urp */
.long 0x4e7bd807 /* movec %a5,%srp */
.word 0xf518 /* pflusha */
movel #TC_ENABLE+TC_PAGE4K,%d0
/*
* this value is also ok for the 68060, we don`t use the cache
* mode/protection defaults
*/
.long 0x4e7b0003 /* movec %d0,%tc (enable the MMU) */
jmp %a0@
2: moveq #0,%d0
.long 0x4e7b0004 /* movec %d0,%itt0 */
jmp LdoneMMUenable:w
 
Lmapphysnotamiga:
#endif
 
#ifdef CONFIG_ATARI
is_not_atari(Lmapphysnotatari)
 
/*
* If the kernel physical address is different from its virtual address, we
* will temporarily set up an identity mapping of the 16MB chunk with
* transparent translation where the kernel is executing.
*/
putc('L')
 
/* fixup the Atari iobase register location before printing */
lea %pc@(Liobase),%a0
movel #0xff000000,%a0@
 
is_040_or_060(Latarimmu68040)
 
lea %pc@(Lmmu),%a3
movel %d5,%d0
jne 1f
lea LdoneMMUenable:w,%a0
jra 3f
1: lea 4f:w,%a0
andl #0xff000000,%d0 /* logical address base */
jeq 2f
orw #TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0
movel %d0,%a3@
.long 0xf0130800 /* pmove %a3@,%tt0 */
jra 3f
/* tt0 doesn't work if physical and virtual address of kernel is in
* the same 16M area (Falcon with Magnum/FX, kernel in alternate ram)
* Transparent translation through kernel pointer table
* Requires that this code until after MMU enabling lies in
* the 256K page around %d5
*/
2: movel %a4@,%d1
andw #0xfff0,%d1
movel %d1,%a1
movel %d5,%d1
moveq #PTR_INDEX_SHIFT,%d0
lsrl %d0,%d1
lea %a1@(%d1:l:4),%a1
movel %d5,%d1
addql #_PAGE_PRESENT,%d1
movel %a1@,%d2
movel %d1,%a1@
lea 5f:w,%a0
/* no limit, 4byte descriptors */
3: movel #0x80000002,%a3@
movel %a5,%a3@(4)
.long 0xf0134800 /* pmove %a3@,%srp */
.long 0xf0134c00 /* pmove %a3@,%crp */
.long 0xf0002400 /* pflusha */
/*
* enable,super root enable,4096 byte pages,7 bit root index,
* 7 bit pointer index, 6 bit page table index.
*/
movel #0x82c07760,%a3@
.long 0xf0134000 /* pmove %a3@,%tc (enable the MMU) */
jmp %a0@
4: clrl %a3@
.long 0xf0130800 /* pmove %a3@,%tt0 */
jra LdoneMMUenable
5: movel %d2,%a1@
jra LdoneMMUenable
 
Latarimmu68040:
movel %d5,%d0
jne 1f
lea LdoneMMUenable:w,%a0
jra 2f
1: lea 3f:w,%a0
andl #0xff000000,%d0 /* logical address base */
orw #TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0
.long 0x4e7b0004 /* movec %d0,%itt0 */
2: .word 0xf518 /* pflusha */
.long 0x4e7bd807 /* movec %a5,%srp */
.long 0x4e7bd806 /* movec %a5,%urp */
movel #TC_ENABLE+TC_PAGE4K,%d0
/*
* this value is also ok for the 68060, we don`t use the cache
* mode/protection defaults
*/
.long 0x4e7b0003 /* movec %d0,%tc (enable the MMU) */
jmp %a0@
3: moveq #0,%d0
.long 0x4e7b0004 /* movec %d0,%itt0 */
tstl %a1
jra LdoneMMUenable
 
Lmapphysnotatari:
#endif
 
LdoneMMUenable:
 
/*
* Fixup the addresses for the kernel pointer table and availmem.
* Convert them from physical addresses to virtual addresses.
*/
 
putc('M')
 
/*
* d5 contains physaddr of kernel start
*/
subl %d5,SYMBOL_NAME(kpt)
 
/*
* do the same conversion on the first available memory
* address (in a6).
*/
subl %d5,%a6
movel %a6,SYMBOL_NAME(availmem) /* first available memory address */
 
putc('N')
 
#if 0
putr()
lea SYMBOL_NAME(kernel_pmd_table),%a0
moveq #63,%d0
1: moveq #7,%d1
putn(%a0)
putc(':')
putc(' ')
2: putn(%a0@+)
dbra %d1,2b
putr()
dbra %d0,1b
putr()
movel SYMBOL_NAME(kpt),%a0
moveq #639,%d0
1: moveq #7,%d1
putn(%a0)
putc(':')
putc(' ')
2: putn(%a0@+)
dbra %d1,2b
putr()
dbra %d0,1b
#endif
/*
* Enable caches
*/
is_040_or_060(Lcache680460)
 
movel #CC3_ENABLE_DB+CC3_CLR_D+CC3_ENABLE_D+CC3_ENABLE_IB+CC3_CLR_I+CC3_ENABLE_I,%d0
movec %d0,%cacr
jra 1f
 
Lcache680460:
.word 0xf4f8 /* cpusha %bc */
 
is_060(Lcache68060)
 
movel #CC6_ENABLE_D+CC6_ENABLE_I,%d0
/* MMU stuff works in copyback mode now, so enable the cache */
movec %d0,%cacr
jra 1f
 
Lcache68060:
movel #CC6_ENABLE_D+CC6_ENABLE_I+CC6_ENABLE_SB+CC6_PUSH_DPI+CC6_ENABLE_B+CC6_CLRA_B,%d0
/* MMU stuff works in copyback mode now, so enable the cache */
movec %d0,%cacr
/* enable superscalar dispatch in PCR */
moveq #1,%d0
.long 0x4e7b0808 /* movec d0,pcr */
1:
 
/*
* Setup initial stack pointer
*/
lea SYMBOL_NAME(init_user_stack)+PAGESIZE,%sp
 
/* jump to the kernel start */
putr()
 
jbsr SYMBOL_NAME(start_kernel)
 
/*
* switch off mmu and exit
*/
 
#ifdef HACKER_KERNEL
ENTRY(kernel_exit)
lea 2f:w,%a0
movel %pc@(Lkernel_start),%a0
lea %a0@(2f:w),%a1
movel %a1,%d0
andl #0xff000000,%d0
jne 1f
jmp %a0@(1f+0x80000000)
1: orw #TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0
.long 0x4e7b0004 /* movec %d0,%itt0 */
jmp %a1@
2: moveq #0,%d0
.long 0x4e7b0003 /* movec %d0,%tc (disable the MMU) */
.word 0xf518 /* pflusha */
.long 0x4e7b0004 /* movec %d0,%itt0 */
movec %d0,%cacr
.word 0xf4f8 /* cpusha %bc */
 
lea %pc@(SYMBOL_NAME(boot_info)),%a0
jmp %a0@(BI_amiga_exit_func:w)@(0:w)
#endif
 
/*
* Serial port output support.
*/
LSERPER = 0xdff032
LSERDAT = 0xdff030
LSERDATR = 0xdff018
LNTSC_PERIOD = 371
LPAL_PERIOD = 368
LNTSC_ECLOCK = 7159090
LSERIAL_CNTRL = 0xbfd000
LSERIAL_DTR = 7
 
/*
* Debug output support
* Atarians have a choice between the parallel port, the serial port
* from the MFP or a serial port of the SCC
*/
 
#ifdef CONFIG_ATARI
/* #define USE_PRINTER */
/* #define USE_SCC */
#define USE_MFP
 
#ifdef USE_PRINTER
 
LPSG_SELECT = 0xff8800
LPSG_READ = 0xff8800
LPSG_WRITE = 0xff8802
LPSG_IO_A = 14
LPSG_IO_B = 15
LPSG_CONTROL = 7
LSTMFP_GPIP = 0xfffa01
LSTMFP_DDR = 0xfffa05
LSTMFP_IERB = 0xfffa09
 
#elif defined(USE_SCC)
LSCC_CTRL_B = 0xff8c85
LSCC_DATA_B = 0xff8c87
 
/* Initialisation table for SCC */
scc_initable:
.byte 9,12 /* Reset */
.byte 4,0x44 /* x16, 1 stopbit, no parity */
.byte 3,0xc0 /* receiver: 8 bpc */
.byte 5,0xe2 /* transmitter: 8 bpc, assert dtr/rts */
.byte 9,0 /* no interrupts */
.byte 10,0 /* NRZ */
.byte 11,0x50 /* use baud rate generator */
.byte 12,24,13,0 /* 9600 baud */
.byte 14,2,14,3 /* use master clock for BRG, enable */
.byte 3,0xc1 /* enable receiver */
.byte 5,0xea /* enable transmitter */
.byte -1
.even
 
#elif defined(USE_MFP)
 
LMFP_UCR = 0xfffa29
LMFP_TDCDR = 0xfffa1d
LMFP_TDDR = 0xfffa25
LMFP_TSR = 0xfffa2d
LMFP_UDR = 0xfffa2f
 
#endif
#endif
 
/*
* Initialize serial port hardware for 9600/8/1
* a0 thrashed
* Atari d0 trashed (a1 in case of SCC)
*/
.even
Lserial_init:
#ifdef CONFIG_AMIGA
cmpil #MACH_AMIGA,%d4
jne 1f
lea %pc@(SYMBOL_NAME(boot_info)),%a0
bclr #LSERIAL_DTR,LSERIAL_CNTRL
movew #LNTSC_PERIOD,LSERPER
cmpl #LNTSC_ECLOCK,%a0@(BI_amiga_eclock)
jeq 9f
movew #LPAL_PERIOD,LSERPER
jra 9f
1:
#endif
#ifdef CONFIG_ATARI
cmpil #MACH_ATARI,%d4
jne 4f
#ifdef USE_PRINTER
bclr #0,LSTMFP_IERB
bclr #0,LSTMFP_DDR
moveb #LPSG_CONTROL,LPSG_SELECT
moveb #0xff,LPSG_WRITE
moveb #LPSG_IO_B,LPSG_SELECT
clrb LPSG_WRITE
moveb #LPSG_IO_A,LPSG_SELECT
moveb LPSG_READ,%d0
bset #5,%d0
moveb %d0,LPSG_WRITE
#elif defined(USE_SCC)
lea LSCC_CTRL_B,%a0
lea %pc@(scc_initable:w),%a1
2: moveb %a1@+,%d0
jmi 3f
moveb %d0,%a0@
moveb %a1@+,%a0@
jra 2b
3: clrb %a0@
#elif defined(USE_MFP)
bclr #1,LMFP_TSR
moveb #0x88,LMFP_UCR
andb #0x70,LMFP_TDCDR
moveb #2,LMFP_TDDR
orb #1,LMFP_TDCDR
bset #1,LMFP_TSR
#endif
4:
#endif
9:
rts
 
/*
* Output character in d7 on serial port.
* d7 thrashed.
*/
Lserial_putc:
moveml %a0/%a1,%sp@-
#ifdef CONFIG_AMIGA
cmpil #MACH_AMIGA,%d4
jne 2f
andw #0x00ff,%d7
oriw #0x0100,%d7
movel %pc@(Lcustom),%a1
movew %d7,%a1@(LSERDAT)
1: movew %a1@(LSERDATR),%d7
andw #0x2000,%d7
jeq 1b
jra 9f
2:
#endif
#ifdef CONFIG_ATARI
cmpil #MACH_ATARI,%d4
jne 4f
movel %pc@(Liobase),%a1
#ifdef USE_PRINTER
3: btst #0,%a1@(LSTMFP_GPIP)
jne 3b
moveb #LPSG_IO_B,%a1@(LPSG_SELECT)
moveb %d7,%a1@(LPSG_WRITE)
moveb #LPSG_IO_A,%a1@(LPSG_SELECT)
moveb %a1@(LPSG_READ),%d7
bclr #5,%d7
moveb %d7,%a1@(LPSG_WRITE)
nop
nop
bset #5,%d7
moveb %d7,%a1@(LPSG_WRITE)
#elif defined(USE_SCC)
3: btst #2,%a1@(LSCC_CTRL_B)
jeq 3b
moveb %d7,%a1@(LSCC_DATA_B)
#elif defined(USE_MFP)
3: btst #7,%a1@(LMFP_TSR)
jeq 3b
moveb %d7,%a1@(LMFP_UDR)
#endif
4:
#endif
9:
moveml %sp@+,%a0/%a1
rts
 
/*
* Output string pointed to by a0 to serial port.
* a0 trashed.
*/
Lserial_puts:
movel %d7,%sp@-
1: moveb %a0@+,%d7
jeq 2f
jbsr Lserial_putc
jra 1b
2: movel %sp@+,%d7
rts
 
/*
* Output number in d7 in hex notation on serial port.
* d0-d2 trashed.
* d7 trashed.
*/
 
Lserial_putnum:
moveml %d0-%d2/%d7,%sp@-
movel %d7,%d1
moveq #4,%d0
moveq #7,%d2
L1: roll %d0,%d1
moveb %d1,%d7
andb #0x0f,%d7
cmpb #0x0a,%d7
jcc 1f
addb #'0',%d7
jra 2f
1: addb #'A'-10,%d7
2: jbsr Lserial_putc
dbra %d2,L1
moveq #32,%d7
jbsr Lserial_putc
moveml %sp@+,%d0-%d2/%d7
rts
 
Lshowtest:
moveml %a0/%d7,%sp@-
putc('A')
putc('=')
putn(%a1)
 
ptestr #5,%a1@,#7,%a0
 
putc('D')
putc('A')
putc('=')
putn(%a0)
 
putc('D')
putc('=')
putn(%a0@)
 
putc('S')
putc('=')
lea %pc@(Lmmu),%a0
pmove %psr,%a0@
clrl %d7
movew %a0@,%d7
jbsr Lserial_putnum
 
putr()
moveml %sp@+,%a0/%d7
rts
 
.data
.even
#ifdef HACKER_KERNEL
Lkernel_start:
.long 0
#endif
Lcustom:
Liobase:
.long 0
Lmmu: .quad 0
SYMBOL_NAME_LABEL(kpt)
.long 0
SYMBOL_NAME_LABEL(availmem)
.long 0
SYMBOL_NAME_LABEL(is_medusa)
.long 0
SYMBOL_NAME_LABEL(m68k_pgtable_cachemode)
.long 0
/console.c
0,0 → 1,2572
/*
* linux/drivers/char/console.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* console.c
*
* This module exports the console io functions:
*
* 'void do_keyboard_interrupt(void)'
*
* 'int vc_allocate(unsigned int console)'
* 'int vc_cons_allocated(unsigned int console)'
* 'int vc_resize(unsigned long lines, unsigned long cols)'
* 'int vc_resize_con(unsigned long lines, unsigned long cols,
* unsigned int currcons)'
* 'void vc_disallocate(unsigned int currcons)'
*
* 'unsigned long con_init(unsigned long)'
* 'int con_open(struct tty_struct *tty, struct file * filp)'
* 'void con_write(struct tty_struct * tty)'
* 'void console_print(const char * b)'
* 'void update_screen(int new_console)'
*
* 'void do_blank_screen(int)'
* 'void do_unblank_screen(void)'
* 'void poke_blanked_console(void)'
*
* 'unsigned short *screen_pos(int currcons, int w_offset, int viewed)'
* 'void complement_pos(int currcons, int offset)'
* 'void invert_screen(int currcons, int offset, int count, int shift)'
*
* 'void scrollback(int lines)'
* 'void scrollfront(int lines)'
*
* 'int con_get_font(char *)'
* 'int con_set_font(char *)'
*
* 'void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)'
* 'int mouse_reporting(void)'
*
* 'unsigned long get_video_num_lines(unsigned int console)'
* 'unsigned long get_video_num_columns(unsigned int console)'
* 'unsigned long get_video_size_row(unsigned int console)'
*
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
*
* Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
* Chars, and VT100 enhancements by Peter MacDonald.
*
* Copy and paste function by Andrew Haylett,
* some enhancements by Alessandro Rubini.
*
* User definable mapping table and font loading by Eugene G. Crosser,
* <crosser@pccross.msk.su>
*
* Code to check for different video-cards mostly by Galen Hunt,
* <g-hunt@ee.utah.edu>
*
* Rudimentary ISO 10646/Unicode/UTF-8 character set support by
* Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>.
*
* Dynamic allocation of consoles, aeb@cwi.nl, May 1994
* Resizing of consoles, aeb, 940926
*
* Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94
* <poe@daimi.aau.dk>
*
* 680x0 LINUX support by Arno Griffioen (arno@usn.nl)
*
* 9-Apr-94: Arno Griffioen: fixed scrolling and delete-char bug.
* Scrolling code moved to amicon.c
*
* 18-Apr-94: David Carter [carter@cs.bris.ac.uk]. 680x0 LINUX modified
* Integrated support for new low level driver `amicon_ocs.c'
*
*/
 
#define BLANK 0x0020
#undef CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */
 
/* A bitmap for codes <32. A bit of 1 indicates that the code
* corresponding to that bit number invokes some special action
* (such as cursor movement) and should not be displayed as a
* glyph unless the disp_ctrl mode is explicitly enabled.
*/
#define CTRL_ACTION 0x0d00ff81
#define CTRL_ALWAYS 0x0800f501 /* Cannot be overridden by disp_ctrl */
 
/*
* Here is the default bell parameters: 750HZ, 1/8th of a second
*/
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
 
/*
* NOTE!!! We sometimes disable and enable interrupts for a short while
* (to put a word in video IO), but this will work even for keyboard
* interrupts. We know interrupts aren't enabled when getting a keyboard
* interrupt, as we use trap-gates. Hopefully all is well.
*/
 
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/console.h>
#include <linux/kd.h>
#include <linux/malloc.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/ioport.h>
 
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
 
#include "../../../drivers/char/kbd_kern.h"
#include "../../../drivers/char/vt_kern.h"
#include "../../../drivers/char/consolemap.h"
#include "../../../drivers/char/selection.h"
 
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
struct tty_driver console_driver;
static int console_refcount;
static struct tty_struct *console_table[MAX_NR_CONSOLES];
static struct termios *console_termios[MAX_NR_CONSOLES];
static struct termios *console_termios_locked[MAX_NR_CONSOLES];
 
static void vc_init(unsigned int console, int do_clear);
 
static void update_attr(int currcons);
static void gotoxy(int currcons, int new_x, int new_y);
static void save_cur(int currcons);
static void blank_screen(void);
static void unblank_screen(void);
extern void change_console(unsigned int);
static inline void set_cursor(int currcons);
static void reset_terminal(int currcons, int do_clear);
extern void reset_vc(unsigned int new_console);
extern void vt_init(void);
extern void register_console(void (*proc)(const char *));
extern void vesa_blank(void);
extern void vesa_unblank(void);
extern void compute_shiftstate(void);
void poke_blanked_console(void);
void do_blank_screen(int);
 
unsigned long video_num_lines;
unsigned long video_num_columns;
unsigned long video_size_row;
 
static int printable = 0; /* Is console ready for printing? */
unsigned long video_font_height; /* Height of current screen font */
unsigned long video_scan_lines; /* Number of scan lines on screen */
unsigned long default_font_height; /* Height of default screen font */
int video_mode_512ch = 0; /* 512-character mode */
static unsigned short console_charmask = 0x0ff;
 
static unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
 
/* used by kbd_bh - set by keyboard_interrupt */
int do_poke_blanked_console = 0;
int console_blanked = 0;
static int blankinterval = 10*60*HZ;
 
static struct vc {
struct vc_data *d;
 
/* might add scrmem, vt_struct, kbd at some time,
to have everything in one place - the disadvantage
would be that vc_cons etc can no longer be static */
} vc_cons [MAX_NR_CONSOLES];
struct consw *conswitchp;
 
#define cols (vc_cons[currcons].d->vc_cols)
#define rows (vc_cons[currcons].d->vc_rows)
#define size_row (vc_cons[currcons].d->vc_size_row)
#define screenbuf_size (vc_cons[currcons].d->vc_screenbuf_size)
#define cons_num (vc_cons[currcons].d->vc_num)
#define origin (vc_cons[currcons].d->vc_origin)
#define scr_end (vc_cons[currcons].d->vc_scr_end)
#define pos (vc_cons[currcons].d->vc_pos)
#define top (vc_cons[currcons].d->vc_top)
#define bottom (vc_cons[currcons].d->vc_bottom)
#define x (vc_cons[currcons].d->vc_x)
#define y (vc_cons[currcons].d->vc_y)
#define vc_state (vc_cons[currcons].d->vc_state)
#define npar (vc_cons[currcons].d->vc_npar)
#define par (vc_cons[currcons].d->vc_par)
#define ques (vc_cons[currcons].d->vc_ques)
#define attr (vc_cons[currcons].d->vc_attr)
#define saved_x (vc_cons[currcons].d->vc_saved_x)
#define saved_y (vc_cons[currcons].d->vc_saved_y)
#define translate (vc_cons[currcons].d->vc_translate)
#define G0_charset (vc_cons[currcons].d->vc_G0_charset)
#define G1_charset (vc_cons[currcons].d->vc_G1_charset)
#define saved_G0 (vc_cons[currcons].d->vc_saved_G0)
#define saved_G1 (vc_cons[currcons].d->vc_saved_G1)
#define utf (vc_cons[currcons].d->vc_utf)
#define utf_count (vc_cons[currcons].d->vc_utf_count)
#define utf_char (vc_cons[currcons].d->vc_utf_char)
#define video_mem_start (vc_cons[currcons].d->vc_video_mem_start)
#define video_mem_end (vc_cons[currcons].d->vc_video_mem_end)
#define video_erase_char (vc_cons[currcons].d->vc_video_erase_char)
#define disp_ctrl (vc_cons[currcons].d->vc_disp_ctrl)
#define toggle_meta (vc_cons[currcons].d->vc_toggle_meta)
#define decscnm (vc_cons[currcons].d->vc_decscnm)
#define decom (vc_cons[currcons].d->vc_decom)
#define decawm (vc_cons[currcons].d->vc_decawm)
#define deccm (vc_cons[currcons].d->vc_deccm)
#define decim (vc_cons[currcons].d->vc_decim)
#define deccolm (vc_cons[currcons].d->vc_deccolm)
#define need_wrap (vc_cons[currcons].d->vc_need_wrap)
#define has_scrolled (vc_cons[currcons].d->vc_has_scrolled)
#define kmalloced (vc_cons[currcons].d->vc_kmalloced)
#define report_mouse (vc_cons[currcons].d->vc_report_mouse)
#define can_do_color (vc_cons[currcons].d->vc_can_do_color)
#define color (vc_cons[currcons].d->vc_color)
#define s_color (vc_cons[currcons].d->vc_s_color)
#define def_color (vc_cons[currcons].d->vc_def_color)
#define foreground (color & 0x0f)
#define background (color & 0xf0)
#define charset (vc_cons[currcons].d->vc_charset)
#define s_charset (vc_cons[currcons].d->vc_s_charset)
#define intensity (vc_cons[currcons].d->vc_intensity)
#define underline (vc_cons[currcons].d->vc_underline)
#define blink (vc_cons[currcons].d->vc_blink)
#define reverse (vc_cons[currcons].d->vc_reverse)
#define s_intensity (vc_cons[currcons].d->vc_s_intensity)
#define s_underline (vc_cons[currcons].d->vc_s_underline)
#define s_blink (vc_cons[currcons].d->vc_s_blink)
#define s_reverse (vc_cons[currcons].d->vc_s_reverse)
#define ulcolor (vc_cons[currcons].d->vc_ulcolor)
#define halfcolor (vc_cons[currcons].d->vc_halfcolor)
#define tab_stop (vc_cons[currcons].d->vc_tab_stop)
#define bell_pitch (vc_cons[currcons].d->vc_bell_pitch)
#define bell_duration (vc_cons[currcons].d->vc_bell_duration)
#define sw (vc_cons[currcons].d->vc_sw)
 
#define vcmode (vt_cons[currcons]->vc_mode)
#if 0 /* XXX */
#define vtmode (vt_cons[currcons]->vt_mode)
#define vtpid (vt_cons[currcons]->vt_pid)
#define vtnewvt (vt_cons[currcons]->vt_newvt)
#endif
 
#define structsize (sizeof(struct vc_data) + sizeof(struct vt_struct))
 
int vc_cons_allocated(unsigned int i)
{
return (i < MAX_NR_CONSOLES && vc_cons[i].d);
}
 
int vc_allocate(unsigned int currcons) /* return 0 on success */
{
if (currcons >= MAX_NR_CONSOLES)
return -ENODEV;
if (!vc_cons[currcons].d) {
long p, q;
 
/* prevent users from taking too much memory */
if (currcons >= MAX_NR_USER_CONSOLES && !suser())
return -EPERM;
 
/* due to the granularity of kmalloc, we waste some memory here */
/* the alloc is done in two steps, to optimize the common situation
of a 25x80 console (structsize=216, screenbuf_size=4000) */
p = (long) kmalloc(structsize, GFP_KERNEL);
if (!p)
return -ENOMEM;
vc_cons[currcons].d = (struct vc_data *) p;
vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data));
 
/* ++Geert: sw->con_init determines console size */
sw = conswitchp;
cons_num = currcons;
sw->con_init (vc_cons[currcons].d);
size_row = cols<<1;
screenbuf_size = rows*size_row;
 
q = (long) kmalloc(screenbuf_size, GFP_KERNEL);
if (!q) {
kfree_s((char *) p, structsize);
vc_cons[currcons].d = NULL;
return -ENOMEM;
}
vc_scrbuf[currcons] = (unsigned short *) q;
kmalloced = 1;
vc_init (currcons, 1);
}
return 0;
}
 
/*
* Change # of rows and columns (0 means the size of fg_console)
* [this is to be used together with some user program
* like resize that changes the hardware videomode]
*/
int vc_resize(unsigned long lines, unsigned long columns)
{
unsigned long cc, ll, ss, sr;
unsigned long occ, oll, oss, osr;
unsigned short *p;
unsigned int currcons = fg_console, i;
unsigned short *newscreens[MAX_NR_CONSOLES];
long ol, nl, rlth, rrem;
 
cc = (columns ? columns : cols);
ll = (lines ? lines : rows);
sr = cc << 1;
ss = sr * ll;
 
/*
* Some earlier version had all consoles of potentially
* different sizes, but that was really messy.
* So now we only change if there is room for all consoles
* of the same size.
*/
for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) {
if (!vc_cons_allocated(currcons))
newscreens[currcons] = 0;
else {
p = (unsigned short *) kmalloc(ss, GFP_USER);
if (!p) {
for (i = 0; i< currcons; i++)
if (newscreens[i])
kfree_s(newscreens[i], ss);
return -ENOMEM;
}
newscreens[currcons] = p;
}
}
 
#if 0 /* XXX */
get_scrmem(fg_console);
#endif
 
for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) {
if (!vc_cons_allocated(currcons))
continue;
 
oll = rows;
occ = cols;
osr = size_row;
oss = screenbuf_size;
 
rows = ll;
cols = cc;
size_row = sr;
screenbuf_size = ss;
 
rlth = MIN(osr, sr);
rrem = sr - rlth;
ol = origin;
nl = (long) newscreens[currcons];
if (ll < oll)
ol += (oll - ll) * osr;
 
update_attr(currcons);
while (ol < scr_end) {
/* ++Geert: TODO: Because the attributes have different meanings
on monochrome and color, they should really be converted if
can_do_color changes... */
memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
if (rrem)
memsetw((void *)(nl + rlth), video_erase_char, rrem);
ol += osr;
nl += sr;
}
 
if (kmalloced)
kfree_s(vc_scrbuf[currcons], oss);
vc_scrbuf[currcons] = newscreens[currcons];
kmalloced = 1;
screenbuf_size = ss;
 
origin = (long) video_mem_start = vc_scrbuf[currcons];
scr_end = video_mem_end = ((long) video_mem_start) + ss;
 
if (scr_end > nl)
memsetw((void *) nl, video_erase_char, scr_end - nl);
 
/* do part of a reset_terminal() */
top = 0;
bottom = rows;
gotoxy(currcons, x, y);
save_cur(currcons);
}
 
#if 0 /* XXX */
set_scrmem(fg_console, 0);
set_origin(fg_console);
#endif /* XXX */
update_screen(fg_console);
set_cursor(fg_console);
 
return 0;
}
 
/*
* ++Geert: Change # of rows and columns for one specific console.
* Of course it's not messy to have all consoles of potentially different sizes,
* except on PCish hardware :-)
*
* This is called by the low level console driver (arch/m68k/console/fbcon.c or
* arch/m68k/console/txtcon.c)
*/
void vc_resize_con(unsigned long lines, unsigned long columns,
unsigned int currcons)
{
unsigned long cc, ll, ss, sr;
unsigned long occ, oll, oss, osr;
unsigned short *newscreen;
long ol, nl, rlth, rrem;
struct winsize ws;
 
if (!columns || !lines || currcons >= MAX_NR_CONSOLES)
return;
 
cc = columns;
ll = lines;
sr = cc << 1;
ss = sr * ll;
 
if (!vc_cons_allocated(currcons))
newscreen = 0;
else if (!(newscreen = (unsigned short *) kmalloc(ss, GFP_USER)))
return;
 
if (vc_cons_allocated(currcons)) {
oll = rows;
occ = cols;
osr = size_row;
oss = screenbuf_size;
 
rows = ll;
cols = cc;
size_row = sr;
screenbuf_size = ss;
 
rlth = MIN(osr, sr);
rrem = sr - rlth;
ol = origin;
nl = (long) newscreen;
if (ll < oll)
ol += (oll - ll) * osr;
 
update_attr(currcons);
while (ol < scr_end) {
/* ++Geert: TODO: Because the attributes have different meanings
on monochrome and color, they should really be converted if
can_do_color changes... */
memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
if (rrem)
memsetw((void *)(nl + rlth), video_erase_char, rrem);
ol += osr;
nl += sr;
}
 
if (kmalloced)
kfree_s(vc_scrbuf[currcons], oss);
vc_scrbuf[currcons] = newscreen;
kmalloced = 1;
screenbuf_size = ss;
 
origin = (long) video_mem_start = vc_scrbuf[currcons];
scr_end = video_mem_end = ((long)video_mem_start) + ss;
 
if (scr_end > nl)
memsetw((void *) nl, video_erase_char, scr_end - nl);
 
/* do part of a reset_terminal() */
top = 0;
bottom = rows;
gotoxy(currcons, x, y);
save_cur(currcons);
 
ws.ws_row = rows;
ws.ws_col = cols;
if (memcmp(&ws, &console_table[currcons]->winsize, sizeof (struct winsize)) &&
console_table[currcons]->pgrp > 0)
kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1);
console_table[currcons]->winsize = ws;
}
 
if (currcons == fg_console)
update_screen(fg_console);
}
 
void vc_disallocate(unsigned int currcons)
{
if (vc_cons_allocated(currcons)) {
if (kmalloced)
kfree_s(vc_scrbuf[currcons], screenbuf_size);
if (currcons >= MIN_NR_CONSOLES)
kfree_s(vc_cons[currcons].d, structsize);
vc_cons[currcons].d = 0;
}
}
 
 
#define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x)
#define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x)
#define is_kbd(x) vc_kbd_mode(kbd_table+currcons,x)
 
#define decarm VC_REPEAT
#define decckm VC_CKMODE
#define kbdapplic VC_APPLIC
#define lnm VC_CRLF
 
/*
* this is what the terminal answers to a ESC-Z or csi0c query.
*/
#define VT100ID "\033[?1;2c"
#define VT102ID "\033[?6c"
 
static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
8,12,10,14, 9,13,11,15 };
 
/*
* gotoxy() must verify all boundaries, because the arguments
* might also be negative. If the given position is out of
* bounds, the cursor is placed at the nearest margin.
*/
static void gotoxy(int currcons, int new_x, int new_y)
{
int max_y;
 
if (new_x < 0)
x = 0;
else
if (new_x >= cols)
x = cols - 1;
else
x = new_x;
if (decom) {
new_y += top;
max_y = bottom;
} else
max_y = rows;
if (new_y < 0)
y = 0;
else
if (new_y >= max_y)
y = max_y - 1;
else
y = new_y;
pos = video_mem_start + y * cols + x;
need_wrap = 0;
}
 
static void hide_cursor(int currcons)
{
sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
return;
}
 
static void set_cursor(int currcons)
{
if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
return;
if (deccm)
sw->con_cursor(vc_cons[currcons].d,CM_DRAW);
else
hide_cursor(currcons);
return;
}
 
void no_scroll(char *str, int *ints)
{
/*
* no_scroll currently does nothing on the m68k.
*/
}
 
/*
* Arno:
* Why do we need these? The keyboard code doesn't seem to do anything
* with them either...
*/
void scrollfront(int l)
{
return;
}
 
void scrollback(int l)
{
return;
}
 
static void scrup(int currcons, unsigned int t, unsigned int b,
int nr)
{
unsigned short *p;
int i;
 
if (b > rows || t >= b)
return;
 
memmove (video_mem_start + t * cols,
video_mem_start + (t + nr) * cols,
(b - t - nr) * cols * 2);
 
p = video_mem_start + (b - nr) * cols;
for (i = nr * cols; i > 0; i--)
*p++ = video_erase_char;
 
if (currcons != fg_console)
return;
/*
* Arno:
* Scrolling has now been moved to amicon.c where it should have
* been all along.
*/
sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr);
 
return;
}
 
static void scrdown(int currcons, unsigned int t, unsigned int b,
int nr)
{
unsigned short *p;
int i;
 
if (b > rows || t >= b)
return;
 
memmove (video_mem_start + (t + nr) * cols,
video_mem_start + t * cols,
(b - t - nr) * cols * 2);
 
p = video_mem_start + t * cols;
for (i = nr * cols; i > 0; i--)
*p++ = video_erase_char;
 
if (currcons != fg_console)
return;
/*
* Arno:
* Scrolling has now been moved to amicon.c where it should have
* been all along.
*/
sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr);
 
return;
}
 
static void lf(int currcons)
{
/* don't scroll if above bottom of scrolling region, or
* if below scrolling region
*/
if (y+1 == bottom)
scrup(currcons,top,bottom, 1);
else if (y < rows-1) {
y++;
pos += cols;
}
need_wrap = 0;
}
 
static void ri(int currcons)
{
/* don't scroll if below top of scrolling region, or
* if above scrolling region
*/
if (y == top)
scrdown(currcons,top,bottom, 1);
else if (y > 0) {
y--;
pos -= cols;
}
need_wrap = 0;
}
 
static inline void cr(int currcons)
{
pos -= x;
need_wrap = x = 0;
}
 
static inline void bs(int currcons)
{
if (x) {
pos--;
x--;
need_wrap = 0;
}
}
 
static inline void del(int currcons)
{
/* ignored */
}
 
static void csi_J(int currcons, int vpar)
{
unsigned long count;
unsigned short *start;
 
switch (vpar) {
case 0: /* erase from cursor to end of display */
count = (video_mem_start
+ cols * rows
- pos);
start = pos;
if (currcons != fg_console)
break;
/* 680x0 do in two stages */
sw->con_clear(vc_cons[currcons].d,y,x,1,cols-x);
sw->con_clear(vc_cons[currcons].d,y+1,0,rows-y-1, cols);
break;
case 1: /* erase from start to cursor */
count = pos - video_mem_start + 1;
start = video_mem_start;
if (currcons != fg_console)
break;
/* 680x0 do in two stages */
sw->con_clear(vc_cons[currcons].d,0,0,y, cols);
sw->con_clear(vc_cons[currcons].d,y,0,1,x + 1);
break;
case 2: /* erase whole display */
count = cols * rows;
start = video_mem_start;
if (currcons != fg_console)
break;
sw->con_clear(vc_cons[currcons].d,0,0,rows, cols);
break;
default:
return;
}
while (count-- > 0)
*start++ = video_erase_char;
need_wrap = 0;
}
 
static void csi_K(int currcons, int vpar)
{
unsigned long count;
unsigned short *start;
 
switch (vpar) {
case 0: /* erase from cursor to end of line */
count = cols - x;
start = pos;
if (currcons != fg_console)
break;
sw->con_clear(vc_cons[currcons].d,y,x,1,cols-x);
break;
case 1: /* erase from start of line to cursor */
start = pos - x;
count = x + 1;
if (currcons != fg_console)
break;
sw->con_clear(vc_cons[currcons].d,y,0,1,x + 1);
break;
case 2: /* erase whole line */
start = pos - x;
count = cols;
if (currcons != fg_console)
break;
sw->con_clear(vc_cons[currcons].d,y,0,1,cols);
break;
default:
return;
}
while (count-- > 0)
*start++ = video_erase_char;
need_wrap = 0;
}
 
static void csi_X(int currcons, int vpar) /* erase the following vpar positions */
{ /* not vt100? */
unsigned long count;
unsigned short * start;
 
if (!vpar)
vpar++;
 
start=pos;
count=(vpar > cols-x) ? (cols-x) : vpar;
 
if (currcons == fg_console)
sw->con_clear(vc_cons[currcons].d,y,x,1,count);
 
while (count-- > 0)
*start++ = video_erase_char;
need_wrap = 0;
}
 
/*
* Arno:
* On 680x0 attributes are currently not used. This piece of code
* seems hardware independent, but uses the EGA/VGA way of representing
* attributes.
* TODO: modify for 680x0 and add attribute processing to putc code.
*
* ++roman: I completely changed the attribute format for monochrome
* mode (!can_do_color). The formerly used MDA (monochrome display
* adapter) format didn't allow the combination of certain effects.
* Now the attribute is just a bit vector:
* Bit 0..1: intensity (0..2)
* Bit 2 : underline
* Bit 3 : reverse
* Bit 7 : blink
*/
static void update_attr(int currcons)
{
if (!can_do_color) {
/* Special treatment for monochrome */
attr = intensity |
(underline ? 4 : 0) |
((reverse ^ decscnm) ? 8 : 0) |
(blink ? 0x80 : 0);
video_erase_char = ' ' | ((reverse ^ decscnm) ? 0x800 : 0);
return;
}
 
attr = color;
if (underline)
attr = (attr & 0xf0) | ulcolor;
else if (intensity == 0)
attr = (attr & 0xf0) | halfcolor;
if (reverse ^ decscnm)
attr = reverse_video_char(attr);
if (blink)
attr ^= 0x80;
if (intensity == 2)
attr ^= 0x08;
if (decscnm)
video_erase_char = (reverse_video_char(color) << 8) | ' ';
else
video_erase_char = (color << 8) | ' ';
}
 
static void default_attr(int currcons)
{
intensity = 1;
underline = 0;
reverse = 0;
blink = 0;
color = def_color;
}
 
static void csi_m(int currcons)
{
int i;
 
for (i=0;i<=npar;i++)
switch (par[i]) {
case 0: /* all attributes off */
default_attr(currcons);
break;
case 1:
intensity = 2;
break;
case 2:
intensity = 0;
break;
case 4:
underline = 1;
break;
case 5:
blink = 1;
break;
case 7:
reverse = 1;
break;
case 10: /* ANSI X3.64-1979 (SCO-ish?)
* Select primary font, don't display
* control chars if defined, don't set
* bit 8 on output.
*/
translate = set_translate(charset == 0
? G0_charset
: G1_charset);
disp_ctrl = 0;
toggle_meta = 0;
break;
case 11: /* ANSI X3.64-1979 (SCO-ish?)
* Select first alternate font, let's
* chars < 32 be displayed as ROM chars.
*/
translate = set_translate(IBMPC_MAP);
disp_ctrl = 1;
toggle_meta = 0;
break;
case 12: /* ANSI X3.64-1979 (SCO-ish?)
* Select second alternate font, toggle
* high bit before displaying as ROM char.
*/
translate = set_translate(IBMPC_MAP);
disp_ctrl = 1;
toggle_meta = 1;
break;
case 21:
case 22:
intensity = 1;
break;
case 24:
underline = 0;
break;
case 25:
blink = 0;
break;
case 27:
reverse = 0;
break;
case 38: /* ANSI X3.64-1979 (SCO-ish?)
* Enables underscore, white foreground
* with white underscore (Linux - use
* default foreground).
*/
color = (def_color & 0x0f) | background;
underline = 1;
break;
case 39: /* ANSI X3.64-1979 (SCO-ish?)
* Disable underline option.
* Reset colour to default? It did this
* before...
*/
color = (def_color & 0x0f) | background;
underline = 0;
break;
case 49:
color = (def_color & 0xf0) | foreground;
break;
default:
if (par[i] >= 30 && par[i] <= 37)
color = color_table[par[i]-30]
| background;
else if (par[i] >= 40 && par[i] <= 47)
color = (color_table[par[i]-40]<<4)
| foreground;
break;
}
update_attr(currcons);
}
 
static void respond_string(const char * p, struct tty_struct * tty)
{
while (*p) {
tty_insert_flip_char(tty, *p, 0);
p++;
}
tty_schedule_flip(tty);
}
 
static void cursor_report(int currcons, struct tty_struct * tty)
{
char buf[40];
 
sprintf(buf, "\033[%ld;%ldR", y + (decom ? top+1 : 1), x+1);
respond_string(buf, tty);
}
 
static inline void status_report(struct tty_struct * tty)
{
respond_string("\033[0n", tty); /* Terminal ok */
}
 
static inline void respond_ID(struct tty_struct * tty)
{
respond_string(VT102ID, tty);
}
 
void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)
{
char buf[8];
 
sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
(char)('!' + mry));
respond_string(buf, tty);
}
 
/* invoked via ioctl(TIOCLINUX) */
int mouse_reporting(void)
{
int currcons = fg_console;
 
return report_mouse;
}
 
static inline unsigned short *screenpos(int currcons, int offset, int viewed)
{
unsigned short *p = (unsigned short *)(origin + offset);
#if 0
if (viewed && currcons == fg_console)
p -= (__real_origin - __origin);
#endif
return p;
}
 
/* Note: inverting the screen twice should revert to the original state */
void invert_screen(int currcons, int offset, int count, int viewed)
{
unsigned short *p;
unsigned short xx, yy, oldattr;
 
count /= 2;
p = screenpos(currcons, offset, viewed);
xx = (offset >> 1) % cols;
yy = (offset >> 1) / cols;
oldattr = attr;
if (can_do_color)
while (count--) {
unsigned short old = scr_readw(p);
unsigned short new = reverse_video_short(old);
scr_writew(new, p);
p++;
if (currcons != fg_console)
continue;
attr = new >> 8;
sw->con_putc(vc_cons[currcons].d, new & 0xff, yy, xx);
if (++xx == cols)
xx = 0, ++yy;
}
else
while (count--) {
unsigned short old = scr_readw(p);
unsigned short new = old ^ 0x800;
scr_writew(new, p);
p++;
if (currcons != fg_console)
continue;
attr = new >> 8;
sw->con_putc(vc_cons[currcons].d, new & 0xff, yy, xx);
if (++xx == cols)
xx = 0, ++yy;
}
attr = oldattr;
}
 
/* used by selection: complement pointer position */
void complement_pos(int currcons, int offset)
{
static unsigned short *p = NULL;
static unsigned short old = 0;
static unsigned short oldx = 0, oldy = 0;
unsigned short new, oldattr;
 
oldattr = attr;
if (p) {
scr_writew(old, p);
if (currcons == fg_console) {
attr = old >> 8;
sw->con_putc(vc_cons[currcons].d, old & 0xff, oldy, oldx);
attr = oldattr;
}
}
if (offset == -1)
p = NULL;
else {
p = screenpos(currcons, offset, 1);
old = scr_readw(p);
oldx = (offset >> 1) % cols;
oldy = (offset >> 1) / cols;
if (can_do_color)
new = old ^ 0x7700;
else
new = old ^ 0x800;
scr_writew(new, p);
if (currcons == fg_console) {
attr = new >> 8;
sw->con_putc(vc_cons[currcons].d, new & 0xff, oldy, oldx);
attr = oldattr;
}
}
}
 
/* used by selection */
unsigned short screen_word(int currcons, int offset, int viewed)
{
return scr_readw(screenpos(currcons, offset, viewed));
}
 
/* used by selection - convert a screen word to a glyph number */
int scrw2glyph(unsigned short scr_word)
{
return ( video_mode_512ch )
? ((scr_word & 0x0800) >> 3) + (scr_word & 0x00ff)
: scr_word & 0x00ff;
}
 
/* used by vcs - note the word offset */
unsigned short *screen_pos(int currcons, int w_offset, int viewed)
{
return screenpos(currcons, 2 * w_offset, viewed);
}
 
void getconsxy(int currcons, char *p)
{
p[0] = x;
p[1] = y;
}
 
void putconsxy(int currcons, char *p)
{
gotoxy(currcons, p[0], p[1]);
set_cursor(currcons);
}
 
static void set_mode(int currcons, int on_off)
{
int i;
 
for (i=0; i<=npar; i++)
if (ques) switch(par[i]) { /* DEC private modes set/reset */
case 1: /* Cursor keys send ^[Ox/^[[x */
if (on_off)
set_kbd(decckm);
else
clr_kbd(decckm);
break;
case 3: /* 80/132 mode switch unimplemented */
deccolm = on_off;
#if 0
(void) vc_resize(rows, deccolm ? 132 : 80);
/* this alone does not suffice; some user mode
utility has to change the hardware regs */
#endif
break;
case 5: /* Inverted screen on/off */
if (decscnm != on_off) {
decscnm = on_off;
invert_screen(currcons, 0, screenbuf_size, 0);
update_attr(currcons);
}
break;
case 6: /* Origin relative/absolute */
decom = on_off;
gotoxy(currcons,0,0);
break;
case 7: /* Autowrap on/off */
decawm = on_off;
break;
case 8: /* Autorepeat on/off */
if (on_off)
set_kbd(decarm);
else
clr_kbd(decarm);
break;
case 9:
report_mouse = on_off ? 1 : 0;
break;
case 25: /* Cursor on/off */
deccm = on_off;
set_cursor(currcons);
break;
case 1000:
report_mouse = on_off ? 2 : 0;
break;
} else switch(par[i]) { /* ANSI modes set/reset */
case 3: /* Monitor (display ctrls) */
disp_ctrl = on_off;
break;
case 4: /* Insert Mode on/off */
decim = on_off;
break;
case 20: /* Lf, Enter == CrLf/Lf */
if (on_off)
set_kbd(lnm);
else
clr_kbd(lnm);
break;
}
}
 
static void setterm_command(int currcons)
{
switch(par[0]) {
case 1: /* set color for underline mode */
if (can_do_color && par[1] < 16) {
ulcolor = color_table[par[1]];
if (underline)
update_attr(currcons);
}
break;
case 2: /* set color for half intensity mode */
if (can_do_color && par[1] < 16) {
halfcolor = color_table[par[1]];
if (intensity == 0)
update_attr(currcons);
}
break;
case 8: /* store colors as defaults */
def_color = attr;
default_attr(currcons);
update_attr(currcons);
break;
case 9: /* set blanking interval */
blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
poke_blanked_console();
break;
case 10: /* set bell frequency in Hz */
if (npar >= 1)
bell_pitch = par[1];
else
bell_pitch = DEFAULT_BELL_PITCH;
break;
case 11: /* set bell duration in msec */
if (npar >= 1)
bell_duration = (par[1] < 2000) ?
par[1]*HZ/1000 : 0;
else
bell_duration = DEFAULT_BELL_DURATION;
break;
case 12: /* bring specified console to the front */
if (par[1] >= 1 && vc_cons_allocated(par[1]-1))
update_screen(par[1]-1);
break;
case 13: /* unblank the screen */
unblank_screen();
break;
}
}
 
static void insert_char(int currcons)
{
int i;
unsigned short *p = pos;
 
for (i = cols - x - 2; i >= 0; i--)
p[i + 1] = p[i];
*pos = video_erase_char;
need_wrap = 0;
 
if (currcons != fg_console)
return;
 
/* Arno:
* Move the remainder of the line (-1 character) one spot to the right
*/
sw->con_bmove(vc_cons[currcons].d,y,x,y,x+1,1,(cols-x-1));
/*
* Print the erase char on the current position
*/
sw->con_putc(vc_cons[currcons].d,(video_erase_char & 0x00ff),y,x);
}
 
static void csi_at(int currcons, unsigned int nr)
{
int i;
unsigned short *p;
 
if (nr > cols - x)
nr = cols - x;
else if (!nr)
nr = 1;
 
p = pos + cols - x - nr;
while (--p >= pos)
p[nr] = *p;
for (i = 0; i < nr; i++)
*++p = video_erase_char;
need_wrap = 0;
 
if (currcons != fg_console)
return;
 
sw->con_bmove (vc_cons[currcons].d, y, x, y, x + nr,
1, cols - x - nr);
while (nr--)
sw->con_putc (vc_cons[currcons].d, video_erase_char & 0x00ff,
y, x + nr);
}
 
static void csi_L(int currcons, unsigned int nr)
{
if (nr > rows)
nr = rows;
else if (!nr)
nr = 1;
scrdown (currcons, y, bottom, nr);
need_wrap = 0;
}
 
static void csi_P(int currcons, unsigned int nr)
{
int i;
unsigned short *p, *end;
 
if (nr > cols - x)
nr = cols - x;
else if (!nr)
nr = 1;
 
p = pos;
end = pos + cols - x - nr;
while (p < end)
*p = p[nr], p++;
for (i = 0; i < nr; i++)
*p++ = video_erase_char;
need_wrap = 0;
 
if (currcons != fg_console)
return;
 
sw->con_bmove (vc_cons[currcons].d, y, x + nr, y, x,
1, cols - x - nr);
 
while (nr--)
sw->con_putc (vc_cons[currcons].d, video_erase_char & 0x00ff,
y, cols - 1 - nr);
}
 
static void csi_M(int currcons, unsigned int nr)
{
if (nr > rows)
nr = rows;
else if (!nr)
nr=1;
scrup (currcons, y, bottom, nr);
need_wrap = 0;
}
 
static void save_cur(int currcons)
{
saved_x = x;
saved_y = y;
s_intensity = intensity;
s_underline = underline;
s_blink = blink;
s_reverse = reverse;
s_charset = charset;
s_color = color;
saved_G0 = G0_charset;
saved_G1 = G1_charset;
}
 
static void restore_cur(int currcons)
{
gotoxy(currcons,saved_x,saved_y);
intensity = s_intensity;
underline = s_underline;
blink = s_blink;
reverse = s_reverse;
charset = s_charset;
color = s_color;
G0_charset = saved_G0;
G1_charset = saved_G1;
translate = set_translate(charset ? G1_charset : G0_charset);
update_attr(currcons);
need_wrap = 0;
}
 
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
ESpalette };
 
static void reset_terminal(int currcons, int do_clear)
{
top = 0;
bottom = rows;
vc_state = ESnormal;
ques = 0;
translate = set_translate(LAT1_MAP);
G0_charset = LAT1_MAP;
G1_charset = GRAF_MAP;
charset = 0;
need_wrap = 0;
report_mouse = 0;
utf = 0;
utf_count = 0;
 
disp_ctrl = 0;
toggle_meta = 0;
 
decscnm = 0;
decom = 0;
decawm = 1;
deccm = 1;
decim = 0;
 
set_kbd(decarm);
clr_kbd(decckm);
clr_kbd(kbdapplic);
clr_kbd(lnm);
kbd_table[currcons].lockstate = 0;
kbd_table[currcons].slockstate = 0;
kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
set_leds();
 
default_attr(currcons);
update_attr(currcons);
 
tab_stop[0] = 0x01010100;
tab_stop[1] =
tab_stop[2] =
tab_stop[3] =
tab_stop[4] = 0x01010101;
 
bell_pitch = DEFAULT_BELL_PITCH;
bell_duration = DEFAULT_BELL_DURATION;
 
gotoxy(currcons,0,0);
save_cur(currcons);
if (do_clear)
csi_J(currcons,2);
}
 
/*
* Turn the Scroll-Lock LED on when the tty is stopped
*/
static void con_stop(struct tty_struct *tty)
{
int console_num;
if (!tty)
return;
console_num = MINOR(tty->device) - (tty->driver.minor_start);
if (!vc_cons_allocated(console_num))
return;
set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
set_leds();
}
 
/*
* Turn the Scroll-Lock LED off when the console is started
*/
static void con_start(struct tty_struct *tty)
{
int console_num;
if (!tty)
return;
console_num = MINOR(tty->device) - (tty->driver.minor_start);
if (!vc_cons_allocated(console_num))
return;
clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
set_leds();
}
 
static int con_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
int c, tc, ok, n = 0;
unsigned int currcons;
struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
 
currcons = vt->vc_num;
if (!vc_cons_allocated(currcons)) {
/* could this happen? */
static int error = 0;
if (!error) {
error = 1;
printk("con_write: tty %d not allocated\n", currcons+1);
}
return 0;
}
 
/* undraw cursor first */
if (currcons == fg_console)
hide_cursor(currcons);
/* clear the selection */
if (currcons == sel_cons)
clear_selection();
 
disable_bh(CONSOLE_BH);
while (count) {
enable_bh(CONSOLE_BH);
c = from_user ? get_user(buf) : *buf;
buf++; n++; count--;
disable_bh(CONSOLE_BH);
 
if (utf) {
/* Combine UTF-8 into Unicode */
/* Incomplete characters silently ignored */
if(c > 0x7f) {
if (utf_count > 0 && (c & 0xc0) == 0x80) {
utf_char = (utf_char << 6) | (c & 0x3f);
utf_count--;
if (utf_count == 0)
tc = c = utf_char;
else continue;
} else {
if ((c & 0xe0) == 0xc0) {
utf_count = 1;
utf_char = (c & 0x1f);
} else if ((c & 0xf0) == 0xe0) {
utf_count = 2;
utf_char = (c & 0x0f);
} else if ((c & 0xf8) == 0xf0) {
utf_count = 3;
utf_char = (c & 0x07);
} else if ((c & 0xfc) == 0xf8) {
utf_count = 4;
utf_char = (c & 0x03);
} else if ((c & 0xfe) == 0xfc) {
utf_count = 5;
utf_char = (c & 0x01);
} else
utf_count = 0;
continue;
}
} else {
tc = c;
utf_count = 0;
}
} else { /* no utf */
tc = translate[toggle_meta ? (c|0x80) : c];
}
 
/* If the original code was < 32 we only allow a
* glyph to be displayed if the code is not normally
* used (such as for cursor movement) or if the
* disp_ctrl mode has been explicitly enabled.
* Note: ESC is *never* allowed to be displayed as
* that would disable all escape sequences!
* To display font position 0x1B, go into UTF mode
* and display character U+F01B, or change the mapping.
*/
ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
: CTRL_ACTION) >> c) & 1))));
 
if (vc_state == ESnormal && ok) {
/* Now try to find out how to display it */
tc = conv_uni_to_pc(tc);
if ( tc == -4 )
{
/* If we got -4 (not found) then see if we have
defined a replacement character (U+FFFD) */
tc = conv_uni_to_pc(0xfffd);
}
else if ( tc == -3 )
{
/* Bad hash table -- hope for the best */
tc = c;
}
if (tc & ~console_charmask)
continue; /* Conversion failed */
 
if (need_wrap) {
cr(currcons);
lf(currcons);
}
#if 1 /* XXX */
/* DPC: 1994-04-12
* Speed up overstrike mode, using new putcs.
*
* P.S. I hate 8 spaces per tab! Use Emacs!
*/
/* Only use this for the foreground console,
where we really draw the chars */
 
if (count > 2 &&
!decim && !utf && currcons == fg_console) {
static char putcs_buf[256];
char *p = putcs_buf;
int putcs_count = 1;
ushort nextx = x + 1;
 
*p++ = tc;
*pos++ = tc | (attr << 8);
if (nextx == cols) {
sw->con_putc(vc_cons[currcons].d,
*putcs_buf, y, x);
pos--;
need_wrap = decawm;
continue;
}
/* TAB TAB TAB - Arghh!!!! */
while (count)
{
enable_bh(CONSOLE_BH);
c = from_user ? get_user(buf) : *buf;
disable_bh(CONSOLE_BH);
tc = translate[toggle_meta ? (c|0x80) : c];
if (!tc ||
!(c >= 32
|| !(((disp_ctrl ? CTRL_ALWAYS
: CTRL_ACTION) >> c) & 1)))
break;
tc = conv_uni_to_pc(tc);
if (tc == -4)
tc = conv_uni_to_pc(0xfffd);
else if (tc == -3)
tc = c;
 
buf++; n++; count--;
if (tc & ~console_charmask)
continue; /* Conversion failed */
 
*p++ = tc;
*pos++ = tc | (attr << 8);
++putcs_count;
++nextx;
if (nextx == cols ||
putcs_count == sizeof (putcs_buf))
break;
}
sw->con_putcs(vc_cons[currcons].d,
putcs_buf, putcs_count, y, x);
if (nextx == cols) {
pos--;
x = cols-1;
need_wrap = decawm;
} else
x += putcs_count;
continue;
}
/* DPC: End of putcs support */
#endif
if (decim)
insert_char(currcons);
*pos = (attr << 8) + tc;
if (currcons == fg_console)
sw->con_putc(vc_cons[currcons].d,tc,y,x);
if (x == cols - 1)
need_wrap = decawm;
else {
pos++;
x++;
}
continue;
}
 
/*
* Control characters can be used in the _middle_
* of an escape sequence.
*/
switch (c) {
case 7:
if (bell_duration)
kd_mksound(bell_pitch, bell_duration);
continue;
case 8:
bs(currcons);
continue;
case 9:
pos -= x;
while (x < cols - 1) {
x++;
if (tab_stop[x >> 5] & (1 << (x & 31)))
break;
}
pos += x;
continue;
case 10: case 11: case 12:
lf(currcons);
if (!is_kbd(lnm))
continue;
case 13:
cr(currcons);
continue;
case 14:
charset = 1;
translate = set_translate(G1_charset);
disp_ctrl = 1;
continue;
case 15:
charset = 0;
translate = set_translate(G0_charset);
disp_ctrl = 0;
continue;
case 24: case 26:
vc_state = ESnormal;
continue;
case 27:
vc_state = ESesc;
continue;
case 127:
del(currcons);
continue;
case 128+27:
vc_state = ESsquare;
continue;
}
switch(vc_state) {
case ESesc:
vc_state = ESnormal;
switch (c) {
case '[':
vc_state = ESsquare;
continue;
case ']':
vc_state = ESnonstd;
continue;
case '%':
vc_state = ESpercent;
continue;
case 'E':
cr(currcons);
lf(currcons);
continue;
case 'M':
ri(currcons);
continue;
case 'D':
lf(currcons);
continue;
case 'H':
tab_stop[x >> 5] |= (1 << (x & 31));
continue;
case 'Z':
respond_ID(tty);
continue;
case '7':
save_cur(currcons);
continue;
case '8':
restore_cur(currcons);
continue;
case '(':
vc_state = ESsetG0;
continue;
case ')':
vc_state = ESsetG1;
continue;
case '#':
vc_state = EShash;
continue;
case 'c':
reset_terminal(currcons,1);
continue;
case '>': /* Numeric keypad */
clr_kbd(kbdapplic);
continue;
case '=': /* Appl. keypad */
set_kbd(kbdapplic);
continue;
}
continue;
case ESnonstd:
if (c=='P') { /* palette escape sequence */
for (npar=0; npar<NPAR; npar++)
par[npar] = 0 ;
npar = 0 ;
vc_state = ESpalette;
continue;
} else if (c=='R') { /* reset palette */
#if 0
reset_palette (currcons);
#endif
vc_state = ESnormal;
} else
vc_state = ESnormal;
continue;
case ESpalette:
if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
if (npar==7) {
#if 0
int i = par[0]*3, j = 1;
palette[i] = 16*par[j++];
palette[i++] += par[j++];
palette[i] = 16*par[j++];
palette[i++] += par[j++];
palette[i] = 16*par[j++];
palette[i] += par[j];
set_palette() ;
#endif
vc_state = ESnormal;
}
} else
vc_state = ESnormal;
continue;
case ESsquare:
for(npar = 0 ; npar < NPAR ; npar++)
par[npar] = 0;
npar = 0;
vc_state = ESgetpars;
if (c == '[') { /* Function key */
vc_state=ESfunckey;
continue;
}
ques = (c=='?');
if (ques)
continue;
case ESgetpars:
if (c==';' && npar<NPAR-1) {
npar++;
continue;
} else if (c>='0' && c<='9') {
par[npar] *= 10;
par[npar] += c-'0';
continue;
} else vc_state=ESgotpars;
case ESgotpars:
vc_state = ESnormal;
switch(c) {
case 'h':
set_mode(currcons,1);
continue;
case 'l':
set_mode(currcons,0);
continue;
case 'n':
if (!ques)
if (par[0] == 5)
status_report(tty);
else if (par[0] == 6)
cursor_report(currcons,tty);
continue;
}
if (ques) {
ques = 0;
continue;
}
switch(c) {
case 'G': case '`':
if (par[0]) par[0]--;
gotoxy(currcons,par[0],y);
continue;
case 'A':
if (!par[0]) par[0]++;
gotoxy(currcons,x,y-par[0]);
continue;
case 'B': case 'e':
if (!par[0]) par[0]++;
gotoxy(currcons,x,y+par[0]);
continue;
case 'C': case 'a':
if (!par[0]) par[0]++;
gotoxy(currcons,x+par[0],y);
continue;
case 'D':
if (!par[0]) par[0]++;
gotoxy(currcons,x-par[0],y);
continue;
case 'E':
if (!par[0]) par[0]++;
gotoxy(currcons,0,y+par[0]);
continue;
case 'F':
if (!par[0]) par[0]++;
gotoxy(currcons,0,y-par[0]);
continue;
case 'd':
if (par[0]) par[0]--;
gotoxy(currcons,x,par[0]);
continue;
case 'H': case 'f':
if (par[0]) par[0]--;
if (par[1]) par[1]--;
gotoxy(currcons,par[1],par[0]);
continue;
case 'J':
csi_J(currcons,par[0]);
continue;
case 'K':
csi_K(currcons,par[0]);
continue;
case 'L':
csi_L(currcons,par[0]);
continue;
case 'M':
csi_M(currcons,par[0]);
continue;
case 'P':
csi_P(currcons,par[0]);
continue;
case 'c':
if (!par[0])
respond_ID(tty);
continue;
case 'g':
if (!par[0])
tab_stop[x >> 5] &= ~(1 << (x & 31));
else if (par[0] == 3) {
tab_stop[0] =
tab_stop[1] =
tab_stop[2] =
tab_stop[3] =
tab_stop[4] = 0;
}
continue;
case 'm':
csi_m(currcons);
continue;
case 'q': /* DECLL - but only 3 leds */
/* map 0,1,2,3 to 0,1,2,4 */
if (par[0] < 4)
setledstate(kbd_table + currcons,
(par[0] < 3) ? par[0] : 4);
continue;
case 'r':
if (!par[0])
par[0]++;
if (!par[1])
par[1] = rows;
/* Minimum allowed region is 2 lines */
if (par[0] < par[1] &&
par[1] <= rows) {
top=par[0]-1;
bottom=par[1];
gotoxy(currcons,0,0);
}
continue;
case 's':
save_cur(currcons);
continue;
case 'u':
restore_cur(currcons);
continue;
case 'X':
csi_X(currcons, par[0]);
continue;
case '@':
csi_at(currcons,par[0]);
continue;
case ']': /* setterm functions */
setterm_command(currcons);
continue;
}
continue;
case ESpercent:
vc_state = ESnormal;
switch (c) {
case '@': /* defined in ISO 2022 */
utf = 0;
continue;
case 'G': /* prelim official escape code */
case '8': /* retained for compatibility */
utf = 1;
continue;
}
continue;
case ESfunckey:
vc_state = ESnormal;
continue;
case EShash:
vc_state = ESnormal;
if (c == '8') {
/* DEC screen alignment test. kludge :-) */
video_erase_char =
(video_erase_char & 0xff00) | 'E';
/* Arno:
* Doesn't work, because csi_J(c,2)
* calls con_clear and doesn't print
* the erase char..
*/
csi_J(currcons, 2);
video_erase_char =
(video_erase_char & 0xff00) | ' ';
}
continue;
case ESsetG0:
if (c == '0')
G0_charset = GRAF_MAP;
else if (c == 'B')
G0_charset = LAT1_MAP;
else if (c == 'U')
G0_charset = IBMPC_MAP;
else if (c == 'K')
G0_charset = USER_MAP;
if (charset == 0)
translate = set_translate(G0_charset);
vc_state = ESnormal;
continue;
case ESsetG1:
if (c == '0')
G1_charset = GRAF_MAP;
else if (c == 'B')
G1_charset = LAT1_MAP;
else if (c == 'U')
G1_charset = IBMPC_MAP;
else if (c == 'K')
G1_charset = USER_MAP;
if (charset == 1)
translate = set_translate(G1_charset);
vc_state = ESnormal;
continue;
default:
vc_state = ESnormal;
}
}
if (vcmode != KD_GRAPHICS)
set_cursor(currcons);
enable_bh(CONSOLE_BH);
return n;
}
 
static int con_write_room(struct tty_struct *tty)
{
if (tty->stopped)
return 0;
return 4096; /* No limit, really; we're not buffering */
}
 
static int con_chars_in_buffer(struct tty_struct *tty)
{
return 0; /* we're not buffering */
}
 
void poke_blanked_console(void)
{
timer_active &= ~(1<<BLANK_TIMER);
if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
return;
if (console_blanked) {
timer_table[BLANK_TIMER].fn = unblank_screen;
timer_table[BLANK_TIMER].expires = 0;
timer_active |= 1<<BLANK_TIMER;
} else if (blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
timer_active |= 1<<BLANK_TIMER;
}
}
 
/* DPC: New version of console_print using putcs */
 
void console_print(const char * b)
{
int currcons = fg_console;
unsigned char c;
const char *start = b;
ushort count = 0;
ushort myx = x;
static int printing = 0;
 
if (!printable || printing)
return; /* console not yet initialized */
printing = 1;
 
if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
currcons = kmsg_redirect - 1;
 
if (!vc_cons_allocated(currcons)) {
/* impossible */
printk("console_print: tty %d not allocated ??\n", currcons+1);
printing = 0;
return;
}
 
/* undraw cursor first */
hide_cursor(currcons);
 
/* Contrived structure to try to emulate original need_wrap behaviour
* Problems caused when we have need_wrap set on '\n' character */
while ((c = *(b++)) != 0) {
if (c == 10 || c == 13 || c == 8 || need_wrap) {
if ((count = b - start - 1) > 0) {
sw->con_putcs(vc_cons[currcons].d, start, count ,
y, x);
x += count;
if (need_wrap)
x--;
}
 
if (c == 8) { /* backspace */
bs(currcons);
start = b;
myx = x;
continue;
}
if (c != 13)
lf(currcons);
cr(currcons);
 
if (c == 10 || c == 13) {
start = b; myx = x; continue;
}
 
start = b-1; myx = x;
}
 
*pos = c | (attr << 8);
if (myx == cols - 1) {
need_wrap = 1;
continue;
}
pos++;
myx++;
}
 
if ((count = b - start -1) > 0) {
sw->con_putcs(vc_cons[currcons].d, start, count ,
y, x);
x += count;
if (x == cols)
{
x--;
need_wrap = 1;
}
}
set_cursor(currcons);
poke_blanked_console();
printing = 0;
}
 
 
/*
* con_throttle and con_unthrottle are only used for
* paste_selection(), which has to stuff in a large number of
* characters...
*/
static void con_throttle(struct tty_struct *tty)
{
}
 
static void con_unthrottle(struct tty_struct *tty)
{
struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
 
wake_up_interruptible(&vt->paste_wait);
}
 
static void vc_init(unsigned int currcons, int do_clear)
{
long base = (long) vc_scrbuf[currcons];
 
pos = (unsigned short *)(origin = (ulong)video_mem_start = base);
scr_end = base + screenbuf_size;
video_mem_end = base + screenbuf_size;
reset_vc(currcons);
def_color = 0x07; /* white */
ulcolor = 0x0f; /* bold white */
halfcolor = 0x08; /* grey */
vt_cons[currcons]->paste_wait = 0;
reset_terminal(currcons, do_clear);
}
 
/*
* This is the console switching bottom half handler.
*
* Doing console switching in a bottom half handler allows
* us to do the switches asynchronously (needed when we want
* to switch due to a keyboard interrupt), while still giving
* us the option to easily disable it to avoid races when we
* need to write to the console.
*/
static void console_bh(void)
{
if (want_console >= 0) {
if (want_console != fg_console) {
change_console(want_console);
/* we only changed when the console had already
been allocated - a new console is not created
in an interrupt routine */
}
want_console = -1;
}
if (do_poke_blanked_console) { /* do not unblank for a LED change */
do_poke_blanked_console = 0;
poke_blanked_console();
}
}
 
/*
* unsigned long con_init(unsigned long);
*
* This routine initializes console interrupts, and does nothing
* else. If you want the screen to clear, call tty_write with
* the appropriate escape-sequence.
*
* Reads the information preserved by setup.s to determine the current display
* type and sets everything accordingly.
*/
unsigned long con_init(unsigned long kmem_start)
{
char *display_desc = "????";
unsigned int currcons = 0;
extern int serial_debug;
 
memset(&console_driver, 0, sizeof(struct tty_driver));
console_driver.magic = TTY_DRIVER_MAGIC;
console_driver.name = "tty";
console_driver.name_base = 1;
console_driver.major = TTY_MAJOR;
console_driver.minor_start = 1;
console_driver.num = MAX_NR_CONSOLES;
console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
console_driver.init_termios = tty_std_termios;
console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
console_driver.refcount = &console_refcount;
console_driver.table = console_table;
console_driver.termios = console_termios;
console_driver.termios_locked = console_termios_locked;
 
console_driver.open = con_open;
console_driver.write = con_write;
console_driver.write_room = con_write_room;
console_driver.chars_in_buffer = con_chars_in_buffer;
console_driver.ioctl = vt_ioctl;
console_driver.stop = con_stop;
console_driver.start = con_start;
console_driver.throttle = con_throttle;
console_driver.unthrottle = con_unthrottle;
if (tty_register_driver(&console_driver))
panic("Couldn't register console driver\n");
kmem_start = conswitchp->con_startup (kmem_start, &display_desc);
 
timer_table[BLANK_TIMER].fn = blank_screen;
timer_table[BLANK_TIMER].expires = 0;
if (blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
timer_active |= 1<<BLANK_TIMER;
}
 
/* Due to kmalloc roundup allocating statically is more efficient -
so provide MIN_NR_CONSOLES for people with very little memory */
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
vc_cons[currcons].d = (struct vc_data *) kmem_start;
kmem_start += sizeof(struct vc_data);
vt_cons[currcons] = (struct vt_struct *) kmem_start;
kmem_start += sizeof(struct vt_struct);
 
/* ++Geert: sw->con_init determines console size */
sw = conswitchp;
cons_num = currcons;
sw->con_init (vc_cons[currcons].d);
size_row = cols<<1;
screenbuf_size = rows*size_row;
 
vc_scrbuf[currcons] = (unsigned short *) kmem_start;
kmem_start += screenbuf_size;
kmalloced = 0;
vc_init(currcons, currcons);
}
 
currcons = fg_console = 0;
 
gotoxy(currcons,0,0);
csi_J(currcons, 0);
printable = 1;
update_screen(fg_console);
sw->con_cursor(vc_cons[currcons].d, CM_DRAW);
printable = 1;
 
/* If "serdebug" cmd line option was present, don't register for printk */
if (!serial_debug)
register_console(console_print);
printk("Console: %s %s %ldx%ld, %d virtual console%s (max %d)\n",
can_do_color ? "colour":"mono",
display_desc,
cols,rows,
MIN_NR_CONSOLES, (MIN_NR_CONSOLES == 1) ? "" : "s", MAX_NR_CONSOLES);
 
init_bh(CONSOLE_BH, console_bh);
return kmem_start;
}
 
void do_blank_screen(int nopowersave)
{
int currcons;
 
if (console_blanked)
return;
 
if (!vc_cons_allocated(fg_console)) {
/* impossible */
printk("blank_screen: tty %d not allocated ??\n", fg_console+1);
return;
}
 
/* don't blank graphics */
if (vt_cons[fg_console]->vc_mode == KD_TEXT) {
timer_active &= ~(1<<BLANK_TIMER);
timer_table[BLANK_TIMER].fn = unblank_screen;
 
/* try not to lose information by blanking,
and not to waste memory */
currcons = fg_console;
has_scrolled = 0;
sw->con_blank (1);
}
else
hide_cursor(fg_console);
console_blanked = fg_console + 1;
}
 
void do_unblank_screen(void)
{
int currcons;
 
if (!console_blanked)
return;
if (!vc_cons_allocated(fg_console)) {
/* impossible */
printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
return;
}
timer_table[BLANK_TIMER].fn = blank_screen;
if (blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
timer_active |= 1<<BLANK_TIMER;
}
 
currcons = fg_console;
console_blanked = 0;
if (sw->con_blank (0))
/* Low-level driver cannot restore -> do it ourselves */
update_screen( fg_console );
set_cursor (fg_console);
}
 
void update_screen(int new_console)
{
int currcons = fg_console;
int xx, yy, startx, attr_save;
char buf[256], *bufp;
unsigned short *p;
static int lock = 0;
 
if (/* new_console == fg_console || */ lock)
return;
if (!vc_cons_allocated(new_console)) {
/* strange ... */
printk("update_screen: tty %d not allocated ??\n", new_console+1);
return;
}
lock = 1;
 
clear_selection();
 
currcons = fg_console = new_console;
sw->con_cursor (vc_cons[currcons].d, CM_ERASE);
sw->con_switch (vc_cons[new_console].d);
/* Update the screen contents */
p = video_mem_start;
attr_save = attr;
for (yy = 0; yy < rows; yy++)
{
bufp = buf;
for (startx = xx = 0; xx < cols; xx++)
{
if (attr != ((*p >> 8) & 0xff))
{
if (bufp > buf)
sw->con_putcs (vc_cons[currcons].d, buf, bufp - buf,
yy, startx);
startx = xx;
bufp = buf;
attr = (*p >> 8) & 0xff;
}
*bufp++ = *p++;
if (bufp == buf + sizeof (buf))
{
sw->con_putcs (vc_cons[currcons].d, buf, bufp - buf,
yy, startx);
startx = xx + 1;
bufp = buf;
}
}
if (bufp > buf)
sw->con_putcs (vc_cons[currcons].d, buf, bufp - buf,
yy, startx);
}
set_cursor (currcons);
attr = attr_save;
set_leds();
compute_shiftstate();
lock = 0;
}
 
/*
* If a blank_screen is due to a timer, then a power save is allowed.
* If it is related to console_switching, then avoid vesa_blank().
*/
static void blank_screen(void)
{
do_blank_screen(0);
}
 
static void unblank_screen(void)
{
do_unblank_screen();
}
 
/*
* Allocate the console screen memory.
*/
int con_open(struct tty_struct *tty, struct file * filp)
{
unsigned int currcons;
int i;
 
currcons = MINOR(tty->device) - tty->driver.minor_start;
i = vc_allocate(currcons);
if (i)
return i;
 
vt_cons[currcons]->vc_num = currcons;
tty->driver_data = vt_cons[currcons];
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = rows;
tty->winsize.ws_col = cols;
}
 
return 0;
}
 
/*
* PIO_FONT support.
*
* The font loading code goes back to the codepage package by
* Joel Hoffman (joel@wam.umd.edu). (He reports that the original
* reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
* Video Systems_ by Richard Wilton. 1987. Microsoft Press".)
*
* Change for certain monochrome monitors by Yury Shevchuck
* (sizif@botik.yaroslavl.su).
*/
 
#define colourmap ((char *)0xa0000)
/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
should use 0xA0000 for the bwmap as well.. */
#define blackwmap ((char *)0xa0000)
#define cmapsz 8192
#define seq_port_reg (0x3c4)
#define seq_port_val (0x3c5)
#define gr_port_reg (0x3ce)
#define gr_port_val (0x3cf)
 
static int set_get_font(char * arg, int set)
{
#ifdef CAN_LOAD_EGA_FONTS
int i;
char *charmap;
int beg;
 
/* no use to "load" CGA... */
 
if (video_type == VIDEO_TYPE_EGAC) {
charmap = colourmap;
beg = 0x0e;
} else if (video_type == VIDEO_TYPE_EGAM) {
charmap = blackwmap;
beg = 0x0a;
} else
return -EINVAL;
 
i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, cmapsz);
if (i)
return i;
 
cli();
outb_p( 0x00, seq_port_reg ); /* First, the sequencer */
outb_p( 0x01, seq_port_val ); /* Synchronous reset */
outb_p( 0x02, seq_port_reg );
outb_p( 0x04, seq_port_val ); /* CPU writes only to map 2 */
outb_p( 0x04, seq_port_reg );
outb_p( 0x07, seq_port_val ); /* Sequential addressing */
outb_p( 0x00, seq_port_reg );
outb_p( 0x03, seq_port_val ); /* Clear synchronous reset */
 
outb_p( 0x04, gr_port_reg ); /* Now, the graphics controller */
outb_p( 0x02, gr_port_val ); /* select map 2 */
outb_p( 0x05, gr_port_reg );
outb_p( 0x00, gr_port_val ); /* disable odd-even addressing */
outb_p( 0x06, gr_port_reg );
outb_p( 0x00, gr_port_val ); /* map start at A000:0000 */
sti();
 
if (set)
memcpy_fromfs (charmap, arg, cmapsz);
else
memcpy_tofs (arg, charmap, cmapsz);
 
cli();
outb_p( 0x00, seq_port_reg ); /* First, the sequencer */
outb_p( 0x01, seq_port_val ); /* Synchronous reset */
outb_p( 0x02, seq_port_reg );
outb_p( 0x03, seq_port_val ); /* CPU writes to maps 0 and 1 */
outb_p( 0x04, seq_port_reg );
outb_p( 0x03, seq_port_val ); /* odd-even addressing */
outb_p( 0x00, seq_port_reg );
outb_p( 0x03, seq_port_val ); /* clear synchronous reset */
 
outb_p( 0x04, gr_port_reg ); /* Now, the graphics controller */
outb_p( 0x00, gr_port_val ); /* select map 0 for CPU */
outb_p( 0x05, gr_port_reg );
outb_p( 0x10, gr_port_val ); /* enable even-odd addressing */
outb_p( 0x06, gr_port_reg );
outb_p( beg, gr_port_val ); /* map starts at b800:0 or b000:0 */
sti();
 
return 0;
#else
return -EINVAL;
#endif
}
 
/*
* Load palette into the EGA/VGA DAC registers. arg points to a colour
* map, 3 bytes per colour, 16 colours, range from 0 to 255.
*/
 
int con_set_cmap (unsigned char *arg)
{
return -EINVAL;
}
 
int con_get_cmap (unsigned char *arg)
{
return -EINVAL;
}
 
void reset_palette(int currcons)
{
}
 
void set_palette(void)
{
}
 
/*
* Load font into the EGA/VGA character generator. arg points to a 8192
* byte map, 32 bytes per character. Only first H of them are used for
* 8xH fonts (0 < H <= 32).
*/
 
int con_set_font (char *arg)
{
hashtable_contents_valid = 0;
return set_get_font (arg,1);
}
 
int con_get_font (char *arg)
{
return set_get_font (arg,0);
}
 
/*
* Adjust the screen to fit a font of a certain height
*
* Returns < 0 for error, 0 if nothing changed, and the number
* of lines on the adjusted console if changed.
*/
int con_adjust_height(unsigned long fontheight)
{
return -EINVAL;
}
 
void set_vesa_blanking(int arg)
{
}
 
unsigned long get_video_num_lines(unsigned int currcons)
{
return(rows);
}
 
unsigned long get_video_num_columns(unsigned int currcons)
{
return(cols);
}
 
unsigned long get_video_size_row(unsigned int currcons)
{
return(size_row);
}
/sys_m68k.c
0,0 → 1,505
/*
* linux/arch/m68k/kernel/sys_m68k.c
*
* This file contains various random system calls that
* have a non-standard calling sequence on the Linux/m68k
* 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>
#include <asm/cachectl.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_user(fd[0],0+fildes);
put_user(fd[1],1+fildes);
return 0;
}
 
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux/m68k cloned Linux/i386, which 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 = get_user((void **)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_user (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;
}
 
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
{
return -ENOSYS;
}
 
/* Convert virtual address VADDR to physical address PADDR, recording
in VALID whether the virtual address is actually mapped. */
#define virt_to_phys_040(vaddr, paddr, valid) \
{ \
register unsigned long _tmp1 __asm__ ("a0") = (vaddr); \
register unsigned long _tmp2 __asm__ ("d0"); \
unsigned long _mmusr; \
\
__asm__ __volatile__ (".word 0xf568 /* ptestr (%1) */\n\t" \
".long 0x4e7a0805 /* movec %%mmusr,%0 */" \
: "=d" (_tmp2) \
: "a" (_tmp1)); \
_mmusr = _tmp2; \
if (0 /* XXX _mmusr & MMU_?_040 */) \
(valid) = 0; \
else \
{ \
(valid) = 1; \
(paddr) = _mmusr & ~0xfff; \
} \
}
 
static inline int
cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len)
{
unsigned long paddr;
int valid;
 
switch (scope)
{
case FLUSH_SCOPE_ALL:
switch (cache)
{
case FLUSH_CACHE_DATA:
/* This nop is needed for some broken versions of the 68040. */
__asm__ __volatile__ ("nop\n\t"
".word 0xf478 /* cpusha %%dc */");
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4b8 /* cpusha %%ic */");
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4f8 /* cpusha %%bc */");
break;
}
break;
 
case FLUSH_SCOPE_LINE:
len >>= 4;
for (;;)
{
virt_to_phys_040 (addr, paddr, valid);
if (valid)
break;
if (len <= PAGE_SIZE / 16)
return 0;
len -= PAGE_SIZE / 16;
addr += PAGE_SIZE;
}
while (len--)
{
register unsigned long tmp __asm__ ("a0") = paddr;
switch (cache)
{
case FLUSH_CACHE_DATA:
__asm__ __volatile__ ("nop\n\t"
".word 0xf468 /* cpushl %%dc,(%0) */"
: : "a" (tmp));
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4a8 /* cpushl %%ic,(%0) */"
: : "a" (tmp));
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4e8 /* cpushl %%bc,(%0) */"
: : "a" (paddr));
break;
}
addr += 16;
if (len)
{
if ((addr & (PAGE_SIZE-1)) < 16)
{
/* Recompute physical address when crossing a page
boundary. */
for (;;)
{
virt_to_phys_040 (addr, paddr, valid);
if (valid)
break;
if (len <= PAGE_SIZE / 16)
return 0;
len -= PAGE_SIZE / 16;
addr += PAGE_SIZE;
}
}
else
paddr += 16;
}
}
break;
 
default:
case FLUSH_SCOPE_PAGE:
for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
{
register unsigned long tmp __asm__ ("a0");
virt_to_phys_040 (addr, paddr, valid);
if (!valid)
continue;
tmp = paddr;
switch (cache)
{
case FLUSH_CACHE_DATA:
__asm__ __volatile__ ("nop\n\t"
".word 0xf470 /* cpushp %%dc,(%0) */"
: : "a" (tmp));
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4b0 /* cpushp %%ic,(%0) */"
: : "a" (tmp));
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ ("nop\n\t"
".word 0xf4f0 /* cpushp %%bc,(%0) */"
: : "a" (tmp));
break;
}
}
break;
}
return 0;
}
 
#define virt_to_phys_060(vaddr, paddr, valid) \
{ \
register unsigned long _tmp __asm__ ("a0") = (vaddr); \
\
__asm__ __volatile__ (".word 0xf5c8 /* plpar (%1) */" \
: "=a" (_tmp) \
: "0" (_tmp)); \
(valid) = 1; /* XXX */ \
(paddr) = _tmp; \
}
 
static inline int
cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len)
{
unsigned long paddr;
int valid;
 
switch (scope)
{
case FLUSH_SCOPE_ALL:
switch (cache)
{
case FLUSH_CACHE_DATA:
__asm__ __volatile__ (".word 0xf478 /* cpusha %%dc */\n\t"
".word 0xf458 /* cinva %%dc */");
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ (".word 0xf4b8 /* cpusha %%ic */\n\t"
".word 0xf498 /* cinva %%ic */");
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ (".word 0xf4f8 /* cpusha %%bc */\n\t"
".word 0xf4d8 /* cinva %%bc */");
break;
}
break;
 
case FLUSH_SCOPE_LINE:
len >>= 4;
for (;;)
{
virt_to_phys_060 (addr, paddr, valid);
if (valid)
break;
if (len <= PAGE_SIZE / 16)
return 0;
len -= PAGE_SIZE / 16;
addr += PAGE_SIZE;
}
while (len--)
{
register unsigned long tmp __asm__ ("a0") = paddr;
switch (cache)
{
case FLUSH_CACHE_DATA:
__asm__ __volatile__ (".word 0xf468 /* cpushl %%dc,(%0) */\n\t"
".word 0xf448 /* cinv %%dc,(%0) */"
: : "a" (tmp));
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ (".word 0xf4a8 /* cpushl %%ic,(%0) */\n\t"
".word 0xf488 /* cinv %%ic,(%0) */"
: : "a" (tmp));
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ (".word 0xf4e8 /* cpushl %%bc,(%0) */\n\t"
".word 0xf4c8 /* cinv %%bc,(%0) */"
: : "a" (paddr));
break;
}
addr += 16;
if (len)
{
if ((addr & (PAGE_SIZE-1)) < 16)
{
/* Recompute the physical address when crossing a
page boundary. */
for (;;)
{
virt_to_phys_060 (addr, paddr, valid);
if (valid)
break;
if (len <= PAGE_SIZE / 16)
return 0;
len -= PAGE_SIZE / 16;
addr += PAGE_SIZE;
}
}
else
paddr += 16;
}
}
break;
 
default:
case FLUSH_SCOPE_PAGE:
for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
{
register unsigned long tmp __asm__ ("a0");
virt_to_phys_060 (addr, paddr, valid);
if (!valid)
continue;
tmp = paddr;
switch (cache)
{
case FLUSH_CACHE_DATA:
__asm__ __volatile__ (".word 0xf470 /* cpushp %%dc,(%0) */\n\t"
".word 0xf450 /* cinv %%dc,(%0) */"
: : "a" (tmp));
break;
case FLUSH_CACHE_INSN:
__asm__ __volatile__ (".word 0xf4b0 /* cpushp %%ic,(%0) */\n\t"
".word 0xf490 /* cinv %%ic,(%0) */"
: : "a" (tmp));
break;
default:
case FLUSH_CACHE_BOTH:
__asm__ __volatile__ (".word 0xf4f0 /* cpushp %%bc,(%0) */\n\t"
".word 0xf4d0 /* cinv %%bc,(%0) */"
: : "a" (tmp));
break;
}
}
break;
}
return 0;
}
 
/* sys_cacheflush -- flush (part of) the processor cache. */
asmlinkage int
sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
{
struct vm_area_struct *vma;
 
if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL
|| cache & ~FLUSH_CACHE_BOTH)
return -EINVAL;
 
if (scope == FLUSH_SCOPE_ALL)
{
/* Only the superuser may flush the whole cache. */
if (!suser ())
return -EPERM;
}
else
{
/* Verify that the specified address region actually belongs to
this process. */
vma = find_vma (current, addr);
if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
return -EINVAL;
}
 
switch (m68k_is040or060)
{
default: /* 030 */
/* Always flush the whole cache, everything else would not be
worth the hassle. */
__asm__ __volatile__
("movec %%cacr, %%d0\n\t"
"or %0, %%d0\n\t"
"movec %%d0, %%cacr"
: /* no outputs */
: "di" ((cache & FLUSH_CACHE_INSN ? 8 : 0)
| (cache & FLUSH_CACHE_DATA ? 0x800 : 0))
: "d0");
return 0;
 
case 4: /* 040 */
return cache_flush_040 (addr, scope, cache, len);
 
case 6: /* 060 */
return cache_flush_060 (addr, scope, cache, len);
}
}
/ksyms.c
0,0 → 1,79
#include <linux/config.h>
#include <linux/module.h>
#include <linux/linkage.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/user.h>
#include <linux/elfcore.h>
 
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
 
asmlinkage long long __ashrdi3 (long long, int);
extern char m68k_debug_device[];
 
#ifdef CONFIG_ATARI
extern void mach_atari_syms_export (void);
#endif
#ifdef CONFIG_AMIGA
extern void mach_amiga_syms_export (void);
#endif
#ifdef CONFIG_MAC
extern void mach_mac_syms_export (void);
#endif
 
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(memcmp),
X(boot_info),
X(m68k_is040or060),
X(cache_push),
X(cache_clear),
X(mm_vtop),
X(mm_ptov),
X(m68k_debug_device),
X(add_isr),
X(remove_isr),
X(dump_fpu),
X(dump_thread),
 
/* The following are special because they're not called
explicitly (the C compiler generates them). Fortunately,
their interface isn't gonna change any time soon now, so
it's OK to leave it out of version control. */
XNOVERS(__ashrdi3),
XNOVERS(memcpy),
#include <linux/symtab_end.h>
};
 
void arch_syms_export(void)
{
register_symtab(&arch_symbol_table);
 
switch (boot_info.machtype) {
#ifdef CONFIG_ATARI
case MACH_ATARI:
mach_atari_syms_export();
break;
#endif
#ifdef CONFIG_AMIGA
case MACH_AMIGA:
mach_amiga_syms_export();
break;
#endif
#ifdef CONFIG_MAC
case MACH_MAC:
mach_mac_syms_export();
break;
#endif
default:
break;
}
}
/traps.c
0,0 → 1,901
/*
* linux/arch/m68k/kernel/traps.c
*
* Copyright (C) 1993, 1994 by Hamish Macdonald
*
* 68040 fixes by Michael Rausch
* 68040 fixes by Martin Apel
* 68060 fixes by Roman Hodek
* 68060 fixes by Jesper Skov
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
 
/*
* Sets up all exception vectors
*/
 
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/a.out.h>
#include <linux/user.h>
#include <linux/string.h>
#include <linux/linkage.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/traps.h>
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
#include <asm/machdep.h>
 
/* assembler routines */
asmlinkage void system_call(void);
asmlinkage void buserr(void);
asmlinkage void trap(void);
asmlinkage void inthandler(void);
asmlinkage void nmihandler(void);
 
e_vector vectors[256] = {
0, 0, buserr, trap, trap, trap, trap, trap,
trap, trap, trap, trap, trap, trap, trap, trap,
trap, trap, trap, trap, trap, trap, trap, trap,
inthandler, inthandler, inthandler, inthandler,
inthandler, inthandler, inthandler, inthandler,
/* TRAP #0-15 */
system_call, trap, trap, trap, trap, trap, trap, trap,
trap, trap, trap, trap, trap, trap, trap, trap,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
 
/* nmi handler for the Amiga */
asm(".text\n"
__ALIGN_STR "\n"
SYMBOL_NAME_STR(nmihandler) ": rte");
 
void trap_init (void)
{
int i;
 
/* setup the exception vector table */
__asm__ volatile ("movec %0,%/vbr" : : "r" ((void*)vectors));
 
for (i = 48; i < 64; i++)
vectors[i] = trap;
 
for (i = 64; i < 256; i++)
vectors[i] = inthandler;
 
/* if running on an amiga, make the NMI interrupt do nothing */
if (MACH_IS_AMIGA) {
vectors[VEC_INT7] = nmihandler;
}
 
#ifdef CONFIG_FPSP_040
if (m68k_is040or060 == 4) {
/* set up FPSP entry points */
asmlinkage void dz_vec(void) asm ("dz");
asmlinkage void inex_vec(void) asm ("inex");
asmlinkage void ovfl_vec(void) asm ("ovfl");
asmlinkage void unfl_vec(void) asm ("unfl");
asmlinkage void snan_vec(void) asm ("snan");
asmlinkage void operr_vec(void) asm ("operr");
asmlinkage void bsun_vec(void) asm ("bsun");
asmlinkage void fline_vec(void) asm ("fline");
asmlinkage void unsupp_vec(void) asm ("unsupp");
 
vectors[VEC_FPDIVZ] = dz_vec;
vectors[VEC_FPIR] = inex_vec;
vectors[VEC_FPOVER] = ovfl_vec;
vectors[VEC_FPUNDER] = unfl_vec;
vectors[VEC_FPNAN] = snan_vec;
vectors[VEC_FPOE] = operr_vec;
vectors[VEC_FPBRUC] = bsun_vec;
vectors[VEC_FPBRUC] = bsun_vec;
vectors[VEC_LINE11] = fline_vec;
vectors[VEC_FPUNSUP] = unsupp_vec;
}
#endif
#ifdef CONFIG_IFPSP_060
if (m68k_is040or060 == 6) {
/* set up IFPSP entry points */
asmlinkage void snan_vec(void) asm ("_060_fpsp_snan");
asmlinkage void operr_vec(void) asm ("_060_fpsp_operr");
asmlinkage void ovfl_vec(void) asm ("_060_fpsp_ovfl");
asmlinkage void unfl_vec(void) asm ("_060_fpsp_unfl");
asmlinkage void dz_vec(void) asm ("_060_fpsp_dz");
asmlinkage void inex_vec(void) asm ("_060_fpsp_inex");
asmlinkage void fline_vec(void) asm ("_060_fpsp_fline");
asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp");
asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd");
asmlinkage void unimp_vec(void) asm ("_060_isp_unimp");
vectors[VEC_FPNAN] = snan_vec;
vectors[VEC_FPOE] = operr_vec;
vectors[VEC_FPOVER] = ovfl_vec;
vectors[VEC_FPUNDER] = unfl_vec;
vectors[VEC_FPDIVZ] = dz_vec;
vectors[VEC_FPIR] = inex_vec;
vectors[VEC_LINE11] = fline_vec;
vectors[VEC_FPUNSUP] = unsupp_vec;
vectors[VEC_UNIMPEA] = effadd_vec;
/* set up ISP entry points */
vectors[VEC_UNIMPII] = unimp_vec;
}
#endif
}
 
void set_evector(int vecnum, void (*handler)(void))
{
if (vecnum >= 0 && vecnum <= 256)
vectors[vecnum] = handler;
}
 
 
static inline void console_verbose(void)
{
extern int console_loglevel;
console_loglevel = 15;
mach_debug_init();
}
 
char *vec_names[] = {
"RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
"ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
"PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
"UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
"FORMAT ERROR", "UNINITIALIZED INTERRUPT",
"UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
"UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
"UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
"UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
"SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
"LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
"SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
"TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
"TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
"TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15"
};
 
char *space_names[] = {
"Space 0", "User Data", "User Program", "Space 3",
"Space 4", "Super Data", "Super Program", "CPU"
};
 
 
 
extern void die_if_kernel(char *,struct pt_regs *,int);
asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code);
 
asmlinkage void trap_c(struct frame *fp);
 
static inline void access_error060 (struct frame *fp)
{
unsigned long fslw = fp->un.fmt4.pc; /* is really FSLW for access error */
 
#ifdef DEBUG
printk("fslw=%#lx, fa=%#lx\n", ssw, fp->un.fmt4.effaddr);
#endif
 
if (fslw & MMU060_BPE) {
/* branch prediction error -> clear branch cache */
__asm__ __volatile__ ("movec %/cacr,%/d0\n\t"
"orl #0x00400000,%/d0\n\t"
"movec %/d0,%/cacr"
: : : "d0" );
/* return if there's no other error */
if (!(fslw & MMU060_ERR_BITS))
return;
}
if (fslw & (MMU060_DESC_ERR | MMU060_WP)) {
unsigned long errorcode;
unsigned long addr = fp->un.fmt4.effaddr;
errorcode = ((fslw & MMU060_WP) ? 1 : 0) |
((fslw & MMU060_W) ? 2 : 0);
#ifdef DEBUG
printk("errorcode = %d\n", errorcode );
#endif
if (fslw & MMU060_MA)
addr = PAGE_ALIGN(addr);
do_page_fault( (struct pt_regs *)fp, addr, errorcode );
}
else {
printk( "68060 access error, fslw=%lx\n", fslw );
trap_c( fp );
}
}
 
static unsigned long probe040 (int iswrite, int fc, unsigned long addr)
{
unsigned long mmusr;
unsigned long fs = get_fs();
 
set_fs (fc);
 
if (iswrite)
/* write */
asm volatile ("movel %1,%/a0\n\t"
".word 0xf548\n\t" /* ptestw (a0) */
".long 0x4e7a8805\n\t" /* movec mmusr,a0 */
"movel %/a0,%0"
: "=g" (mmusr)
: "g" (addr)
: "a0");
else
asm volatile ("movel %1,%/a0\n\t"
".word 0xf568\n\t" /* ptestr (a0) */
".long 0x4e7a8805\n\t" /* movec mmusr,a0 */
"movel %/a0,%0"
: "=g" (mmusr)
: "g" (addr)
: "a0");
 
 
set_fs (fs);
 
return mmusr;
}
 
static void do_040writeback (unsigned short ssw,
unsigned short wbs,
unsigned long wba,
unsigned long wbd,
struct frame *fp)
{
unsigned long fs = get_fs ();
unsigned long mmusr;
unsigned long errorcode;
 
/*
* No special handling for the second writeback anymore.
* It misinterpreted the misaligned status sometimes.
* This way an extra page-fault may be caused (Martin Apel).
*/
 
mmusr = probe040 (1, wbs & WBTM_040, wba);
errorcode = (mmusr & MMU_R_040) ? 3 : 2;
if (do_page_fault ((struct pt_regs *)fp, wba, errorcode))
/* just return if we can't perform the writeback */
return;
 
set_fs (wbs & WBTM_040);
switch (wbs & WBSIZ_040) {
case BA_SIZE_BYTE:
put_fs_byte (wbd & 0xff, (char *)wba);
break;
case BA_SIZE_WORD:
put_fs_word (wbd & 0xffff, (short *)wba);
break;
case BA_SIZE_LONG:
put_fs_long (wbd, (int *)wba);
break;
}
set_fs (fs);
}
 
static inline void access_error040 (struct frame *fp)
{
unsigned short ssw = fp->un.fmt7.ssw;
unsigned long mmusr;
 
#ifdef DEBUG
printk("ssw=%#x, fa=%#lx\n", ssw, fp->un.fmt7.faddr);
printk("wb1s=%#x, wb2s=%#x, wb3s=%#x\n", fp->un.fmt7.wb1s,
fp->un.fmt7.wb2s, fp->un.fmt7.wb3s);
printk ("wb2a=%lx, wb3a=%lx, wb2d=%lx, wb3d=%lx\n",
fp->un.fmt7.wb2a, fp->un.fmt7.wb3a,
fp->un.fmt7.wb2d, fp->un.fmt7.wb3d);
#endif
 
 
if (ssw & ATC_040) {
unsigned long addr = fp->un.fmt7.faddr;
unsigned long errorcode;
 
/*
* The MMU status has to be determined AFTER the address
* has been corrected if there was a misaligned access (MA).
*/
if (ssw & MA_040)
addr = PAGE_ALIGN (addr);
 
/* MMU error, get the MMUSR info for this access */
mmusr = probe040 (!(ssw & RW_040), ssw & TM_040, addr);
/*
#ifdef DEBUG
printk("mmusr = %lx\n", mmusr);
#endif
*/
errorcode = ((mmusr & MMU_R_040) ? 1 : 0) |
((ssw & RW_040) ? 0 : 2);
do_page_fault ((struct pt_regs *)fp, addr, errorcode);
} else {
printk ("68040 access error, ssw=%x\n", ssw);
trap_c (fp);
}
 
#if 0
if (fp->un.fmt7.wb1s & WBV_040)
printk("access_error040: cannot handle 1st writeback. oops.\n");
#endif
 
/*
* We may have to do a couple of writebacks here.
*
* MR: we can speed up the thing a little bit and let do_040writeback()
* not produce another page fault as wb2 corresponds to the address that
* caused the fault. on write faults no second fault is generated, but
* on read faults for security reasons (although per definitionem impossible)
*/
 
if (fp->un.fmt7.wb2s & WBV_040 && (fp->un.fmt7.wb2s &
WBTT_040) != BA_TT_MOVE16)
do_040writeback (ssw,
fp->un.fmt7.wb2s, fp->un.fmt7.wb2a,
fp->un.fmt7.wb2d, fp);
 
if (fp->un.fmt7.wb3s & WBV_040)
do_040writeback (ssw, fp->un.fmt7.wb3s,
fp->un.fmt7.wb3a, fp->un.fmt7.wb3d,
fp);
}
 
static inline void bus_error030 (struct frame *fp)
{
volatile unsigned short temp;
unsigned short mmusr;
unsigned long addr, desc, errorcode;
unsigned short ssw = fp->un.fmtb.ssw;
int user_space_fault = 1;
 
#if DEBUG
printk ("pid = %x ", current->pid);
printk ("SSW=%#06x ", ssw);
 
if (ssw & (FC | FB))
printk ("Instruction fault at %#010lx\n",
ssw & FC ?
fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2
:
fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr);
if (ssw & DF)
printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n",
ssw & RW ? "read" : "write",
fp->un.fmtb.daddr,
space_names[ssw & DFC], fp->ptregs.pc);
#endif
 
if (fp->ptregs.sr & PS_S) {
/* kernel fault must be a data fault to user space */
if (! ((ssw & DF) && ((ssw & DFC) == USER_DATA))) {
/* instruction fault or kernel data fault! */
if (ssw & (FC | FB))
printk ("Instruction fault at %#010lx\n",
fp->ptregs.pc);
if (ssw & DF) {
printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n",
ssw & RW ? "read" : "write",
fp->un.fmtb.daddr,
space_names[ssw & DFC], fp->ptregs.pc);
}
printk ("BAD KERNEL BUSERR\n");
die_if_kernel("Oops",&fp->ptregs,0);
force_sig(SIGSEGV, current);
user_space_fault = 0;
}
} else {
/* user fault */
if (!(ssw & (FC | FB)) && !(ssw & DF))
/* not an instruction fault or data fault! BAD */
panic ("USER BUSERR w/o instruction or data fault");
user_space_fault = 1;
#if DEBUG
printk("User space bus-error\n");
#endif
}
 
/* ++andreas: If a data fault and an instruction fault happen
at the same time map in both pages. */
 
/* First handle the data fault, if any. */
if (ssw & DF)
{
addr = fp->un.fmtb.daddr;
 
if (user_space_fault) {
asm volatile ("ptestr #1,%2@,#7,%0\n\t"
"pmove %/psr,%1@"
: "=a&" (desc)
: "a" (&temp), "a" (addr));
mmusr = temp;
} else
mmusr = MMU_I;
#if DEBUG
printk ("mmusr is %#x for addr %#lx in task %p\n",
mmusr, addr, current);
printk ("descriptor address is %#lx, contents %#lx\n",
mm_ptov(desc), *(unsigned long *)mm_ptov(desc));
#endif
 
errorcode = (mmusr & MMU_I) ? 0 : 1;
/* if (!(ssw & RW)) updated to 1.2.13pl6 */
if (!(ssw & RW) || ssw & RM)
errorcode |= 2;
 
if (mmusr & MMU_I)
do_page_fault ((struct pt_regs *)fp, addr, errorcode);
 
/* else if ((mmusr & MMU_WP) && !(ssw & RW)) */
 
else if ((mmusr & MMU_WP) && (!(ssw & RW) || ssw & RM))
do_page_fault ((struct pt_regs *)fp, addr, errorcode);
else if (mmusr & (MMU_B|MMU_L|MMU_S)) {
printk ("invalid %s access at %#lx from pc %#lx\n",
!(ssw & RW) ? "write" : "read", addr,
fp->ptregs.pc);
die_if_kernel("Oops",&fp->ptregs,mmusr);
force_sig(SIGSEGV, current);
return;
} else {
#ifdef DEBUG
static volatile long tlong;
#endif
 
printk ("weird %s access at %#lx from pc %#lx (ssw is %#x)\n",
!(ssw & RW) ? "write" : "read", addr,
fp->ptregs.pc, ssw);
asm volatile ("ptestr #1,%1@,#0\n\t"
"pmove %/psr,%0@"
: /* no outputs */
: "a" (&temp), "a" (addr));
mmusr = temp;
 
printk ("level 0 mmusr is %#x\n", mmusr);
#if 0
asm volatile ("pmove %/tt0,%0@"
: /* no outputs */
: "a" (&tlong));
printk ("tt0 is %#lx, ", tlong);
asm volatile ("pmove %/tt1,%0@"
: /* no outputs */
: "a" (&tlong));
printk ("tt1 is %#lx\n", tlong);
#endif
#if DEBUG
printk("Unknown SIGSEGV - 1\n");
#endif
die_if_kernel("Oops",&fp->ptregs,mmusr);
force_sig(SIGSEGV, current);
return;
}
 
/* setup an ATC entry for the access about to be retried */
if (!(ssw & RW))
asm volatile ("ploadw #1,%0@" : /* no outputs */
: "a" (addr));
else
asm volatile ("ploadr #1,%0@" : /* no outputs */
: "a" (addr));
 
/* If this was a data fault due to an invalid page and a
prefetch is pending on the same page, simulate it (but
only if the page is now valid). Otherwise we'll get an
weird insn access. */
if ((ssw & RB) && (mmusr & MMU_I))
{
unsigned long iaddr;
 
if ((fp->ptregs.format) == 0xB)
iaddr = fp->un.fmtb.baddr;
else
iaddr = fp->ptregs.pc + 4;
if (((addr ^ iaddr) & PAGE_MASK) == 0)
{
/* We only need to check the ATC as the entry has
already been set up above. */
asm volatile ("ptestr #1,%1@,#0\n\t"
"pmove %/psr,%0@"
: : "a" (&temp), "a" (iaddr));
mmusr = temp;
#ifdef DEBUG
printk ("prefetch iaddr=%#lx ssw=%#x mmusr=%#x\n",
iaddr, ssw, mmusr);
#endif
if (!(mmusr & MMU_I))
{
unsigned short insn;
asm volatile ("movesw %1@,%0"
: "=r" (insn)
: "a" (iaddr));
fp->un.fmtb.isb = insn;
fp->un.fmtb.ssw &= ~RB;
}
}
}
}
 
/* Now handle the instruction fault. */
 
/* get the fault address */
if ((fp->ptregs.format) == 0xA )
if (ssw & FC)
addr = fp->ptregs.pc + 2;
else if (ssw & FB)
addr = fp->ptregs.pc + 4;
else
return;
else
if (ssw & FC)
addr = fp->un.fmtb.baddr - 2;
else if (ssw & FB)
addr = fp->un.fmtb.baddr;
else
return;
 
if ((ssw & DF) && ((addr ^ fp->un.fmtb.daddr) & PAGE_MASK) == 0)
/* Insn fault on same page as data fault */
return;
 
if (user_space_fault) {
asm volatile ("ptestr #1,%2@,#7,%0\n\t"
"pmove %/psr,%1@"
: "=a&" (desc)
: "a" (&temp), "a" (addr));
mmusr = temp;
} else
mmusr = MMU_I;
#ifdef DEBUG
printk ("mmusr is %#x for addr %#lx in task %p\n",
mmusr, addr, current);
printk ("descriptor address is %#lx, contents %#lx\n",
mm_ptov(desc), *(unsigned long *)mm_ptov(desc));
#endif
 
errorcode = (mmusr & MMU_I) ? 0 : 1;
 
if (mmusr & MMU_I)
do_page_fault ((struct pt_regs *)fp, addr, errorcode);
else if (mmusr & (MMU_B|MMU_L|MMU_S)) {
printk ("invalid insn access at %#lx from pc %#lx\n",
addr, fp->ptregs.pc);
#if DEBUG
printk("Unknown SIGSEGV - 2\n");
#endif
die_if_kernel("Oops",&fp->ptregs,mmusr);
force_sig(SIGSEGV, current);
return;
} else {
#ifdef DEBUG
static volatile long tlong;
#endif
 
printk ("weird insn access at %#lx from pc %#lx (ssw is %#x)\n",
addr, fp->ptregs.pc, ssw);
asm volatile ("ptestr #1,%1@,#0\n\t"
"pmove %/psr,%0@"
: /* no outputs */
: "a" (&temp), "a" (addr));
mmusr = temp;
printk ("level 0 mmusr is %#x\n", mmusr);
#ifdef DEBUG
if (boot_info.cputype & CPU_68030) {
asm volatile ("pmove %/tt0,%0@"
: /* no outputs */
: "a" (&tlong));
printk ("tt0 is %#lx, ", tlong);
asm volatile ("pmove %/tt1,%0@"
: /* no outputs */
: "a" (&tlong));
printk ("tt1 is %#lx\n", tlong);
}
 
#endif
#if DEBUG
printk("Unknown SIGSEGV - 3\n");
#endif
die_if_kernel("Oops",&fp->ptregs,mmusr);
force_sig(SIGSEGV, current);
return;
}
 
/* setup an ATC entry for the access about to be retried */
asm volatile ("ploadr #1,%0@" : /* no outputs */
: "a" (addr));
}
 
asmlinkage void buserr_c(struct frame *fp)
{
/* Only set esp0 if coming from user mode */
if (user_mode(&fp->ptregs))
current->tss.esp0 = (unsigned long) fp;
 
#if DEBUG
printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format);
#endif
 
switch (fp->ptregs.format) {
case 4: /* 68060 access error */
access_error060 (fp);
break;
case 0x7: /* 68040 access error */
access_error040 (fp);
break;
case 0xa:
case 0xb:
bus_error030 (fp);
break;
default:
die_if_kernel("bad frame format",&fp->ptregs,0);
#if DEBUG
printk("Unknown SIGSEGV - 4\n");
#endif
force_sig(SIGSEGV, current);
}
}
 
 
int kstack_depth_to_print = 48;
 
/* MODULE_RANGE is a guess of how much space is likely to be
vmalloced. */
#define MODULE_RANGE (8*1024*1024)
 
static void dump_stack(struct frame *fp)
{
unsigned long *stack, *endstack, addr, module_start, module_end;
extern char _start, _etext;
int i;
 
addr = (unsigned long)&fp->un;
printk("Frame format=%X ", fp->ptregs.format);
switch (fp->ptregs.format) {
case 0x2:
printk("instr addr=%08lx\n", fp->un.fmt2.iaddr);
addr += sizeof(fp->un.fmt2);
break;
case 0x3:
printk("eff addr=%08lx\n", fp->un.fmt3.effaddr);
addr += sizeof(fp->un.fmt3);
break;
case 0x4:
printk((m68k_is040or060 == 6 ? "fault addr=%08lx fslw=%08lx\n"
: "eff addr=%08lx pc=%08lx\n"),
fp->un.fmt4.effaddr, fp->un.fmt4.pc);
addr += sizeof(fp->un.fmt4);
break;
case 0x7:
printk("eff addr=%08lx ssw=%04x faddr=%08lx\n",
fp->un.fmt7.effaddr, fp->un.fmt7.ssw, fp->un.fmt7.faddr);
printk("wb 1 stat/addr/data: %04x %08lx %08lx\n",
fp->un.fmt7.wb1s, fp->un.fmt7.wb1a, fp->un.fmt7.wb1dpd0);
printk("wb 2 stat/addr/data: %04x %08lx %08lx\n",
fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, fp->un.fmt7.wb2d);
printk("wb 3 stat/addr/data: %04x %08lx %08lx\n",
fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, fp->un.fmt7.wb3d);
printk("push data: %08lx %08lx %08lx %08lx\n",
fp->un.fmt7.wb1dpd0, fp->un.fmt7.pd1, fp->un.fmt7.pd2,
fp->un.fmt7.pd3);
addr += sizeof(fp->un.fmt7);
break;
case 0x9:
printk("instr addr=%08lx\n", fp->un.fmt9.iaddr);
addr += sizeof(fp->un.fmt9);
break;
case 0xa:
printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n",
fp->un.fmta.ssw, fp->un.fmta.isc, fp->un.fmta.isb,
fp->un.fmta.daddr, fp->un.fmta.dobuf);
addr += sizeof(fp->un.fmta);
break;
case 0xb:
printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n",
fp->un.fmtb.ssw, fp->un.fmtb.isc, fp->un.fmtb.isb,
fp->un.fmtb.daddr, fp->un.fmtb.dobuf);
printk("baddr=%08lx dibuf=%08lx ver=%x\n",
fp->un.fmtb.baddr, fp->un.fmtb.dibuf, fp->un.fmtb.ver);
addr += sizeof(fp->un.fmtb);
break;
default:
printk("\n");
}
 
stack = (unsigned long *)addr;
endstack = (unsigned long *)PAGE_ALIGN(addr);
 
printk("Stack from %08lx:\n ", (unsigned long)stack);
for (i = 0; i < kstack_depth_to_print; i++) {
if (stack + 1 > endstack)
break;
if (i && ((i % 8) == 0))
printk("\n ");
printk("%08lx ", *stack++);
}
 
printk ("\nCall Trace: ");
stack = (unsigned long *) addr;
i = 1;
module_start = VMALLOC_START;
module_end = module_start + MODULE_RANGE;
while (stack + 1 <= endstack) {
addr = *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) &&
(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 < 10; i++)
printk("%04x ", 0xffff & ((short *) fp->ptregs.pc)[i]);
printk ("\n");
}
 
void bad_super_trap (struct frame *fp)
{
console_verbose();
if ((fp->ptregs.vector) < 48*4)
printk ("*** %s *** FORMAT=%X\n",
vec_names[(fp->ptregs.vector) >> 2],
fp->ptregs.format);
else
printk ("*** Exception %d *** FORMAT=%X\n",
(fp->ptregs.vector) >> 2,
fp->ptregs.format);
if (((fp->ptregs.vector) >> 2) == VEC_ADDRERR
&& !m68k_is040or060) {
unsigned short ssw = fp->un.fmtb.ssw;
 
printk ("SSW=%#06x ", ssw);
 
if (ssw & RC)
printk ("Pipe stage C instruction fault at %#010lx\n",
(fp->ptregs.format) == 0xA ?
fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2);
if (ssw & RB)
printk ("Pipe stage B instruction fault at %#010lx\n",
(fp->ptregs.format) == 0xA ?
fp->ptregs.pc + 4 : fp->un.fmtb.baddr);
if (ssw & DF)
printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n",
ssw & RW ? "read" : "write",
fp->un.fmtb.daddr, space_names[ssw & DFC],
fp->ptregs.pc);
}
printk ("Current process id is %d\n", current->pid);
die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0);
}
 
asmlinkage void trap_c(struct frame *fp)
{
int sig;
 
if ((fp->ptregs.sr & PS_S)
&& ((fp->ptregs.vector) >> 2) == VEC_TRACE
&& !(fp->ptregs.sr & PS_T)) {
/* traced a trapping instruction */
unsigned char *lp = ((unsigned char *)&fp->un.fmt2) + 4;
current->flags |= PF_DTRACE;
/* clear the trace bit */
(*(unsigned short *)lp) &= ~PS_T;
return;
} else if (fp->ptregs.sr & PS_S) {
bad_super_trap(fp);
return;
}
 
/* send the appropriate signal to the user program */
switch ((fp->ptregs.vector) >> 2) {
case VEC_ADDRERR:
sig = SIGBUS;
break;
case VEC_BUSERR:
sig = SIGSEGV;
break;
case VEC_ILLEGAL:
case VEC_PRIV:
case VEC_LINE10:
case VEC_LINE11:
case VEC_COPROC:
case VEC_TRAP1:
case VEC_TRAP2:
case VEC_TRAP3:
case VEC_TRAP4:
case VEC_TRAP5:
case VEC_TRAP6:
case VEC_TRAP7:
case VEC_TRAP8:
case VEC_TRAP9:
case VEC_TRAP10:
case VEC_TRAP11:
case VEC_TRAP12:
case VEC_TRAP13:
case VEC_TRAP14:
sig = SIGILL;
break;
case VEC_FPBRUC:
case VEC_FPIR:
case VEC_FPDIVZ:
case VEC_FPUNDER:
case VEC_FPOE:
case VEC_FPOVER:
case VEC_FPNAN:
{
unsigned char fstate[216];
 
__asm__ __volatile__ ("fsave %0@" : : "a" (fstate) : "memory");
/* Set the exception pending bit in the 68882 idle frame */
if (*(unsigned short *) fstate == 0x1f38)
{
fstate[fstate[1]] |= 1 << 3;
__asm__ __volatile__ ("frestore %0@" : : "a" (fstate));
}
}
/* fall through */
case VEC_ZERODIV:
case VEC_TRAP:
sig = SIGFPE;
break;
case VEC_TRACE: /* ptrace single step */
fp->ptregs.sr &= ~PS_T;
case VEC_TRAP15: /* breakpoint */
sig = SIGTRAP;
break;
default:
sig = SIGILL;
break;
}
 
force_sig (sig, current);
}
 
asmlinkage void set_esp0 (unsigned long ssp)
{
current->tss.esp0 = ssp;
}
 
void die_if_kernel (char *str, struct pt_regs *fp, int nr)
{
if (!(fp->sr & PS_S))
return;
 
console_verbose();
printk("%s: %08x\n",str,nr);
printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp);
printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
fp->d0, fp->d1, fp->d2, fp->d3);
printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
fp->d4, fp->d5, fp->a0, fp->a1);
 
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page)
printk("Corrupted stack page\n");
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, current->kernel_stack_page);
dump_stack((struct frame *)fp);
do_exit(SIGSEGV);
}
/Makefile
0,0 → 1,21
#
# 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.o:
$(CC) -D__ASSEMBLY__ -traditional -Wa,-m68030 -c $< -o $*.o
 
all: kernel.o head.o
O_TARGET := kernel.o
O_OBJS := entry.o process.o traps.o ints.o signal.o ptrace.o \
setup.o bios32.o sys_m68k.o console.o time.o
OX_OBJS = ksyms.o
 
head.o: head.S
 
include $(TOPDIR)/Rules.make
/ints.c
0,0 → 1,236
/*
* ints.c -- 680x0 Linux general interrupt handling code
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* 07/03/96: Timer initialization, and thus mach_sched_init(),
* removed from request_irq() and moved to init_time().
* We should therefore consider renaming our add_isr() and
* remove_isr() to request_irq() and free_irq()
* respectively, so they are compliant with the other
* architectures. /Jes
*/
 
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
 
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/traps.h>
#include <asm/page.h>
#include <asm/machdep.h>
 
/* list is accessed 0-6 for IRQs 1-7 */
static isr_node_t *isr_list[7];
 
/* The number of spurious interrupts */
volatile unsigned long num_spurious;
/*
unsigned long interrupt_stack[PAGE_SIZE/sizeof(long)];
*/
 
/*
* void init_IRQ(void)
*
* Parameters: None
*
* Returns: Nothing
*
* This function should be called during kernel startup to initialize
* the IRQ handling routines.
*/
 
void init_IRQ(void)
{
/* Setup interrupt stack pointer */
/*
asm ("movec %0,%/isp"
: : "r" (interrupt_stack + sizeof (interrupt_stack) / sizeof (long)));
*/
mach_init_INTS ();
}
 
void insert_isr (isr_node_t **listp, isr_node_t *node)
{
unsigned long spl;
isr_node_t *cur;
 
save_flags(spl);
cli();
 
cur = *listp;
 
while (cur && cur->pri <= node->pri)
{
listp = &cur->next;
cur = cur->next;
}
 
node->next = cur;
*listp = node;
 
restore_flags(spl);
}
 
void delete_isr (isr_node_t **listp, isrfunc isr, void *data)
{
unsigned long flags;
isr_node_t *np;
 
save_flags(flags);
cli();
for (np = *listp; np; listp = &np->next, np = *listp) {
if (np->isr == isr && np->data == data) {
*listp = np->next;
/* Mark it as free. */
np->isr = NULL;
restore_flags(flags);
return;
}
}
restore_flags(flags);
printk ("delete_isr: isr %p not found on list!\n", isr);
}
 
#define NUM_ISR_NODES 100
static isr_node_t nodes[NUM_ISR_NODES];
 
isr_node_t *new_isr_node(void)
{
isr_node_t *np;
 
for (np = nodes; np < &nodes[NUM_ISR_NODES]; np++)
if (np->isr == NULL)
return np;
 
printk ("new_isr_node: out of nodes");
return NULL;
}
 
int add_isr (unsigned long source, isrfunc isr, int pri, void *data,
char *name)
{
isr_node_t *p;
 
if (source & IRQ_MACHSPEC)
{
return mach_add_isr (source, isr, pri, data, name);
}
 
if (source < IRQ1 || source > IRQ7)
panic ("add_isr: Incorrect IRQ source %ld from %s\n", source, name);
 
p = new_isr_node();
if (p == NULL)
return 0;
p->isr = isr;
p->pri = pri;
p->data = data;
p->name = name;
p->next = NULL;
 
insert_isr (&isr_list[source-1], p);
 
return 1;
}
 
int remove_isr (unsigned long source, isrfunc isr, void *data)
{
if (source & IRQ_MACHSPEC)
return mach_remove_isr (source, isr, data);
 
if (source < IRQ1 || source > IRQ7) {
printk ("remove_isr: Incorrect IRQ source %ld\n", source);
return 0;
}
 
delete_isr (&isr_list[source - 1], isr, data);
return 1;
}
 
void call_isr_list(int irq, isr_node_t *p, struct pt_regs *fp)
{
while (p) {
p->isr (irq, fp, p->data);
p = p->next;
}
}
 
asmlinkage void process_int(int vec, struct pt_regs *regs)
{
int level;
 
if (vec >= VECOFF(VEC_INT1) && vec <= VECOFF(VEC_INT7))
level = (vec - VECOFF(VEC_SPUR)) >> 2;
else {
if (mach_process_int)
mach_process_int(vec, regs);
else
panic("Can't process interrupt vector 0x%03x\n", vec);
return;
}
 
kstat.interrupts[level]++;
call_isr_list (level, isr_list[level-1], regs);
}
 
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long flags, const char * devname, void *dev_id)
{
return -EINVAL;
}
 
void free_irq(unsigned int irq, void *dev_id)
{
}
 
/*
* Do we need these probe functions on the m68k?
*/
unsigned long probe_irq_on (void)
{
return 0;
}
 
int probe_irq_off (unsigned long irqs)
{
return 0;
}
 
void enable_irq(unsigned int irq_nr)
{
if ((irq_nr & IRQ_MACHSPEC) && mach_enable_irq)
mach_enable_irq(irq_nr);
}
 
void disable_irq(unsigned int irq_nr)
{
if ((irq_nr & IRQ_MACHSPEC) && mach_disable_irq)
mach_disable_irq(irq_nr);
}
 
int get_irq_list(char *buf)
{
int i, len = 0;
isr_node_t *p;
/* autovector interrupts */
for (i = IRQ1; i <= IRQ7; ++i) {
if (!isr_list[i-1])
continue;
len += sprintf(buf+len, "auto %2d: %8d ", i, kstat.interrupts[i]);
for (p = isr_list[i-1]; p; p = p->next) {
len += sprintf(buf+len, "%s\n", p->name);
if (p->next)
len += sprintf(buf+len, " ");
}
}
 
len = mach_get_irq_list(buf, len);
return len;
}
/signal.c
0,0 → 1,557
/*
* linux/arch/m68k/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
 
/*
* 680x0 support by Hamish Macdonald
*/
 
#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>
#include <asm/pgtable.h>
#include <asm/traps.h>
#include <asm/bootinfo.h>
 
#define offsetof(type, member) ((size_t)(&((type *)0)->member))
 
#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);
 
static const int extra_sizes[16] = {
0,
-1, /* sizeof(((struct frame *)0)->un.fmt1), */
sizeof(((struct frame *)0)->un.fmt2),
sizeof(((struct frame *)0)->un.fmt3),
sizeof(((struct frame *)0)->un.fmt4),
-1, /* sizeof(((struct frame *)0)->un.fmt5), */
-1, /* sizeof(((struct frame *)0)->un.fmt6), */
sizeof(((struct frame *)0)->un.fmt7),
-1, /* sizeof(((struct frame *)0)->un.fmt8), */
sizeof(((struct frame *)0)->un.fmt9),
sizeof(((struct frame *)0)->un.fmta),
sizeof(((struct frame *)0)->un.fmtb),
-1, /* sizeof(((struct frame *)0)->un.fmtc), */
-1, /* sizeof(((struct frame *)0)->un.fmtd), */
-1, /* sizeof(((struct frame *)0)->un.fmte), */
-1, /* sizeof(((struct frame *)0)->un.fmtf), */
};
 
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int do_sigsuspend(struct pt_regs *regs)
{
unsigned long oldmask = current->blocked;
unsigned long newmask = regs->d3;
 
current->blocked = newmask & _BLOCKABLE;
regs->d0 = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(oldmask, regs))
return -EINTR;
}
}
 
static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */
 
/*
* This sets regs->usp even though we don't actually use sigstacks yet..
*/
asmlinkage int do_sigreturn(unsigned long __unused)
{
struct sigcontext_struct context;
struct frame * regs;
struct switch_stack *sw;
int fsize = 0;
int formatvec = 0;
unsigned long fp;
unsigned long usp = rdusp();
 
#if 0
printk("sys_sigreturn, usp=%08x\n", (unsigned) usp);
#endif
 
/* get stack frame pointer */
sw = (struct switch_stack *) &__unused;
regs = (struct frame *) (sw + 1);
 
/* get previous context (including pointer to possible extra junk) */
if (verify_area(VERIFY_READ, (void *)usp, sizeof(context)))
goto badframe;
 
memcpy_fromfs(&context,(void *)usp, sizeof(context));
 
fp = usp + sizeof (context);
 
/* restore signal mask */
current->blocked = context.sc_mask & _BLOCKABLE;
 
/* restore passed registers */
regs->ptregs.d0 = context.sc_d0;
regs->ptregs.d1 = context.sc_d1;
regs->ptregs.a0 = context.sc_a0;
regs->ptregs.a1 = context.sc_a1;
regs->ptregs.sr = (regs->ptregs.sr & 0xff00)|(context.sc_sr & 0xff);
regs->ptregs.pc = context.sc_pc;
 
wrusp(context.sc_usp);
formatvec = context.sc_formatvec;
regs->ptregs.format = formatvec >> 12;
regs->ptregs.vector = formatvec & 0xfff;
if (context.sc_fpstate[0])
{
/* Verify the frame format. */
if (context.sc_fpstate[0] != fpu_version){
#if DEBUG
printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
context.sc_fpcntl);
printk("Wrong fpu: sc_fpstate[0]=%02x fpu_version=%02x\n",
(unsigned) context.sc_fpstate[0], (unsigned) fpu_version);
{
int i;
printk("Saved fp_state: ");
for (i = 0; i < 216; i++){
printk("%02x ", context.sc_fpstate[i]);
}
printk("\n");
}
#endif
goto badframe;
}
if (boot_info.cputype & FPU_68881)
{
if (context.sc_fpstate[1] != 0x18
&& context.sc_fpstate[1] != 0xb4)
goto badframe;
}
else if (boot_info.cputype & FPU_68882)
{
if (context.sc_fpstate[1] != 0x38
&& context.sc_fpstate[1] != 0xd4){
#if 0
printk("Wrong 68882 fpu-state\n");
#endif
goto badframe;
}
}
else if (boot_info.cputype & FPU_68040)
{
if (!((context.sc_fpstate[1] == 0x00)|| \
(context.sc_fpstate[1] == 0x28)|| \
(context.sc_fpstate[1] == 0x60))){
#if 0
printk("Wrong 68040 fpu-state\n");
#endif
goto badframe;
}
}
else if (boot_info.cputype & FPU_68060)
{
if (!((context.sc_fpstate[1] == 0x00)|| \
(context.sc_fpstate[1] == 0x60)|| \
(context.sc_fpstate[1] == 0xe0))){
#if 0
printk("Wrong 68060 fpu-state\n");
#endif
goto badframe;
}
}
__asm__ volatile ("fmovemx %0,%/fp0-%/fp1\n\t"
"fmoveml %1,%/fpcr/%/fpsr/%/fpiar"
: /* no outputs */
: "m" (*context.sc_fpregs),
"m" (*context.sc_fpcntl));
}
__asm__ volatile ("frestore %0" : : "m" (*context.sc_fpstate));
 
fsize = extra_sizes[regs->ptregs.format];
if (fsize < 0) {
/*
* user process trying to return with weird frame format
*/
#if DEBUG
printk("user process returning with weird frame format\n");
#endif
goto badframe;
}
 
/* OK. Make room on the supervisor stack for the extra junk,
* if necessary.
*/
 
if (fsize) {
if (verify_area(VERIFY_READ, (void *)fp, fsize))
goto badframe;
 
#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack))
__asm__ __volatile__
("movel %0,%/a0\n\t"
"subl %1,%/a0\n\t" /* make room on stack */
"movel %/a0,%/sp\n\t" /* set stack pointer */
/* move switch_stack and pt_regs */
"1: movel %0@+,%/a0@+\n\t"
" dbra %2,1b\n\t"
"lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt stuff */
"lsrl #2,%1\n\t"
"subql #1,%1\n\t"
"2: movesl %4@+,%2\n\t"
" movel %2,%/a0@+\n\t"
" dbra %1,2b\n\t"
"bral " SYMBOL_NAME_STR(ret_from_signal)
: /* no outputs, it doesn't ever return */
: "a" (sw), "d" (fsize), "d" (frame_offset/4-1),
"n" (frame_offset), "a" (fp)
: "a0");
#undef frame_offset
goto badframe;
/* NOTREACHED */
}
 
return regs->ptregs.d0;
badframe:
do_exit(SIGSEGV);
}
 
/*
* Set up a signal frame...
*
* This routine is somewhat complicated by the fact that if the
* kernel may be entered by an exception other than a system call;
* e.g. a bus error or other "bad" exception. If this is the case,
* then *all* the context on the kernel stack frame must be saved.
*
* For a large number of exceptions, the stack frame format is the same
* as that which will be created when the process traps back to the kernel
* when finished executing the signal handler. In this case, nothing
* must be done. This is exception frame format "0". For exception frame
* formats "2", "9", "A" and "B", the extra information on the frame must
* be saved. This information is saved on the user stack and restored
* when the signal handler is returned.
*
* The format of the user stack when executing the signal handler is:
*
* usp -> RETADDR (points to code below)
* signum (parm #1)
* sigcode (parm #2 ; vector number)
* scp (parm #3 ; sigcontext pointer, pointer to #1 below)
* code1 (addaw #20,sp) ; pop parms and code off stack
* code2 (moveq #119,d0; trap #0) ; sigreturn syscall
* #1| oldmask
* | old usp
* | d0 (first saved reg)
* | d1
* | a0
* | a1
* | sr (saved status register)
* | pc (old pc; one to return to)
* | forvec (format and vector word of old supervisor stack frame)
* | floating point context
*
* These are optionally followed by some extra stuff, depending on the
* stack frame interrupted. This is 1 longword for format "2", 3
* longwords for format "9", 6 longwords for format "A", and 21
* longwords for format "B".
*/
 
#define UFRAME_SIZE(fs) (sizeof(struct sigcontext_struct)/4 + 6 + fs/4)
 
static void setup_frame (struct sigaction * sa, unsigned long **fp,
unsigned long pc, struct frame *regs, int
signr, unsigned long oldmask)
{
struct sigcontext_struct context;
unsigned long *frame, *tframe;
int fsize = extra_sizes[regs->ptregs.format];
 
if (fsize < 0) {
printk ("setup_frame: Unknown frame format %#x\n",
regs->ptregs.format);
do_exit(SIGSEGV);
}
frame = *fp - UFRAME_SIZE(fsize);
if (verify_area(VERIFY_WRITE,frame,UFRAME_SIZE(fsize)*4))
do_exit(SIGSEGV);
if (fsize) {
memcpy_tofs (frame + UFRAME_SIZE(0), &regs->un, fsize);
regs->ptregs.stkadj = fsize;
}
 
/* set up the "normal" stack seen by the signal handler */
tframe = frame;
 
/* return address points to code on stack */
put_user((ulong)(frame+4), tframe); tframe++;
if (current->exec_domain && current->exec_domain->signal_invmap)
put_user(current->exec_domain->signal_invmap[signr], tframe);
else
put_user(signr, tframe);
tframe++;
 
put_user(regs->ptregs.vector, tframe); tframe++;
/* "scp" parameter. points to sigcontext */
put_user((ulong)(frame+6), tframe); tframe++;
 
/* set up the return code... */
put_user(0xdefc0014,tframe); tframe++; /* addaw #20,sp */
put_user(0x70774e40,tframe); tframe++; /* moveq #119,d0; trap #0 */
 
/* Flush caches so the instructions will be correctly executed. (MA) */
cache_push_v ((unsigned long)frame, (int)tframe - (int)frame);
 
/* setup and copy the sigcontext structure */
context.sc_mask = oldmask;
context.sc_usp = (unsigned long)*fp;
context.sc_d0 = regs->ptregs.d0;
context.sc_d1 = regs->ptregs.d1;
context.sc_a0 = regs->ptregs.a0;
context.sc_a1 = regs->ptregs.a1;
context.sc_sr = regs->ptregs.sr;
context.sc_pc = pc;
context.sc_formatvec = (regs->ptregs.format << 12 |
regs->ptregs.vector);
#if DEBUG
printk("formatvec: %02x\n", (unsigned) context.sc_formatvec);
#endif
__asm__ volatile ("fsave %0" : : "m" (*context.sc_fpstate) : "memory");
if (context.sc_fpstate[0])
{
fpu_version = context.sc_fpstate[0];
#if DEBUG
{
int i;
printk("Saved fp_state: ");
for (i = 0; i < 216; i++){
printk("%02x ", context.sc_fpstate[i]);
}
printk("\n");
}
printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
context.sc_fpcntl);
#endif
__asm__ volatile ("fmovemx %/fp0-%/fp1,%0\n\t"
"fmoveml %/fpcr/%/fpsr/%/fpiar,%1"
: /* no outputs */
: "m" (*context.sc_fpregs),
"m" (*context.sc_fpcntl)
: "memory");
}
#if DEBUG
{
int i;
printk("Saved fp_state: ");
for (i = 0; i < 216; i++){
printk("%02x ", context.sc_fpstate[i]);
}
printk("\n");
}
#endif
memcpy_tofs (tframe, &context, sizeof(context));
/*
* no matter what frame format we were using before, we
* will do the "RTE" using a normal 4 word frame.
*/
regs->ptregs.format = 0;
 
/* "return" new usp to caller */
*fp = frame;
}
 
/*
* 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_in)
{
unsigned long mask = ~current->blocked;
unsigned long handler_signal = 0;
unsigned long *frame = NULL;
unsigned long pc = 0;
unsigned long signr;
struct frame *regs = (struct frame *)regs_in;
struct sigaction * sa;
 
current->tss.esp0 = (unsigned long) regs;
 
while ((signr = current->signal & mask)) {
__asm__("bfffo %2,#0,#0,%1\n\t"
"bfclr %0,%1,#1\n\t"
"eorw #31,%1"
:"=m" (current->signal),"=r" (signr)
:"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);
schedule();
if (!(signr = current->exit_code)) {
discard_frame:
/* Make sure that a faulted bus cycle
isn't restarted. */
switch (regs->ptregs.format) {
case 7:
case 9:
case 10:
case 11:
regs->ptregs.stkadj = extra_sizes[regs->ptregs.format];
regs->ptregs.format = 0;
break;
}
continue;
}
current->exit_code = 0;
if (signr == SIGSTOP)
goto discard_frame;
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 SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
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);
schedule();
continue;
 
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGIOT: case SIGFPE: case SIGSEGV:
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, (struct pt_regs *)regs))
signr |= 0x80;
}
/* fall through */
default:
current->signal |= _S(signr & 0x7f);
do_exit(signr);
}
}
/*
* OK, we're invoking a handler
*/
if (regs->ptregs.orig_d0 >= 0) {
if (regs->ptregs.d0 == -ERESTARTNOHAND ||
(regs->ptregs.d0 == -ERESTARTSYS &&
!(sa->sa_flags & SA_RESTART)))
regs->ptregs.d0 = -EINTR;
}
handler_signal |= 1 << (signr-1);
mask &= ~sa->sa_mask;
}
if (regs->ptregs.orig_d0 >= 0 &&
(regs->ptregs.d0 == -ERESTARTNOHAND ||
regs->ptregs.d0 == -ERESTARTSYS ||
regs->ptregs.d0 == -ERESTARTNOINTR)) {
regs->ptregs.d0 = regs->ptregs.orig_d0;
regs->ptregs.pc -= 2;
}
if (!handler_signal) /* no handler will be called - return 0 */
{
/* If we are about to discard some frame stuff we must
copy over the remaining frame. */
if (regs->ptregs.stkadj)
{
struct frame *tregs =
(struct frame *) ((ulong) regs + regs->ptregs.stkadj);
 
/* This must be copied with decreasing addresses to
handle overlaps. */
tregs->ptregs.vector = regs->ptregs.vector;
tregs->ptregs.format = regs->ptregs.format;
tregs->ptregs.pc = regs->ptregs.pc;
tregs->ptregs.sr = regs->ptregs.sr;
}
return 0;
}
pc = regs->ptregs.pc;
frame = (unsigned long *)rdusp();
signr = 1;
sa = current->sig->action;
for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
if (mask > handler_signal)
break;
if (!(mask & handler_signal))
continue;
setup_frame(sa,&frame,pc,regs,signr,oldmask);
pc = (unsigned long) sa->sa_handler;
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
/* force a supervisor-mode page-in of the signal handler to reduce races */
__asm__ __volatile__("movesb %0,%/d0": :"m" (*(char *)pc):"d0");
current->blocked |= sa->sa_mask;
oldmask |= sa->sa_mask;
}
wrusp((unsigned long)frame);
regs->ptregs.pc = pc;
 
/*
* if setup_frame saved some extra frame junk, we need to
* skip over that stuff when doing the RTE. This means we have
* to move the machine portion of the stack frame to where the
* "RTE" instruction expects it. The signal that we need to
* do this is that regs->stkadj is nonzero.
*/
if (regs->ptregs.stkadj) {
struct frame *tregs =
(struct frame *)((ulong)regs + regs->ptregs.stkadj);
#if DEBUG
printk("Performing stackadjust=%04x\n", (unsigned)
regs->ptregs.stkadj);
#endif
/* This must be copied with decreasing addresses to
handle overlaps. */
tregs->ptregs.vector = regs->ptregs.vector;
tregs->ptregs.format = regs->ptregs.format;
tregs->ptregs.pc = regs->ptregs.pc;
tregs->ptregs.sr = regs->ptregs.sr;
}
 
return 1;
}

powered by: WebSVN 2.1.0

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