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), ®s->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; |
} |