diff options
author | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
---|---|---|
committer | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
commit | 709c4d66e0b107ca606941b988bad717c0b45d9b (patch) | |
tree | 37ee08b1eb308f3b2b6426d5793545c38396b838 /recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch | |
parent | fa6cd5a3b993f16c27de4ff82b42684516d433ba (diff) |
rename packages/ to recipes/ per earlier agreement
See links below for more details:
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816
Signed-off-by: Denys Dmytriyenko <denis@denix.org>
Acked-by: Mike Westerhof <mwester@dls.net>
Acked-by: Philip Balister <philip@balister.org>
Acked-by: Khem Raj <raj.khem@gmail.com>
Acked-by: Marcin Juszkiewicz <hrw@openembedded.org>
Acked-by: Koen Kooi <koen@openembedded.org>
Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch')
-rw-r--r-- | recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch | 9069 |
1 files changed, 9069 insertions, 0 deletions
diff --git a/recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch b/recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch new file mode 100644 index 0000000000..e64013e8ae --- /dev/null +++ b/recipes/linux/linux-colinux-2.6.10/colinux-0.6.2.patch @@ -0,0 +1,9069 @@ +diff -urN a/CREDITS b/CREDITS +--- a/CREDITS ++++ b/CREDITS +@@ -52,6 +52,12 @@ + S: Buenos Aires + S: Argentina + ++A: Dan Aloni ++E: da-x@colinux.org ++D: Cooperative Linux ++D: Various kernel patches ++S: Israel ++ + N: Tim Alpaerts + E: tim_alpaerts@toyota-motor-europe.com + D: 802.2 class II logical link control layer, +diff -urN a/Makefile b/Makefile +--- a/Makefile ++++ b/Makefile +@@ -319,6 +319,11 @@ + AS = $(CROSS_COMPILE)as + LD = $(CROSS_COMPILE)ld + CC = $(CROSS_COMPILE)gcc ++ifeq ($(GCCTRACE),Y) ++CC = $(CORSS_COMPILE)$(COLINUX_ROOT)/bin/tracewrapper.py gcc ++else ++CC = $(CROSS_COMPILE)gcc ++endif + CPP = $(CC) -E + AR = $(CROSS_COMPILE)ar + NM = $(CROSS_COMPILE)nm +diff -urN a/arch/i386/Kconfig b/arch/i386/Kconfig +--- a/arch/i386/Kconfig ++++ b/arch/i386/Kconfig +@@ -205,6 +205,7 @@ + + config M586TSC + bool "Pentium-Classic" ++ depends on !COOPERATIVE + help + Select this for a Pentium Classic processor with the RDTSC (Read + Time Stamp Counter) instruction for benchmarking. +@@ -543,6 +544,10 @@ + If you have a system with several CPUs, you do not need to say Y + here: the IO-APIC will be used automatically. + ++config X86_UP_COPIC ++ bool 'Cooperative PIC (COPIC) support' ++ depends on COOPERATIVE ++ + config X86_LOCAL_APIC + bool + depends on !SMP && X86_UP_APIC +@@ -555,7 +560,7 @@ + + config X86_TSC + bool +- depends on (MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2) && !X86_NUMAQ ++ depends on (MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2) && !X86_NUMAQ && !COOPERATIVE + default y + + config X86_MCE +@@ -882,6 +887,10 @@ + + source kernel/power/Kconfig + ++config COOPERATIVE ++ bool 'Cooperative Mode' ++ default y ++ + source "drivers/acpi/Kconfig" + + menu "APM (Advanced Power Management) BIOS Support" +diff -urN a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile +--- a/arch/i386/kernel/Makefile ++++ b/arch/i386/kernel/Makefile +@@ -17,6 +17,7 @@ + obj-$(CONFIG_X86_MSR) += msr.o + obj-$(CONFIG_X86_CPUID) += cpuid.o + obj-$(CONFIG_MICROCODE) += microcode.o ++obj-$(CONFIG_COOPERATIVE) += cooperative.o + obj-$(CONFIG_APM) += apm.o + obj-$(CONFIG_X86_SMP) += smp.o smpboot.o + obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o +diff -urN a/arch/i386/kernel/cooperative.c b/arch/i386/kernel/cooperative.c +--- a/arch/i386/kernel/cooperative.c ++++ b/arch/i386/kernel/cooperative.c +@@ -0,0 +1,340 @@ ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/interrupt.h> ++#include <linux/mm.h> ++ ++#include <linux/cooperative_internal.h> ++#include <asm/cooperative_internal.h> ++#include <asm/smp.h> ++#include <asm/desc.h> ++#include <asm/mmu_context.h> ++#include <asm/debugreg.h> ++#include <asm/i387.h> ++ ++CO_TRACE_STOP; ++ ++ ++/* ++ * The next asm code is the first Linux code that runs in the ++ * coLinux kernel context. It receives %ecx which contains the ++ * address of the passage page. The passage page code sets %ecx ++ * to this value in its context restore part. ++ */ ++ ++asm( ++ "" ++ ".section .text\n" ++ ".globl co_start\n" ++ "co_start:\n" ++ " call co_start_arch\n" ++ ".previous\n" ++ ""); ++ ++static int co_passage_page_holding_count = 0; ++ ++static void co_early_cpu_init(void) ++{ ++ /* ++ * On the first switch to Linux we must set up a valid TR because ++ * the passage page code assumes such one exists. This is basically ++ * copied code from cpu_init(). ++ * ++ * P.S this is protected by CO_TRACE_STOP so that we don't ++ * have a monitor context switch. ++ */ ++ int cpu = smp_processor_id(); ++ struct tss_struct * t = &per_cpu(init_tss, cpu); ++ struct thread_struct *thread = ¤t->thread; ++ ++ /* ++ * Initialize the per-CPU GDT with the boot GDT, ++ * and set up the GDT descriptor: ++ */ ++ memcpy(&per_cpu(cpu_gdt_table, cpu), cpu_gdt_table, GDT_SIZE); ++ cpu_gdt_descr[cpu].size = GDT_SIZE - 1; ++ cpu_gdt_descr[cpu].address = (unsigned long)&per_cpu(cpu_gdt_table, cpu); ++ ++ /* ++ * Set up the per-thread TLS descriptor cache: ++ */ ++ memcpy(thread->tls_array, &per_cpu(cpu_gdt_table, cpu), GDT_ENTRY_TLS_ENTRIES * 8); ++ ++ __asm__ __volatile__("lgdt %0" : : "m" (cpu_gdt_descr[cpu])); ++ __asm__ __volatile__("lidt %0" : : "m" (idt_descr)); ++ ++ /* ++ * Delete NT ++ */ ++ __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); ++ ++ /* ++ * Set up and load the per-CPU TSS and LDT ++ */ ++ atomic_inc(&init_mm.mm_count); ++ current->active_mm = &init_mm; ++ enter_lazy_tlb(&init_mm, current); ++ ++ load_esp0(t, thread); ++ set_tss_desc(cpu,t); ++ per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS].b &= 0xfffffdff; ++ ++ load_TR_desc(); ++ ++ load_LDT(&init_mm.context); ++ ++ /* Set up doublefault TSS pointer in the GDT */ ++ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); ++ per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_DOUBLEFAULT_TSS].b &= 0xfffffdff; ++ ++ /* Clear %fs and %gs. */ ++ asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); ++ ++ __asm__ __volatile__("movl %%cr4, %0" : "=r" (mmu_cr4_features)); ++} ++ ++asm( ++ "" ++ ".section .text\n" ++ ".globl co_arch_start_kernel\n" ++ "co_arch_start_kernel:\n" ++ " call co_startup_entry\n" ++ ".previous\n" ++ ""); ++ ++void co_start_arch(void) ++{ ++ co_early_cpu_init(); ++ co_start_kernel(); ++} ++ ++extern void ctrl_alt_del(void); ++ ++void co_handle_jiffies(long count) ++{ ++ unsigned long flags; ++ struct pt_regs regs; ++ ++ if (count > HZ) { ++ xtime.tv_sec += count / HZ; ++ count -= ((count / HZ) * HZ); ++ } ++ ++ while (count > 0) { ++ local_irq_save(flags); ++ regs.orig_eax = TIMER_IRQ; ++ do_IRQ(®s); ++ local_irq_restore(flags); ++ ++ count--; ++ } ++} ++ ++void co_handle_incoming_message(co_message_node_t *node_message) ++{ ++ unsigned long flags; ++ struct pt_regs regs; ++ co_linux_message_t *message; ++ ++ message = (co_linux_message_t *)&node_message->msg.data; ++ ++ switch (message->device) { ++ case CO_DEVICE_POWER: { ++ co_linux_message_power_t *type = (co_linux_message_power_t *)message->data; ++ switch (type->type) { ++ case CO_LINUX_MESSAGE_POWER_ALT_CTRL_DEL: { ++ ctrl_alt_del(); ++ break; ++ } ++ } ++ co_free_message(node_message); ++ break; ++ } ++ ++ case CO_DEVICE_KEYBOARD: { ++ co_queue_incoming_message(node_message); ++ ++ local_irq_save(flags); ++ regs.orig_eax = KEYBOARD_IRQ; ++ do_IRQ(®s); ++ local_irq_restore(flags); ++ break; ++ } ++ ++ case CO_DEVICE_NETWORK: { ++ co_queue_incoming_message(node_message); ++ ++ local_irq_save(flags); ++ regs.orig_eax = NETWORK_IRQ; ++ do_IRQ(®s); ++ local_irq_restore(flags); ++ break; ++ } ++ ++ case CO_DEVICE_SERIAL: { ++ co_queue_incoming_message(node_message); ++ ++ local_irq_save(flags); ++ cocd_interrupt(); ++ local_irq_restore(flags); ++ break; ++ } ++ ++ default: ++ co_free_message(node_message); ++ break; ++ } ++} ++ ++void co_switch_wrapper_protected(void) ++{ ++ kernel_fpu_begin(); ++ ++ /* ++ * We don't trust the passage page code to safely restore %gs and %fs. ++ * ++ * This wrapper ensures that if %fs or %gs are invalid, the processes ++ * exits with a segmentation fault rather than bringing down the ++ * machine. ++ **/ ++ unsigned long fs = 0; ++ unsigned long gs = 0; ++ ++ asm volatile("movl %%fs,%0": "=m" (fs)); ++ asm volatile("movl %%gs,%0": "=m" (gs)); ++ ++ /* ++ * Nullify the registers so the passage page code restores to ++ * null segment values on return. ++ */ ++ asm volatile("movl %0, %%fs; movl %0, %%gs" : : "r" (0)); ++ ++ /* And switch... */ ++ co_switch(); ++ ++ /* ++ * Safely restore the registers. ++ */ ++ loadsegment(fs, fs); ++ loadsegment(gs, gs); ++ ++ kernel_fpu_end(); ++} ++ ++void co_switch_wrapper(void) ++{ ++ /* taken from irq.c: debugging check for stack overflow */ ++ long esp; ++ ++ __asm__ __volatile__("andl %%esp,%0" : "=r" (esp) : "0" (THREAD_SIZE - 1)); ++ if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) { ++ printk("co_switch_wrapper: stack overflow: %ld\n", esp - sizeof(struct thread_info)); ++ co_terminate(CO_TERMINATE_STACK_OVERFLOW); ++ } ++ ++ co_switch_wrapper_protected(); ++} ++ ++void co_passage_page_acquire(unsigned long *flags) ++{ ++ local_irq_save(*flags); ++ co_passage_page_holding_count++; ++} ++ ++int co_passage_page_held(void) ++{ ++ return co_passage_page_holding_count; ++} ++ ++void co_passage_page_release(unsigned long flags) ++{ ++ co_passage_page_holding_count--; ++ local_irq_restore(flags); ++} ++ ++void co_debug(const char *fmt, ...) ++{ ++} ++ ++#define MAX_TRACE_POINTS 1024 ++ ++typedef struct { ++ unsigned char *code; ++ unsigned char original_byte; ++ int off; ++} co_tracepoint_t; ++ ++co_tracepoint_t tracepoints[MAX_TRACE_POINTS]; ++static int active_tracepoints = 0; ++ ++void co_kernel_breakpoint(struct pt_regs * regs) ++{ ++ int i = 0; ++ unsigned char *code = (unsigned char *)regs->eip; ++ if (!code) ++ return; ++ ++ for (i=0; i < active_tracepoints; i++) { ++ if (tracepoints[i].code == code - 1) { ++ co_debug("TRACEPOINT: %x\n", code - 1); ++ break; ++ } ++ } ++ ++ if (i == active_tracepoints) { ++ /* Bad, we don't know this tracepoint */ ++ co_terminate(CO_TERMINATE_INVALID_OPERATION); ++ return; ++ } ++ ++ *tracepoints[i].code = tracepoints[i].original_byte; ++ regs->eflags |= (1 << 8); /* Enable TF */ ++ regs->eip = (unsigned long)(code - 1); ++ tracepoints[i].off = 1; ++} ++ ++void co_kernel_set_breakpoints(void) ++{ ++ int i; ++ ++ for (i=0; i < active_tracepoints; i++) ++ if (tracepoints[i].code && tracepoints[i].off) { ++ *tracepoints[i].code = 0xcc; ++ tracepoints[i].off = 0; ++ } ++} ++ ++int co_kernel_debug(struct pt_regs *regs, long error_code, unsigned int condition) ++{ ++ /* if not a single step trap */ ++ if (!(condition & DR_STEP)) ++ return 0; ++ ++ /* if userspace */ ++ if (regs->xcs & 3) ++ return 0; ++ ++ regs->eflags &= ~(1 << 8); /* Disable TF */ ++ ++ co_kernel_set_breakpoints(); ++ ++ return 1; ++} ++ ++void co_kernel_tracepoint_add(unsigned char *code) ++{ ++ if (active_tracepoints >= MAX_TRACE_POINTS) ++ return; ++ ++ tracepoints[active_tracepoints].code = code; ++ tracepoints[active_tracepoints].original_byte = *code; ++ tracepoints[active_tracepoints].off = 0; ++ active_tracepoints++; ++ *code = 0xcc; ++} ++ ++co_arch_info_t co_arch_info = { ++ .kernel_cs = __KERNEL_CS, ++ .kernel_ds = __KERNEL_DS, ++}; ++ ++CO_TRACE_CONTINUE; +diff -urN a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c +--- a/arch/i386/kernel/cpu/common.c ++++ b/arch/i386/kernel/cpu/common.c +@@ -4,6 +4,7 @@ + #include <linux/smp.h> + #include <linux/module.h> + #include <linux/percpu.h> ++#include <linux/cooperative_internal.h> + #include <asm/semaphore.h> + #include <asm/processor.h> + #include <asm/i387.h> +@@ -570,11 +571,13 @@ + + /* Clear all 6 debug registers: */ + ++ if (!cooperative_mode_enabled()) { + #define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) ); + +- CD(0); CD(1); CD(2); CD(3); /* no db4 and db5 */; CD(6); CD(7); ++ CD(0); CD(1); CD(2); CD(3); /* no db4 and db5 */; CD(6); CD(7); + + #undef CD ++ } + + /* + * Force FPU initialization: +diff -urN a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S +--- a/arch/i386/kernel/entry.S ++++ b/arch/i386/kernel/entry.S +@@ -158,7 +158,7 @@ + ALIGN + ret_from_exception: + preempt_stop +-ret_from_intr: ++ENTRY(ret_from_intr) + GET_THREAD_INFO(%ebp) + movl EFLAGS(%esp), %eax # mix EFLAGS and CS + movb CS(%esp), %al +diff -urN a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S +--- a/arch/i386/kernel/head.S ++++ b/arch/i386/kernel/head.S +@@ -238,6 +238,7 @@ + rep + movsl + 1: ++ENTRY(co_startup_entry) + checkCPUtype: + + movl $-1,X86_CPUID # -1 for no CPUID initially +@@ -425,7 +426,7 @@ + .data + + ENTRY(stack_start) +- .long init_thread_union+THREAD_SIZE ++ .long init_thread_union+THREAD_SIZE-100 + .long __BOOT_DS + + ready: .byte 0 +diff -urN a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c +--- a/arch/i386/kernel/i387.c ++++ b/arch/i386/kernel/i387.c +@@ -17,6 +17,7 @@ + #include <asm/user.h> + #include <asm/ptrace.h> + #include <asm/uaccess.h> ++#include <linux/cooperative_internal.h> + + #ifdef CONFIG_MATH_EMULATION + #define HAVE_HWFP (boot_cpu_data.hard_math) +@@ -37,6 +38,10 @@ + if (mask == 0) mask = 0x0000ffbf; + } + mxcsr_feature_mask &= mask; ++ ++ if (cooperative_mode_enabled()) ++ return; ++ + stts(); + } + +@@ -386,6 +391,7 @@ + return err; + } + ++ + /* + * ptrace request handlers. + */ +diff -urN a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c +--- a/arch/i386/kernel/i8259.c ++++ b/arch/i386/kernel/i8259.c +@@ -26,9 +26,89 @@ + #include <asm/i8259.h> + + #include <linux/irq.h> ++#include <linux/cooperative_internal.h> + + #include <io_ports.h> + ++#ifdef CONFIG_COOPERATIVE ++ ++CO_TRACE_STOP; ++ ++void proxy_interrupt_handler(unsigned long interrupt, struct pt_regs regs) ++{ ++ unsigned long flags; ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_FORWARD_INTERRUPT; ++ co_passage_page->params[0] = interrupt + 0x20; ++ co_passage_page->params[1] = regs.eip; ++ co_passage_page->params[2] = (unsigned long)(&((&interrupt)[10])); ++ co_passage_page->host_state.flags &= ~(1 << 9); /* Turn IF off */ ++ co_switch_wrapper(); ++ co_callback(flags); ++} ++ ++CO_TRACE_CONTINUE; ++ ++#define IRQLIST_16(x) \ ++ IRQ(x,0) IRQ(x,1) IRQ(x,2) IRQ(x,3) \ ++ IRQ(x,4) IRQ(x,5) IRQ(x,6) IRQ(x,7) \ ++ IRQ(x,8) IRQ(x,9) IRQ(x,a) IRQ(x,b) \ ++ IRQ(x,c) IRQ(x,d) IRQ(x,e) IRQ(x,f) ++ ++#define IRQLIST_224 \ ++ IRQLIST_16(0x0) IRQLIST_16(0x1) IRQLIST_16(0x2) IRQLIST_16(0x3) \ ++ IRQLIST_16(0x4) IRQLIST_16(0x5) IRQLIST_16(0x6) IRQLIST_16(0x7) \ ++ IRQLIST_16(0x8) IRQLIST_16(0x9) IRQLIST_16(0xa) IRQLIST_16(0xb) \ ++ IRQLIST_16(0xc) IRQLIST_16(0xd) ++ ++#define IRQ(x,y) \ ++ extern asmlinkage void IRQ_proxy_##x##y##_interrupt(void); ++IRQLIST_224; ++#undef IRQ ++ ++#define BIRQ(id) \ ++asm( \ ++ "\n"__ALIGN_STR"\n" \ ++ ".section .text\n" \ ++ ".globl IRQ_proxy_" #id "_interrupt\n" \ ++ "IRQ_proxy_" #id "_interrupt:\n" \ ++ "push %eax\n\t" \ ++ "cld;\n\t" \ ++ "pushl %es;\n\t" \ ++ "pushl %ds;\n\t" \ ++ "pushl %eax;\n\t" \ ++ "pushl %ebp;\n\t" \ ++ "pushl %edi;\n\t" \ ++ "pushl %esi;\n\t" \ ++ "pushl %edx;\n\t" \ ++ "pushl %ecx;\n\t" \ ++ "pushl %ebx;\n\t" \ ++ "movl $123, %edx;\n\t" \ ++ "movl %edx, %ds;\n\t" \ ++ "movl %edx, %es;\n\t" \ ++ "pushl $" #id "\n\t" \ ++ "call proxy_interrupt_handler\n\t" \ ++ "popl %ebx\n\t" \ ++ "jmp ret_from_intr\n" \ ++ ".previous\n" \ ++ ); \ ++ ++#define IRQ(x,y) BIRQ(x##y) ++IRQLIST_224; ++#undef IRQ ++ ++#define IRQ(x,y) &IRQ_proxy_##x##y##_interrupt, ++void (*proxy_interrupt[NR_IRQS])(void) = { ++ IRQLIST_224 ++}; ++#undef IRQ ++ ++#undef IRQLIST_16 ++#undef IRQLIST_224 ++ ++#endif ++ + /* + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. +@@ -364,6 +444,9 @@ + { + int i; + ++ if (cooperative_mode_enabled()) ++ return; ++ + #ifdef CONFIG_X86_LOCAL_APIC + init_bsp_APIC(); + #endif +@@ -388,6 +471,65 @@ + } + } + ++#ifdef CONFIG_X86_UP_COPIC ++ ++/* ++ * Not like you have any other choice other than using ++ * COPIC in Cooperative mode. ++ */ ++ ++static void end_COPIC_irq(unsigned int irq) ++{ ++} ++ ++#define shutdown_COPIC_irq disable_COPIC_irq ++ ++static void mask_and_ack_COPIC(unsigned int irq) ++{ ++} ++ ++static unsigned int startup_COPIC_irq(unsigned int irq) ++{ ++ return 0; ++} ++ ++void disable_COPIC_irq(unsigned int irq) ++{ ++} ++ ++void enable_COPIC_irq(unsigned int irq) ++{ ++} ++ ++static struct hw_interrupt_type co_pic_irq_type = { ++ "CO-PIC", ++ startup_COPIC_irq, ++ shutdown_COPIC_irq, ++ enable_COPIC_irq, ++ disable_COPIC_irq, ++ mask_and_ack_COPIC, ++ end_COPIC_irq, ++ NULL ++}; ++ ++void __init init_COPIC_irqs(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_IRQS; i++) { ++ irq_desc[i].status = IRQ_DISABLED; ++ irq_desc[i].action = 0; ++ irq_desc[i].depth = 1; ++ ++ irq_desc[i].handler = &co_pic_irq_type; ++ } ++ ++} ++ ++#else ++#define init_COPIC_irqs() do {} while (0); ++#endif ++ + void __init init_IRQ(void) + { + int i; +@@ -395,6 +537,22 @@ + /* all the set up before the call gates are initialised */ + pre_intr_init_hook(); + ++ if (cooperative_mode_enabled()) { ++ printk("Setting proxy interrupt vectors\n"); ++ ++ init_COPIC_irqs(); ++ ++ for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { ++ int vector = FIRST_EXTERNAL_VECTOR + i; ++ if (i >= NR_IRQS) ++ break; ++ if (vector != SYSCALL_VECTOR) ++ set_intr_gate(vector, proxy_interrupt[i]); ++ } ++ ++ return; ++ } ++ + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become +diff -urN a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c +--- a/arch/i386/kernel/ioport.c ++++ b/arch/i386/kernel/ioport.c +@@ -15,6 +15,7 @@ + #include <linux/stddef.h> + #include <linux/slab.h> + #include <linux/thread_info.h> ++#include <linux/cooperative_internal.h> + + /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ + static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int extent, int new_value) +@@ -61,6 +62,9 @@ + struct tss_struct * tss; + unsigned long *bitmap; + ++ if (cooperative_mode_enabled()) ++ return -EPERM; ++ + if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) + return -EINVAL; + if (turn_on && !capable(CAP_SYS_RAWIO)) +@@ -133,6 +137,9 @@ + unsigned int level = regs->ebx; + unsigned int old = (regs->eflags >> 12) & 3; + ++ if (cooperative_mode_enabled()) ++ return -EPERM; ++ + if (level > 3) + return -EINVAL; + /* Trying to gain more privileges? */ +diff -urN a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c +--- a/arch/i386/kernel/process.c ++++ b/arch/i386/kernel/process.c +@@ -52,6 +52,7 @@ + + #include <linux/irq.h> + #include <linux/err.h> ++#include <linux/cooperative_internal.h> + + asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + +@@ -147,21 +148,24 @@ + /* endless idle loop with no priority at all */ + while (1) { + while (!need_resched()) { +- void (*idle)(void); +- /* +- * Mark this as an RCU critical section so that +- * synchronize_kernel() in the unload path waits +- * for our completion. +- */ +- rcu_read_lock(); +- idle = pm_idle; ++ void (*idle)(void) = pm_idle; ++ ++ /* ++ * Mark this as an RCU critical section so that ++ * synchronize_kernel() in the unload path waits ++ * for our completion. ++ */ ++ rcu_read_lock(); ++ ++ if (cooperative_mode_enabled()) ++ idle = co_idle_processor; + + if (!idle) + idle = default_idle; + + irq_stat[smp_processor_id()].idle_timestamp = jiffies; + idle(); +- rcu_read_unlock(); ++ rcu_read_unlock(); + } + schedule(); + } +diff -urN a/arch/i386/kernel/reboot.c b/arch/i386/kernel/reboot.c +--- a/arch/i386/kernel/reboot.c ++++ b/arch/i386/kernel/reboot.c +@@ -13,6 +13,7 @@ + #include <asm/uaccess.h> + #include <asm/apic.h> + #include "mach_reboot.h" ++#include <linux/cooperative_internal.h> + + /* + * Power off function, if any +@@ -217,6 +218,11 @@ + { + unsigned long flags; + ++ if (cooperative_mode_enabled()) { ++ co_terminate(CO_TERMINATE_REBOOT); ++ return; ++ } ++ + local_irq_disable(); + + /* Write zero to CMOS register number 0x0f, which the BIOS POST +@@ -332,8 +338,13 @@ + */ + smp_send_stop(); + #endif /* CONFIG_SMP */ +- ++ + lapic_shutdown(); ++ ++ if (cooperative_mode_enabled()) { ++ co_terminate(CO_TERMINATE_REBOOT); ++ return; ++ } + + #ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +@@ -364,12 +375,18 @@ + + void machine_halt(void) + { ++ co_terminate(CO_TERMINATE_HALT); + } + + EXPORT_SYMBOL(machine_halt); + + void machine_power_off(void) + { ++ if (cooperative_mode_enabled()) { ++ co_terminate(CO_TERMINATE_POWEROFF); ++ return; ++ } ++ + lapic_shutdown(); + + if (efi_enabled) +diff -urN a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c +--- a/arch/i386/kernel/setup.c ++++ b/arch/i386/kernel/setup.c +@@ -39,6 +39,7 @@ + #include <linux/efi.h> + #include <linux/init.h> + #include <linux/edd.h> ++#include <linux/cooperative_internal.h> + #include <video/edid.h> + #include <asm/e820.h> + #include <asm/mpspec.h> +@@ -668,8 +669,17 @@ + int len = 0; + int userdef = 0; + +- /* Save unparsed command line copy for /proc/cmdline */ +- saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; ++ if (cooperative_mode_enabled()) { ++ /* ++ * Better to have 'root=/dev/cobd0' here. ++ */ ++ from = co_boot_parameters; ++ snprintf(saved_command_line, COMMAND_LINE_SIZE, "%s", ++ co_boot_parameters); ++ } else { ++ /* Save unparsed command line copy for /proc/cmdline */ ++ saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; ++ } + + for (;;) { + /* +@@ -1019,6 +1029,8 @@ + static unsigned long __init setup_memory(void) + { + unsigned long bootmap_size, start_pfn, max_low_pfn; ++ extern char _end; ++ unsigned long start_va = 0; + + /* + * partially used pages are not usable - thus +@@ -1026,9 +1038,16 @@ + */ + start_pfn = PFN_UP(init_pg_tables_end); + +- find_max_pfn(); +- +- max_low_pfn = find_max_low_pfn(); ++ if (cooperative_mode_enabled()) { ++ max_low_pfn = max_pfn = co_memory_size / PAGE_SIZE; ++ start_pfn = PFN_UP(__pa((unsigned long)&_end)) + 0x10; ++ start_va = (unsigned long)__va(start_pfn << PAGE_SHIFT); ++ co_alloc_pages(start_va, 0x20); ++ } else { ++ find_max_pfn(); ++ ++ max_low_pfn = find_max_low_pfn(); ++ } + + #ifdef CONFIG_HIGHMEM + highstart_pfn = highend_pfn = max_pfn; +@@ -1040,37 +1059,47 @@ + #endif + printk(KERN_NOTICE "%ldMB LOWMEM available.\n", + pages_to_mb(max_low_pfn)); +- /* +- * Initialize the boot-time allocator (with low memory only): +- */ +- bootmap_size = init_bootmem(start_pfn, max_low_pfn); + +- register_bootmem_low_pages(max_low_pfn); + + /* +- * Reserve the bootmem bitmap itself as well. We do this in two +- * steps (first step was init_bootmem()) because this catches +- * the (very unlikely) case of us accidentally initializing the +- * bootmem allocator with an invalid RAM area. ++ * Initialize the boot-time allocator (with low memory only): + */ +- reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + +- bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)); ++ bootmap_size = init_bootmem(start_pfn, max_low_pfn); + +- /* +- * reserve physical page 0 - it's a special BIOS page on many boxes, +- * enabling clean reboots, SMP operation, laptop functions. +- */ +- reserve_bootmem(0, PAGE_SIZE); + +- /* reserve EBDA region, it's a 4K region */ +- reserve_ebda_region(); ++ if (cooperative_mode_enabled()) { ++ unsigned long bootmem_end = start_va + bootmap_size + (0x10 << PAGE_SHIFT); ++ unsigned long physical_end = __PAGE_OFFSET + (max_low_pfn << PAGE_SHIFT); ++ ++ free_bootmem(__pa(bootmem_end), physical_end - bootmem_end); ++ } else { ++ register_bootmem_low_pages(max_low_pfn); ++ ++ /* ++ * Reserve the bootmem bitmap itself as well. We do this in two ++ * steps (first step was init_bootmem()) because this catches ++ * the (very unlikely) case of us accidentally initializing the ++ * bootmem allocator with an invalid RAM area. ++ */ ++ reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + ++ bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)); ++ ++ /* ++ * reserve physical page 0 - it's a special BIOS page on many boxes, ++ * enabling clean reboots, SMP operation, laptop functions. ++ */ ++ reserve_bootmem(0, PAGE_SIZE); + +- /* could be an AMD 768MPX chipset. Reserve a page before VGA to prevent +- PCI prefetch into it (errata #56). Usually the page is reserved anyways, +- unless you have no PS/2 mouse plugged in. */ +- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && +- boot_cpu_data.x86 == 6) +- reserve_bootmem(0xa0000 - 4096, 4096); ++ /* reserve EBDA region, it's a 4K region */ ++ reserve_ebda_region(); ++ ++ /* could be an AMD 768MPX chipset. Reserve a page before VGA to prevent ++ PCI prefetch into it (errata #56). Usually the page is reserved anyways, ++ unless you have no PS/2 mouse plugged in. */ ++ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && ++ boot_cpu_data.x86 == 6) ++ reserve_bootmem(0xa0000 - 4096, 4096); ++ } + + #ifdef CONFIG_SMP + /* +@@ -1094,6 +1123,7 @@ + #endif + + #ifdef CONFIG_BLK_DEV_INITRD ++#ifndef CONFIG_COOPERATIVE + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE); +@@ -1109,6 +1139,17 @@ + initrd_start = 0; + } + } ++#else ++ if (co_initrd != NULL) { ++ printk(KERN_INFO "initrd enabled: start: 0x%x size: 0x%08lx)\n", ++ (unsigned int)co_initrd, (long unsigned int)co_initrd_size); ++ ++ initrd_start = (unsigned long)co_initrd; ++ initrd_end = (unsigned long)co_initrd + co_initrd_size; ++ ++ reserve_bootmem(virt_to_phys(co_initrd), co_initrd_size); ++ } ++#endif + #endif + return max_low_pfn; + } +@@ -1315,6 +1356,7 @@ + efi_enabled = 1; + #endif + ++ boot_cpu_data.hard_math = 1; + ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; +@@ -1338,7 +1380,7 @@ + ARCH_SETUP + if (efi_enabled) + efi_init(); +- else { ++ else if (!cooperative_mode_enabled()) { + printk(KERN_INFO "BIOS-provided physical RAM map:\n"); + print_memory_map(machine_specific_memory_setup()); + } +@@ -1392,8 +1434,9 @@ + } + #endif + +- +- dmi_scan_machine(); ++ if (!cooperative_mode_enabled()) { ++ dmi_scan_machine(); ++ } + + #ifdef CONFIG_X86_GENERICARCH + generic_apic_probe(*cmdline_p); +@@ -1411,9 +1454,14 @@ + get_smp_config(); + #endif + +- register_memory(max_low_pfn); ++ if (!cooperative_mode_enabled()) { ++ register_memory(max_low_pfn); ++ } + + #ifdef CONFIG_VT ++#ifdef CONFIG_COOPERATIVE_CONSOLE ++ conswitchp = &colinux_con; ++#else + #if defined(CONFIG_VGA_CONSOLE) + if (!efi_enabled || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY)) + conswitchp = &vga_con; +@@ -1421,6 +1469,7 @@ + conswitchp = &dummy_con; + #endif + #endif ++#endif + } + + #include "setup_arch_post.h" +diff -urN a/arch/i386/kernel/sysenter.c b/arch/i386/kernel/sysenter.c +--- a/arch/i386/kernel/sysenter.c ++++ b/arch/i386/kernel/sysenter.c +@@ -13,6 +13,7 @@ + #include <linux/gfp.h> + #include <linux/string.h> + #include <linux/elf.h> ++#include <linux/cooperative_internal.h> + + #include <asm/cpufeature.h> + #include <asm/msr.h> +@@ -43,11 +44,11 @@ + + static int __init sysenter_setup(void) + { +- void *page = (void *)get_zeroed_page(GFP_ATOMIC); ++ void *page = get_zeroed_page(GFP_ATOMIC); + + __set_fixmap(FIX_VSYSCALL, __pa(page), PAGE_READONLY_EXEC); + +- if (!boot_cpu_has(X86_FEATURE_SEP)) { ++ if (cooperative_mode_enabled() || !boot_cpu_has(X86_FEATURE_SEP)) { + memcpy(page, + &vsyscall_int80_start, + &vsyscall_int80_end - &vsyscall_int80_start); +@@ -59,6 +60,7 @@ + &vsyscall_sysenter_end - &vsyscall_sysenter_start); + + on_each_cpu(enable_sep_cpu, NULL, 1, 1); ++ + return 0; + } + +diff -urN a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c +--- a/arch/i386/kernel/time.c ++++ b/arch/i386/kernel/time.c +@@ -45,6 +45,7 @@ + #include <linux/sysdev.h> + #include <linux/bcd.h> + #include <linux/efi.h> ++#include <linux/cooperative_internal.h> + + #include <asm/io.h> + #include <asm/smp.h> +@@ -94,8 +95,9 @@ + void do_gettimeofday(struct timeval *tv) + { + unsigned long seq; +- unsigned long usec, sec; +- unsigned long max_ntp_tick; ++ unsigned long sec; ++ long usec; ++ long max_ntp_tick; + + do { + unsigned long lost; +@@ -129,6 +131,13 @@ + sec++; + } + ++ if (cooperative_mode_enabled()) { ++ while (usec < 0) { ++ usec += 1000000; ++ sec--; ++ } ++ } ++ + tv->tv_sec = sec; + tv->tv_usec = usec; + } +@@ -174,6 +183,9 @@ + { + int retval; + ++ if (cooperative_mode_enabled()) ++ return -1; ++ + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + if (efi_enabled) +@@ -243,7 +255,8 @@ + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ +- if ((time_status & STA_UNSYNC) == 0 && ++ if (!cooperative_mode_enabled() && ++ (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) + >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 && +@@ -307,6 +320,9 @@ + { + unsigned long retval; + ++ if (cooperative_mode_enabled()) ++ return co_get_host_time(); ++ + spin_lock(&rtc_lock); + + if (efi_enabled) +diff -urN a/arch/i386/kernel/timers/Makefile b/arch/i386/kernel/timers/Makefile +--- a/arch/i386/kernel/timers/Makefile ++++ b/arch/i386/kernel/timers/Makefile +@@ -7,3 +7,4 @@ + obj-$(CONFIG_X86_CYCLONE_TIMER) += timer_cyclone.o + obj-$(CONFIG_HPET_TIMER) += timer_hpet.o + obj-$(CONFIG_X86_PM_TIMER) += timer_pm.o ++obj-$(CONFIG_COOPERATIVE) += timer_cooperative.o +diff -urN a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c +--- a/arch/i386/kernel/timers/timer.c ++++ b/arch/i386/kernel/timers/timer.c +@@ -13,6 +13,9 @@ + #endif + /* list of timers, ordered by preference, NULL terminated */ + static struct init_timer_opts* __initdata timers[] = { ++#ifdef CONFIG_COOPERATIVE ++ &timer_cooperative_init, ++#endif + #ifdef CONFIG_X86_CYCLONE_TIMER + &timer_cyclone_init, + #endif +diff -urN a/arch/i386/kernel/timers/timer_cooperative.c b/arch/i386/kernel/timers/timer_cooperative.c +--- a/arch/i386/kernel/timers/timer_cooperative.c ++++ b/arch/i386/kernel/timers/timer_cooperative.c +@@ -0,0 +1,140 @@ ++/* ++ * Cooperative mode timer. ++ * ++ * Dan Aloni <da-x@colinux.org>, 2003-2004 (C). ++ */ ++ ++#include <linux/init.h> ++#include <linux/errno.h> ++ ++#include <asm/timer.h> ++#include <asm/cooperative.h> ++#include <asm/div64.h> ++#include <asm/param.h> ++ ++#include <linux/cooperative.h> ++#include <linux/cooperative_internal.h> ++ ++static unsigned long long first_time; ++static unsigned long frequencey; ++static unsigned long long last_mark, last_mark_quotient; ++static unsigned long long last_time; ++ ++static unsigned long long query_host_highprec_time(void) ++{ ++ unsigned long flags; ++ unsigned long long this_time; ++ unsigned long long diff; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_GET_HIGH_PREC_TIME; ++ co_switch_wrapper(); ++ ++ this_time = *(unsigned long long *)(&co_passage_page->params[0]); ++ frequencey = *(unsigned long *)(&co_passage_page->params[2]); ++ diff = ((long long)this_time - (long long)last_time); ++ ++ /* ++ * There shouldn't be any particularly large difference between ++ * the current and last host timestamps. For sanity, reset the ++ * global reference variables if we encounter any difference ++ * larger than one second. ++ */ ++ ++ if (diff < 0 || diff > frequencey) { ++ first_time = this_time; ++ last_mark_quotient = last_mark = 0; ++ } ++ ++ last_time = this_time; ++ co_passage_page_release(flags); ++ ++ return this_time; ++} ++ ++static unsigned long long monotonic_clock_cooperative(void) ++{ ++ return 0; ++} ++ ++static long get_offset_cooperative(void) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ unsigned long long this_time = query_host_highprec_time() - first_time; ++ unsigned long reminder = 0, result; ++ long long diff, lldiff; ++ long signed_result; ++ ++ diff = ((long long)this_time - (long long)(last_mark)); ++ if (diff < 0) ++ lldiff = -diff; ++ else ++ lldiff = diff; ++ ++ lldiff *= 1000000; ++ result = div_ll_X_l_rem(lldiff, frequencey, &reminder); ++ ++ signed_result = result; ++ if (diff < 0) ++ signed_result = -signed_result; ++ ++ local_irq_restore(flags); ++ return signed_result; ++} ++ ++static void mark_offset_cooperative(void) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ last_mark += frequencey / HZ; ++ last_mark_quotient += frequencey % HZ; ++ if (frequencey > HZ) { ++ last_mark += 1; ++ last_mark_quotient -= HZ; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++static void delay_cooperative(unsigned long loops) ++{ ++ /* ++ * A bogos delay loop for creating BogoMIPS... ++ */ ++ ++ loops = loops / 10000; ++ while (loops) { ++ query_host_highprec_time(); ++ loops -= 1; ++ } ++} ++ ++static int __init init_cooperative_timer(char* override) ++{ ++ first_time = query_host_highprec_time(); ++ ++ /* Always pick this timer */ ++ return 0; ++} ++ ++/************************************************************/ ++ ++/* tsc timer_opts struct */ ++struct timer_opts timer_cooperative = { ++ .name = "cooperative", ++ .mark_offset = mark_offset_cooperative, ++ .get_offset = get_offset_cooperative, ++ .monotonic_clock = monotonic_clock_cooperative, ++ .delay = delay_cooperative, ++}; ++ ++struct init_timer_opts __initdata timer_cooperative_init = { ++ .init = init_cooperative_timer, ++ .opts = &timer_cooperative, ++}; +diff -urN a/arch/i386/kernel/timers/timer_cyclone.c b/arch/i386/kernel/timers/timer_cyclone.c +--- a/arch/i386/kernel/timers/timer_cyclone.c ++++ b/arch/i386/kernel/timers/timer_cyclone.c +@@ -103,7 +103,7 @@ + jiffies_64++; + } + +-static unsigned long get_offset_cyclone(void) ++static long get_offset_cyclone(void) + { + u32 offset; + +diff -urN a/arch/i386/kernel/timers/timer_hpet.c b/arch/i386/kernel/timers/timer_hpet.c +--- a/arch/i386/kernel/timers/timer_hpet.c ++++ b/arch/i386/kernel/timers/timer_hpet.c +@@ -73,7 +73,7 @@ + return base + cycles_2_ns(this_offset - last_offset); + } + +-static unsigned long get_offset_hpet(void) ++static long get_offset_hpet(void) + { + register unsigned long eax, edx; + +diff -urN a/arch/i386/kernel/timers/timer_none.c b/arch/i386/kernel/timers/timer_none.c +--- a/arch/i386/kernel/timers/timer_none.c ++++ b/arch/i386/kernel/timers/timer_none.c +@@ -6,7 +6,7 @@ + /* nothing needed */ + } + +-static unsigned long get_offset_none(void) ++static long get_offset_none(void) + { + return 0; + } +diff -urN a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c +--- a/arch/i386/kernel/timers/timer_pit.c ++++ b/arch/i386/kernel/timers/timer_pit.c +@@ -89,7 +89,7 @@ + * comp.protocols.time.ntp! + */ + +-static unsigned long get_offset_pit(void) ++static long get_offset_pit(void) + { + int count; + unsigned long flags; +diff -urN a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c +--- a/arch/i386/kernel/timers/timer_pm.c ++++ b/arch/i386/kernel/timers/timer_pm.c +@@ -227,7 +227,7 @@ + * get the offset (in microseconds) from the last call to mark_offset() + * - Called holding a reader xtime_lock + */ +-static unsigned long get_offset_pmtmr(void) ++static long get_offset_pmtmr(void) + { + u32 now, offset, delta = 0; + +diff -urN a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c +--- a/arch/i386/kernel/timers/timer_tsc.c ++++ b/arch/i386/kernel/timers/timer_tsc.c +@@ -83,7 +83,7 @@ + */ + static unsigned long fast_gettimeoffset_quotient; + +-static unsigned long get_offset_tsc(void) ++static long get_offset_tsc(void) + { + register unsigned long eax, edx; + +diff -urN a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c +--- a/arch/i386/kernel/traps.c ++++ b/arch/i386/kernel/traps.c +@@ -51,6 +51,9 @@ + #include <asm/arch_hooks.h> + #include <asm/kdebug.h> + ++#include <linux/cooperative_internal.h> ++#include <asm/cooperative_internal.h> ++ + #include <linux/irq.h> + #include <linux/module.h> + +@@ -382,6 +385,12 @@ + } + + kernel_trap: { ++ if (cooperative_mode_enabled()) { ++ if (trapnr == 3) { ++ co_kernel_breakpoint(regs); ++ return; ++ } ++ } + if (!fixup_exception(regs)) + die(str, regs, error_code); + return; +@@ -683,9 +692,15 @@ + unsigned int condition; + struct task_struct *tsk = current; + siginfo_t info; +- ++ + __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + ++ if (cooperative_mode_enabled() && ++ co_kernel_debug(regs, error_code, condition)) ++ { ++ return; ++ } ++ + if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, + SIGTRAP) == NOTIFY_STOP) + return; +diff -urN a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S +--- a/arch/i386/kernel/vmlinux.lds.S ++++ b/arch/i386/kernel/vmlinux.lds.S +@@ -14,11 +14,12 @@ + { + . = __PAGE_OFFSET + 0x100000; + /* read-only */ ++ _kernel_start = . ; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + SCHED_TEXT +- LOCK_TEXT ++ LOCK_TEXT + *(.fixup) + *(.gnu.warning) + } = 0x9090 +diff -urN a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c +--- a/arch/i386/mm/fault.c ++++ b/arch/i386/mm/fault.c +@@ -449,7 +449,8 @@ + printk(KERN_ALERT " printing eip:\n"); + printk("%08lx\n", regs->eip); + asm("movl %%cr3,%0":"=r" (page)); +- page = ((unsigned long *) __va(page))[address >> 22]; ++ page = ((unsigned long *) __va(CO_P_TO_PP(page)))[address >> 22]; ++ page = CO_P_TO_PP(page); + printk(KERN_ALERT "*pde = %08lx\n", page); + /* + * We must not directly access the pte in the highpte +@@ -462,6 +463,7 @@ + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; ++ page = CO_P_TO_PP(page); + printk(KERN_ALERT "*pte = %08lx\n", page); + } + #endif +@@ -522,7 +524,7 @@ + pte_t *pte_k; + + asm("movl %%cr3,%0":"=r" (pgd_paddr)); +- pgd = index + (pgd_t *)__va(pgd_paddr); ++ pgd = index + (pgd_t *)__va(CO_P_TO_PP((unsigned long)pgd_paddr)); + pgd_k = init_mm.pgd + index; + + if (!pgd_present(*pgd_k)) +diff -urN a/arch/i386/mm/init.c b/arch/i386/mm/init.c +--- a/arch/i386/mm/init.c ++++ b/arch/i386/mm/init.c +@@ -27,6 +27,7 @@ + #include <linux/slab.h> + #include <linux/proc_fs.h> + #include <linux/efi.h> ++#include <linux/cooperative_internal.h> + + #include <asm/processor.h> + #include <asm/system.h> +@@ -76,7 +77,7 @@ + { + if (pmd_none(*pmd)) { + pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); +- set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); ++ set_pmd(pmd, __pmd(CO_PP_TO_P(__pa(page_table)) | _PAGE_TABLE)); + if (page_table != pte_offset_kernel(pmd, 0)) + BUG(); + +@@ -313,21 +314,23 @@ + set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT)); + #endif + +- /* Enable PSE if available */ +- if (cpu_has_pse) { +- set_in_cr4(X86_CR4_PSE); +- } +- +- /* Enable PGE if available */ +- if (cpu_has_pge) { +- set_in_cr4(X86_CR4_PGE); +- __PAGE_KERNEL |= _PAGE_GLOBAL; +- __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL; ++ if (!cooperative_mode_enabled()) { ++ /* Enable PSE if available */ ++ if (cpu_has_pse) { ++ set_in_cr4(X86_CR4_PSE); ++ } ++ ++ /* Enable PGE if available */ ++ if (cpu_has_pge) { ++ set_in_cr4(X86_CR4_PGE); ++ __PAGE_KERNEL |= _PAGE_GLOBAL; ++ __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL; ++ } ++ ++ kernel_physical_mapping_init(pgd_base); ++ remap_numa_kva(); + } + +- kernel_physical_mapping_init(pgd_base); +- remap_numa_kva(); +- + /* + * Fixed mappings, only the page table structure has to be + * created - mappings will be set by set_fixmap(): +@@ -394,19 +397,26 @@ + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned int max_dma, high, low; + +- max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; +- low = max_low_pfn; +- high = highend_pfn; +- +- if (low < max_dma) +- zones_size[ZONE_DMA] = low; +- else { +- zones_size[ZONE_DMA] = max_dma; +- zones_size[ZONE_NORMAL] = low - max_dma; ++ if (!cooperative_mode_enabled()) { ++ max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; ++ low = max_low_pfn; ++ high = highend_pfn; ++ ++ if (low < max_dma) ++ zones_size[ZONE_DMA] = low; ++ else { ++ zones_size[ZONE_DMA] = max_dma; ++ zones_size[ZONE_NORMAL] = low - max_dma; + #ifdef CONFIG_HIGHMEM +- zones_size[ZONE_HIGHMEM] = high - low; ++ zones_size[ZONE_HIGHMEM] = high - low; + #endif ++ } ++ } else { ++ zones_size[ZONE_DMA] = 0; ++ zones_size[ZONE_NORMAL] = max_low_pfn; ++ zones_size[ZONE_HIGHMEM] = 0; + } ++ + free_area_init(zones_size); + } + #else +@@ -574,7 +584,6 @@ + if (!mem_map) + BUG(); + #endif +- + bad_ppro = ppro_with_ram_bug(); + + #ifdef CONFIG_HIGHMEM +@@ -630,8 +639,10 @@ + if (!cpu_has_pae) + panic("cannot execute a PAE-enabled kernel on a PAE-less CPU!"); + #endif +- if (boot_cpu_data.wp_works_ok < 0) +- test_wp_bit(); ++ if (!cooperative_mode_enabled()) { ++ if (boot_cpu_data.wp_works_ok < 0) ++ test_wp_bit(); ++ } + + /* + * Subtle. SMP is doing it's boot stuff late (because it has to +diff -urN a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c +--- a/arch/i386/mm/ioremap.c ++++ b/arch/i386/mm/ioremap.c +@@ -11,6 +11,7 @@ + #include <linux/vmalloc.h> + #include <linux/init.h> + #include <linux/slab.h> ++#include <linux/cooperative_internal.h> + #include <asm/io.h> + #include <asm/fixmap.h> + #include <asm/cacheflush.h> +@@ -190,7 +191,14 @@ + void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size) + { + unsigned long last_addr; +- void __iomem *p = __ioremap(phys_addr, size, _PAGE_PCD); ++ void __iomem *p; ++ ++ if (cooperative_mode_enabled()) { ++ panic("ioremap_nocache %ld:%ld\n", phys_addr, size); ++ return NULL; ++ } ++ ++ p = __ioremap(phys_addr, size, _PAGE_PCD); + if (!p) + return p; + +diff -urN a/drivers/block/Kconfig b/drivers/block/Kconfig +--- a/drivers/block/Kconfig ++++ b/drivers/block/Kconfig +@@ -358,6 +358,15 @@ + "real" root file system, etc. See <file:Documentation/initrd.txt> + for details. + ++config BLK_DEV_COBD ++ tristate 'Cooperative block device support' ++ default y ++ depends on COOPERATIVE=y ++ help ++ Virtual block device support for cooperative kernels. ++ ++ If unsure, say Y. ++ + config INITRAMFS_SOURCE + string "Source directory of cpio_list" + default "" +diff -urN a/drivers/block/Makefile b/drivers/block/Makefile +--- a/drivers/block/Makefile ++++ b/drivers/block/Makefile +@@ -29,6 +29,7 @@ + obj-$(CONFIG_ATARI_SLM) += acsi_slm.o + obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o + obj-$(CONFIG_BLK_DEV_RAM) += rd.o ++obj-$(CONFIG_BLK_DEV_COBD) += cobd.o + obj-$(CONFIG_BLK_DEV_LOOP) += loop.o + obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o + obj-$(CONFIG_BLK_DEV_XD) += xd.o +diff -urN a/drivers/block/cobd.c b/drivers/block/cobd.c +--- a/drivers/block/cobd.c ++++ b/drivers/block/cobd.c +@@ -0,0 +1,540 @@ ++/* ++ * Copyright (C) 2003 Dan Aloni <da-x@colinux.org> ++ * ++ * Cooperative Linux Block Device implementation ++ */ ++ ++#include <linux/major.h> ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/major.h> ++#include <linux/stat.h> ++#include <linux/slab.h> ++#include <linux/bio.h> ++#include <linux/blkdev.h> ++#include <linux/cooperative_internal.h> ++#include <linux/file.h> ++#include <linux/ioctl.h> ++#include <linux/ctype.h> ++ ++#include <asm/uaccess.h> ++#include <asm/types.h> ++ ++#include <linux/devfs_fs_kernel.h> ++ ++#define PBD_BLOCK_SIZE 512 ++ ++static int hardsect_size = 512; ++static int hardsect_size_shift = 9; ++static spinlock_t cobd_lock = SPIN_LOCK_UNLOCKED; ++static int cobd_max; ++ ++struct cobd_device { ++ int unit; ++ int refcount; ++ struct block_device *device; ++}; ++ ++static int cobd_request(struct cobd_device *cobd, co_block_request_type_t type, co_block_request_t *out_request) ++{ ++ co_block_request_t *request; ++ unsigned long flags; ++ long rc = 0; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_BLOCK; ++ co_passage_page->params[1] = cobd->unit; ++ request = (co_block_request_t *)&co_passage_page->params[2]; ++ request->type = type; ++ request->rc = -1; ++ co_switch_wrapper(); ++ rc = request->rc; ++ *out_request = *request; ++ co_passage_page_release(flags); ++ ++ return rc; ++} ++ ++static int cobd_stat(struct cobd_device *cobd, co_block_request_t *out_request) ++{ ++ return cobd_request(cobd, CO_BLOCK_STAT, out_request); ++} ++ ++static int cobd_get_alias(struct cobd_device *cobd, co_block_request_t *out_request) ++{ ++ return cobd_request(cobd, CO_BLOCK_GET_ALIAS, out_request); ++} ++ ++static int cobd_ioctl(struct inode * inode, struct file * file, ++ unsigned int cmd, unsigned long arg) ++{ ++ return -ENOTTY; /* unknown command */ ++} ++ ++static int cobd_open(struct inode *inode, struct file *file) ++{ ++ struct cobd_device *cobd = (struct cobd_device *)(inode->i_bdev->bd_disk->private_data); ++ co_block_request_t *co_request; ++ co_block_request_t stat_request; ++ unsigned long flags; ++ int result; ++ ++ if (cobd->device && cobd->device != inode->i_bdev) ++ return -EBUSY; ++ ++ if (cobd_stat(cobd, &stat_request)) ++ return -ENODEV; ++ ++ result = 0; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_BLOCK; ++ co_passage_page->params[1] = cobd->unit; ++ co_request = (co_block_request_t *)&co_passage_page->params[2]; ++ co_request->type = CO_BLOCK_OPEN; ++ co_switch_wrapper(); ++ if (co_request->rc) ++ result = -EIO; ++ else ++ cobd->refcount++; ++ co_passage_page_release(flags); ++ ++ if (result) ++ return result; ++ ++ if (cobd->refcount == 1) { ++ set_capacity(inode->i_bdev->bd_disk, stat_request.disk_size >> 9); ++ cobd->device = inode->i_bdev; ++ } ++ ++ return 0; ++} ++ ++static int cobd_release(struct inode *inode, struct file *file) ++{ ++ struct cobd_device *cobd = (struct cobd_device *)(inode->i_bdev->bd_disk->private_data); ++ co_block_request_t *co_request; ++ unsigned long flags; ++ int ret = 0; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_BLOCK; ++ co_passage_page->params[1] = cobd->unit; ++ co_request = (co_block_request_t *)&co_passage_page->params[2]; ++ co_request->type = CO_BLOCK_CLOSE; ++ co_switch_wrapper(); ++ if (co_request->rc) ++ ret = -EIO; ++ cobd->refcount--; ++ co_passage_page_release(flags); ++ ++ if (cobd->refcount == 0) ++ cobd->device = NULL; ++ ++ return ret; ++} ++ ++/* ++ * Handle an I/O request. ++ */ ++static int cobd_transfer(struct cobd_device *cobd, unsigned long sector, ++ unsigned long nsect, char *buffer, int write) ++{ ++ co_block_request_t *co_request; ++ unsigned long flags; ++ int ret = 0; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_BLOCK; ++ co_passage_page->params[1] = cobd->unit; ++ co_request = (co_block_request_t *)&co_passage_page->params[2]; ++ if (!write) ++ co_request->type = CO_BLOCK_READ; ++ else ++ co_request->type = CO_BLOCK_WRITE; ++ co_request->offset = ((unsigned long long)sector) << hardsect_size_shift; ++ co_request->size = nsect << hardsect_size_shift; ++ co_request->address = buffer; ++ co_request->rc = 0; ++ co_switch_wrapper(); ++ ++ if (!co_request->rc) ++ ret = 1; ++ ++ co_passage_page_release(flags); ++ return ret; ++} ++ ++static void do_cobd_request(request_queue_t *q) ++{ ++ struct request *req; ++ struct cobd_device *cobd; ++ ++ while ((req = elv_next_request(q)) != NULL) { ++ int ret; ++ ++ if (!blk_fs_request(req)) { ++ end_request(req, 0); ++ continue; ++ } ++ cobd = (struct cobd_device *)(req->rq_disk->private_data); ++ ++ ret = cobd_transfer(cobd, req->sector, req->current_nr_sectors, ++ req->buffer, rq_data_dir(req)); ++ end_request(req, ret); ++ } ++} ++ ++static struct block_device_operations cobd_fops = { ++ .owner = THIS_MODULE, ++ .open = cobd_open, ++ .release = cobd_release, ++ .ioctl = cobd_ioctl, ++}; ++ ++static struct gendisk **cobd_disks; ++ ++static struct cobd_device cobd_devs[CO_MODULE_MAX_COBD]; ++ ++static int __init cobd_drives_init(void) ++{ ++ int result, i; ++ ++ if (register_blkdev(COLINUX_MAJOR, "cobd")) { ++ printk(KERN_WARNING "Unable to get major number %d for cobd device\n", COLINUX_MAJOR); ++ return -EIO; ++ } ++ ++ cobd_max = CO_MODULE_MAX_COBD; ++ ++ result = -ENOMEM; /* for the possible errors */ ++ ++ cobd_disks = kmalloc(cobd_max * sizeof(struct gendisk *), GFP_KERNEL); ++ if (!cobd_disks) ++ goto fail_malloc; ++ ++ for (i=0; i < cobd_max; i++) { ++ cobd_disks[i] = alloc_disk(1); ++ if (!cobd_disks[i]) ++ goto fail_malloc3; ++ } ++ ++ for (i=0; i < cobd_max; i++) { ++ struct cobd_device *cobd = &cobd_devs[i]; ++ struct gendisk *disk = cobd_disks[i]; ++ ++ disk->queue = blk_init_queue(do_cobd_request, &cobd_lock); ++ if (!disk->queue) ++ goto fail_malloc4; ++ ++ blk_queue_hardsect_size(disk->queue, hardsect_size); ++ ++ cobd->unit = i; ++ disk->major = COLINUX_MAJOR; ++ disk->first_minor = i; ++ disk->fops = &cobd_fops; ++ sprintf(disk->disk_name, "cobd%d", i); ++ sprintf(disk->devfs_name, "cobd/%d", i); ++ disk->private_data = cobd; ++ } ++ ++ devfs_mk_dir("cobd"); ++ ++ for (i=0; i < cobd_max; i++) ++ add_disk(cobd_disks[i]); ++ ++ printk(KERN_INFO "cobd: loaded (max %d devices)\n", cobd_max); ++ return 0; ++ ++/* error path */ ++fail_malloc4: ++ while (i--) ++ blk_cleanup_queue(cobd_disks[i]->queue); ++ devfs_remove("cobd"); ++ i = cobd_max; ++ ++fail_malloc3: ++ while (i--) ++ if (cobd_disks[i] != NULL) ++ put_disk(cobd_disks[i]); ++ ++ kfree(cobd_disks); ++ ++fail_malloc: ++ if (unregister_blkdev(COLINUX_MAJOR, "cobd")) ++ printk(KERN_WARNING "cobd: cannot unregister blkdev\n"); ++ ++ return result; ++} ++ ++struct cobd_alias_major { ++ const char *name; ++ int registered; ++ int number; ++}; ++ ++struct cobd_alias { ++ const char *name; ++ struct cobd_alias_major *major; ++ int minor_start; ++ int minor_count; ++ struct gendisk **gendisk; ++}; ++ ++struct cobd_alias_major cobd_aliases_major_ide0 = { ++ .name = "ide0", ++ .number = IDE0_MAJOR, ++}; ++ ++struct cobd_alias_major cobd_aliases_major_ide1 = { ++ .name = "ide1", ++ .number = IDE1_MAJOR, ++}; ++ ++struct cobd_alias_major cobd_aliases_major_ide2 = { ++ .name = "ide2", ++ .number = IDE2_MAJOR, ++}; ++ ++struct cobd_alias_major cobd_aliases_major_ide3 = { ++ .name = "ide3", ++ .number = IDE3_MAJOR, ++}; ++ ++struct cobd_alias_major cobd_aliases_major_sd = { ++ .name = "sd", ++ .number = SCSI_DISK0_MAJOR, ++}; ++ ++struct cobd_alias cobd_aliases[] = { ++ {"hda", &cobd_aliases_major_ide0, 0x00, 21, }, ++ {"hdb", &cobd_aliases_major_ide0, 0x40, 21, }, ++ {"hdc", &cobd_aliases_major_ide1, 0x00, 21, }, ++ {"hdd", &cobd_aliases_major_ide1, 0x40, 21, }, ++ {"hde", &cobd_aliases_major_ide2, 0x00, 21, }, ++ {"hdf", &cobd_aliases_major_ide2, 0x40, 21, }, ++ {"hdg", &cobd_aliases_major_ide3, 0x00, 21, }, ++ {"hdh", &cobd_aliases_major_ide3, 0x40, 21, }, ++ {"sda", &cobd_aliases_major_sd, 0x00, 0x10, }, ++ {"sdb", &cobd_aliases_major_sd, 0x10, 0x10, }, ++ {"sdc", &cobd_aliases_major_sd, 0x20, 0x10, }, ++ {"sdd", &cobd_aliases_major_sd, 0x30, 0x10, }, ++ {"sde", &cobd_aliases_major_sd, 0x40, 0x10, }, ++ {"sdf", &cobd_aliases_major_sd, 0x50, 0x10, }, ++ {"sdg", &cobd_aliases_major_sd, 0x60, 0x10, }, ++ {"sdh", &cobd_aliases_major_sd, 0x70, 0x10, }, ++ {"sdi", &cobd_aliases_major_sd, 0x80, 0x10, }, ++ {"sdj", &cobd_aliases_major_sd, 0x90, 0x10, }, ++ {"sdk", &cobd_aliases_major_sd, 0xa0, 0x10, }, ++ {"sdl", &cobd_aliases_major_sd, 0xb0, 0x10, }, ++ {"sdm", &cobd_aliases_major_sd, 0xc0, 0x10, }, ++ {"sdn", &cobd_aliases_major_sd, 0xd0, 0x10, }, ++ {"sdp", &cobd_aliases_major_sd, 0xe0, 0x10, }, ++ {"sdq", &cobd_aliases_major_sd, 0xf0, 0x10, }, ++ {NULL, }, ++}; ++ ++static int __init skip_atoi(const char **s) ++{ ++ /* lib/spprintf.h */ ++ ++ int i=0; ++ ++ while (isdigit(**s)) ++ i = i*10 + *((*s)++) - '0'; ++ ++ return i; ++} ++ ++static int __init cobd_spawn_alias(struct cobd_alias *alias, ++ const char *alias_name_requested, ++ int cobd_unit) ++{ ++ const char *index_str_start = &alias_name_requested[strlen(alias->name)]; ++ const char *index_str_end = index_str_start; ++ struct cobd_device *cobd; ++ struct gendisk *disk; ++ ++ int index = skip_atoi(&index_str_end); ++ ++ if (!((index >= 0) && (index <= alias->minor_count))) { ++ printk(KERN_WARNING "index out of bounds for alias %s (1 - %d)\n", ++ alias_name_requested, alias->minor_count); ++ return -1; ++ } ++ ++ if (alias->gendisk == NULL) { ++ static struct gendisk **gendisks; ++ gendisks = kmalloc(alias->minor_count * sizeof(struct gendisk *), GFP_KERNEL); ++ memset(gendisks, 0, alias->minor_count * sizeof(struct gendisk *)); ++ ++ if (!gendisks) { ++ printk(KERN_WARNING "cannot allocate gendisk array for %s\n", alias->name); ++ return -ENOMEM; ++ } ++ ++ if (!alias->major->registered) { ++ if (register_blkdev(alias->major->number, alias->major->name)) { ++ printk(KERN_WARNING "unable to get major number %d for cobd alias device %s\n", ++ alias->major->number, alias_name_requested); ++ kfree(gendisks); ++ return -EIO; ++ } ++ ++ alias->major->registered = 1; ++ } ++ ++ alias->gendisk = gendisks; ++ devfs_mk_dir(alias->name); ++ } ++ ++ if (alias->gendisk[index] != NULL) { ++ printk(KERN_WARNING "alias %s already used\n", alias_name_requested); ++ return -1; ++ } ++ ++ disk = alloc_disk(1); ++ if (!disk) { ++ printk(KERN_WARNING "cannot allocate disk for alias %s\n", alias_name_requested); ++ return -1; ++ } ++ ++ disk->queue = blk_init_queue(do_cobd_request, &cobd_lock); ++ if (!disk->queue) { ++ printk(KERN_WARNING "cannot allocate init queue for alias %s\n", alias_name_requested); ++ put_disk(disk); ++ return -1; ++ } ++ ++ cobd = &cobd_devs[cobd_unit]; ++ blk_queue_hardsect_size(disk->queue, hardsect_size); ++ disk->major = alias->major->number; ++ disk->first_minor = alias->minor_start + index; ++ disk->fops = &cobd_fops; ++ if (index) ++ sprintf(disk->disk_name, "%s%d", alias->name, index); ++ else ++ sprintf(disk->disk_name, "%s", alias->name); ++ sprintf(disk->devfs_name, "%s/%d", alias->name, index); ++ disk->private_data = cobd; ++ add_disk(disk); ++ alias->gendisk[index] = disk; ++ ++ printk("cobd alias cobd%d -> %s created\n", cobd_unit, alias_name_requested); ++ ++ return 0; ++} ++ ++static int __init cobd_aliases_init(void) ++{ ++ int unit; ++ co_block_request_t request; ++ ++ for (unit=0; unit < cobd_max; unit++) { ++ int result = cobd_get_alias(&cobd_devs[unit], &request); ++ if (result) ++ continue; ++ ++ printk("alias for cobd%d is %s\n", unit, request.alias); ++ ++ struct cobd_alias *alias = &cobd_aliases[0]; ++ while (alias->name) { ++ const char *match = (strstr(request.alias, alias->name)); ++ if (match == request.alias) { ++ cobd_spawn_alias(alias, request.alias, unit); ++ break; ++ } ++ alias++; ++ } ++ ++ if (alias->name == NULL) ++ printk("alias %s is unknown (see cobd_aliases in cobd.c)\n", request.alias); ++ } ++ ++ return 0; ++} ++ ++static void cobd_drives_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < cobd_max; i++) { ++ blk_cleanup_queue(cobd_disks[i]->queue); ++ del_gendisk(cobd_disks[i]); ++ put_disk(cobd_disks[i]); ++ } ++ ++ devfs_remove("cobd"); ++ if (unregister_blkdev(COLINUX_MAJOR, "cobd")) ++ printk(KERN_WARNING "cobd: cannot unregister blkdev\n"); ++ ++ kfree(cobd_disks); ++} ++ ++static void cobd_aliases_exit(void) ++{ ++ struct cobd_alias *alias = &cobd_aliases[0]; ++ while (alias->name != NULL) { ++ if (alias->gendisk == NULL) { ++ alias++; ++ continue; ++ } ++ ++ int index; ++ for (index=0; index < alias->minor_count; index++) { ++ struct gendisk *disk = alias->gendisk[index]; ++ if (!disk) ++ return; ++ ++ blk_cleanup_queue(disk->queue); ++ del_gendisk(disk); ++ put_disk(disk); ++ } ++ ++ devfs_remove(alias->name); ++ if (!alias->major->registered) { ++ unregister_blkdev(alias->major->number, alias->major->name); ++ alias->major->registered = 0; ++ } ++ kfree(alias->gendisk); ++ ++ alias++; ++ } ++} ++ ++static int __init cobd_init(void) ++{ ++ int result = cobd_drives_init(); ++ if (result) ++ return result; ++ ++ cobd_aliases_init(); ++ ++ return result; ++} ++ ++static void cobd_exit(void) ++{ ++ cobd_aliases_exit(); ++ cobd_drives_exit(); ++} ++ ++module_init(cobd_init); ++module_exit(cobd_exit); ++ ++ +diff -urN a/drivers/char/Makefile b/drivers/char/Makefile +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -27,6 +27,7 @@ + obj-$(CONFIG_STALLION) += stallion.o + obj-$(CONFIG_ISTALLION) += istallion.o + obj-$(CONFIG_DIGI) += pcxx.o ++obj-$(CONFIG_COOPERATIVE) += cocd.o + obj-$(CONFIG_DIGIEPCA) += epca.o + obj-$(CONFIG_SPECIALIX) += specialix.o + obj-$(CONFIG_MOXA_INTELLIO) += moxa.o +diff -urN a/drivers/char/cocd.c b/drivers/char/cocd.c +--- a/drivers/char/cocd.c ++++ b/drivers/char/cocd.c +@@ -0,0 +1,368 @@ ++/* ++ * Copyright (C) 2004 Dan Aloni <da-x@colinux.org> ++ * ++ * Cooperative Linux Serial Line implementation ++ * ++ * Compatible with UML, also based on some code from there. ++ * Also based on The tiny_tty.c example driver by Greg Kroah-Hartman (greg@kroah.com). ++ */ ++ ++/* ++ * 20040908: Ballard, Jonathan H. <jhballard@hotmail.com> ++ * : Implemented cocd_task() & throttle. ++ * 20041224: Used schedule() instead of shedule_work(). ++ * 20050101: Uses interruptible_sleep_on() and wake_up() instead of schedule(). ++ * : Uses list_*() for dispatched data flow to each unit. ++ * : Handles multiple units in seperate tasks. ++ * ++*/ ++ ++ ++#include <linux/major.h> ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/errno.h> ++#include <linux/major.h> ++#include <linux/stat.h> ++#include <linux/file.h> ++#include <linux/ioctl.h> ++#include <linux/device.h> ++#include <linux/console.h> ++#include <linux/wait.h> ++ ++#include <linux/workqueue.h> ++#include <linux/devfs_fs_kernel.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++ ++#include <linux/cooperative_internal.h> ++ ++#include <asm/uaccess.h> ++ ++struct cocd_tty { ++ struct semaphore sem; /* locks this structure */ ++ struct tty_struct *tty; /* tty for this device */ ++ unsigned open_count; /* open()/close() tally */ ++ struct work_struct work; /* individual unit task */ ++ struct list_head inq; /* input queue */ ++ wait_queue_head_t waitq; ++ int throttled; /* data flow throttle bit */ ++}; ++ ++static struct tty_driver *cocd_driver = NULL; ++DECLARE_MUTEX(cocd_sem); ++ ++static void cocd_unit_task(void *data) ++{ ++ co_message_node_t *input; ++ struct cocd_tty *cocd = data; ++ co_linux_message_t *message; ++ char *p, *e, *m; ++ struct tty_struct *tty; ++ ++ tty = cocd->tty; ++ ++ while(cocd->open_count) { ++ down(&cocd->sem); ++ if(list_empty(&cocd->inq)) { ++ up(&cocd->sem); ++ interruptible_sleep_on(&cocd->waitq); ++ continue; ++ } ++ input = list_entry(cocd->inq.prev, co_message_node_t, node); ++ up(&cocd->sem); ++ ++ message = (co_linux_message_t *)&input->msg.data; ++ e = (m = p = message->data) + message->size; ++ while(p < e && cocd->open_count) { ++ if(cocd->throttled) { ++ interruptible_sleep_on(&cocd->waitq); ++ continue; ++ } ++ if(e < (m += (TTY_FLIPBUF_SIZE - tty->flip.count))) ++ m = e; ++ while(p < m) ++ tty_insert_flip_char(tty, *(p++), 0); ++ if(tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ tty_flip_buffer_push(tty); ++ } ++ } ++ down(&cocd->sem); ++ list_del(&input->node); ++ up(&cocd->sem); ++ co_free_message(input); ++ if(tty->flip.count && cocd->open_count) { ++ if(cocd->throttled) { ++ interruptible_sleep_on(&cocd->waitq); ++ } ++ tty_flip_buffer_push(tty); ++ continue; ++ } ++ } ++ down(&cocd->sem); ++ while(!list_empty(&cocd->inq)) { ++ input = list_entry(cocd->inq.prev, co_message_node_t, node); ++ list_del(&input->node); ++ co_free_message(input); ++ } ++ up(&cocd->sem); ++ kfree(cocd); ++} ++ ++int cocd_open(struct tty_struct *tty, struct file * filp) ++{ ++ struct cocd_tty *cocd = NULL; ++ ++ down(&cocd_sem); ++ ++ /* MOD_INC_USE_COUNT; - Removed in 2.6, reference count is handled ++ * outside the module in 2.6 ++ */ ++ ++ if ((cocd = (struct cocd_tty *)tty->driver_data)) { ++ down (&cocd->sem); ++ } else { ++ if(!(cocd = kmalloc(sizeof(*cocd), GFP_KERNEL))) { ++ /* MOD_DEC_USE_COUNT; - Removed in 2.6, reference count ++ * is handled outside the module in 2.6 ++ */ ++ ++ up(&cocd_sem); ++ return -ENOMEM; ++ } ++ ++ init_MUTEX_LOCKED(&cocd->sem); ++ cocd->open_count = 0; ++ cocd->tty = tty; ++ cocd->throttled = 0; ++ INIT_WORK(&cocd->work, cocd_unit_task, cocd); ++ INIT_LIST_HEAD(&cocd->inq); ++ init_waitqueue_head(&cocd->waitq); ++ tty->driver_data = cocd; ++ tty->low_latency = 1; ++ schedule_work(&cocd->work); ++ } ++ ++ cocd->open_count++; ++ ++ up(&cocd->sem); ++ up(&cocd_sem); ++ ++ return 0; ++} ++ ++void cocd_close(struct tty_struct *tty, struct file * filp) ++{ ++ struct cocd_tty *cocd = NULL; ++ ++ down(&cocd_sem); ++ ++ cocd = (struct cocd_tty *)tty->driver_data; ++ if (!cocd) { ++ printk("cocd: no attached struct\n"); ++ goto out; ++ } ++ ++ down(&cocd->sem); ++ if (cocd->open_count == 1) { /* last close */ ++ tty->driver_data = NULL; ++ wake_up(&cocd->waitq); ++ } ++ cocd->open_count--; ++ up(&cocd->sem); ++ ++out: ++ /* MOD_DEC_USE_COUNT; - Removed in 2.6, reference count is handled ++ * outside the module in 2.6 ++ */ ++ ++ up(&cocd_sem); ++} ++ ++void cocd_interrupt(void) ++{ ++ if (!cocd_driver) ++ return; ++ ++ co_message_node_t *input; ++ if(!co_get_message(&input, CO_DEVICE_SERIAL)) ++ return; ++ if(!input) ++ return; ++ ++ co_linux_message_t *message; ++ struct tty_struct *tty; ++ struct cocd_tty *cocd; ++ message = (co_linux_message_t *)&input->msg.data; ++ down(&cocd_sem); ++ if (message->unit < CO_MODULE_MAX_SERIAL ++ && (tty = cocd_driver->ttys[message->unit]) ++ && (cocd = (struct cocd_tty *)tty->driver_data)) { ++ up(&cocd_sem); ++ down(&cocd->sem); ++ list_add_tail(&input->node,&cocd->inq); ++ up(&cocd->sem); ++ wake_up(&cocd->waitq); ++ return; ++ } ++ up(&cocd_sem); ++ co_free_message(input); ++} ++ ++int cocd_write(struct tty_struct * tty, ++ const unsigned char *buf, int count) ++{ ++ const char *kbuf_scan = NULL; ++ int count_left; ++ ++ kbuf_scan = buf; ++ count_left = count; ++ ++ while (count_left > 0) { ++ int count_partial = count_left; ++ if (count_partial > 1000) ++ count_partial = 1000; ++ ++ co_send_message(CO_MODULE_LINUX, ++ CO_MODULE_SERIAL0 + tty->index, ++ CO_PRIORITY_DISCARDABLE, ++ CO_MESSAGE_TYPE_STRING, ++ count_partial, ++ kbuf_scan); ++ ++ count_left -= count_partial; ++ kbuf_scan += count_partial; ++ } ++ ++ return count; ++} ++ ++int cocd_write_room(struct tty_struct *tty) ++{ ++ struct cocd_tty *cocd = NULL; ++ ++ cocd = (struct cocd_tty *)tty->driver_data; ++ if (!cocd) ++ return 0; ++ ++ down(&cocd->sem); ++ if (cocd->open_count == 0) { ++ /* port was not opened */ ++ up(&cocd->sem); ++ return 0; ++ } ++ ++ up(&cocd->sem); ++ return 255; ++} ++ ++void cocd_hangup(struct tty_struct *tty) ++{ ++} ++ ++void cocd_throttle(struct tty_struct * tty) ++{ ++ struct cocd_tty *cocd; ++ cocd = (struct cocd_tty *)tty->driver_data; ++ if (!cocd) ++ return; ++ down(&cocd->sem); ++ cocd->throttled = 1; ++ up(&cocd->sem); ++} ++ ++void cocd_unthrottle(struct tty_struct * tty) ++{ ++ struct cocd_tty *cocd; ++ cocd = (struct cocd_tty *)tty->driver_data; ++ if (!cocd) ++ return; ++ down(&cocd->sem); ++ cocd->throttled = 0; ++ up(&cocd->sem); ++ wake_up(&cocd->waitq); ++} ++ ++void cocd_flush_buffer(struct tty_struct *tty) ++{ ++} ++ ++void cocd_set_termios(struct tty_struct *tty, struct termios *old_termios) ++{ ++} ++ ++int cocd_chars_in_buffer(struct tty_struct *tty) ++{ ++ return 0; ++} ++ ++static struct tty_operations cocd_ops = { ++ .open = cocd_open, ++ .close = cocd_close, ++ .write = cocd_write, ++ .write_room = cocd_write_room, ++ .flush_buffer = cocd_flush_buffer, ++ .throttle = cocd_throttle, ++ .unthrottle = cocd_unthrottle, ++ .hangup = cocd_hangup, ++ .chars_in_buffer = cocd_chars_in_buffer, ++ .set_termios = cocd_set_termios, ++}; ++ ++static struct tty_driver *cocd_driver; ++ ++static void cocd_console_write(struct console *c, const char *string, unsigned len) ++{ ++} ++ ++static struct tty_driver *cocd_console_device(struct console *c, int *index) ++{ ++ *index = c->index; ++ return cocd_driver; ++} ++ ++static int cocd_console_setup(struct console *co, char *options) ++{ ++ return(0); ++} ++ ++static struct console cocd_cons = { ++ name: "ttyS", ++ write: cocd_console_write, ++ device: cocd_console_device, ++ setup: cocd_console_setup, ++ flags: CON_PRINTBUFFER, ++ index: -1, ++}; ++ ++static int __init cocd_init(void) ++{ ++ cocd_driver = alloc_tty_driver(CO_MODULE_MAX_SERIAL); ++ ++ if (!cocd_driver) ++ panic("Couldn't allocate cocd driver"); ++ ++ cocd_driver->owner = THIS_MODULE; ++ cocd_driver->driver_name = "Cooperative serial lines"; ++ cocd_driver->name = "ttS"; ++ cocd_driver->devfs_name = "tts/"; ++ cocd_driver->major = TTY_MAJOR; ++ cocd_driver->minor_start = 64; ++ cocd_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ cocd_driver->subtype = 0; ++ cocd_driver->init_termios = tty_std_termios; ++ cocd_driver->flags = 0; ++ ++ tty_set_operations(cocd_driver, &cocd_ops); ++ ++ if (tty_register_driver(cocd_driver)) ++ panic("Couldn't register cocd driver"); ++ ++ register_console(&cocd_cons); ++ ++ return 0; ++} ++ ++module_init(cocd_init); +diff -urN a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -16,7 +16,7 @@ + default y + depends on INPUT && INPUT_KEYBOARD + select SERIO +- select SERIO_I8042 if PC ++ select SERIO_I8042 if PC && !COOPERATIVE + select SERIO_GSCPS2 if GSC + help + Say Y here if you want to use a standard AT or PS/2 keyboard. Usually +diff -urN a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c +--- a/drivers/input/keyboard/atkbd.c ++++ b/drivers/input/keyboard/atkbd.c +@@ -26,6 +26,7 @@ + #include <linux/input.h> + #include <linux/serio.h> + #include <linux/workqueue.h> ++#include <linux/cooperative_internal.h> + + #define DRIVER_DESC "AT and PS/2 keyboard driver" + +@@ -640,6 +641,9 @@ + { + unsigned char param[2]; + ++ if (cooperative_mode_enabled()) ++ return 0; ++ + /* + * Some systems, where the bit-twiddling when testing the io-lines of the + * controller may confuse the keyboard need a full reset of the keyboard. On +diff -urN a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig +--- a/drivers/input/mouse/Kconfig ++++ b/drivers/input/mouse/Kconfig +@@ -14,7 +14,7 @@ + config MOUSE_PS2 + tristate "PS/2 mouse" + default y +- depends on INPUT && INPUT_MOUSE ++ depends on INPUT && INPUT_MOUSE && !COOPERATIVE + select SERIO + select SERIO_I8042 if PC + select SERIO_GSCPS2 if GSC +@@ -36,9 +36,21 @@ + To compile this driver as a module, choose M here: the + module will be called psmouse. + ++config MOUSE_COOPERATIVE ++ tristate "Cooperative Mouse driver" ++ default y ++ depends on INPUT && INPUT_MOUSE && COOPERATIVE ++ ---help--- ++ Virtual mouse driver for cooperative kernels. ++ ++ If unsure, say Y. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called psmouse. ++ + config MOUSE_SERIAL + tristate "Serial mouse" +- depends on INPUT && INPUT_MOUSE ++ depends on INPUT && INPUT_MOUSE && !COOPERATIVE + select SERIO + ---help--- + Say Y here if you have a serial (RS-232, COM port) mouse connected +@@ -52,7 +64,7 @@ + + config MOUSE_INPORT + tristate "InPort/MS/ATIXL busmouse" +- depends on INPUT && INPUT_MOUSE && ISA ++ depends on INPUT && INPUT_MOUSE && ISA && !COOPERATIVE + help + Say Y here if you have an InPort, Microsoft or ATI XL busmouse. + They are rather rare these days. +@@ -62,13 +74,13 @@ + + config MOUSE_ATIXL + bool "ATI XL variant" +- depends on MOUSE_INPORT ++ depends on MOUSE_INPORT && !COOPERATIVE + help + Say Y here if your mouse is of the ATI XL variety. + + config MOUSE_LOGIBM + tristate "Logitech busmouse" +- depends on INPUT && INPUT_MOUSE && ISA ++ depends on INPUT && INPUT_MOUSE && ISA && !COOPERATIVE + help + Say Y here if you have a Logitech busmouse. + They are rather rare these days. +@@ -78,7 +90,7 @@ + + config MOUSE_PC110PAD + tristate "IBM PC110 touchpad" +- depends on INPUT && INPUT_MOUSE && ISA ++ depends on INPUT && INPUT_MOUSE && ISA && !COOPERATIVE + help + Say Y if you have the IBM PC-110 micro-notebook and want its + touchpad supported. +@@ -88,7 +100,7 @@ + + config MOUSE_MAPLE + tristate "Maple bus mouse" +- depends on SH_DREAMCAST && INPUT && INPUT_MOUSE && MAPLE ++ depends on SH_DREAMCAST && INPUT && INPUT_MOUSE && MAPLE && !COOPERATIVE + help + Say Y if you have a DreamCast console and a mouse attached to + its Maple bus. +@@ -98,7 +110,7 @@ + + config MOUSE_AMIGA + tristate "Amiga mouse" +- depends on AMIGA && INPUT && INPUT_MOUSE ++ depends on AMIGA && INPUT && INPUT_MOUSE && !COOPERATIVE + help + Say Y here if you have an Amiga and want its native mouse + supported by the kernel. +@@ -108,7 +120,7 @@ + + config MOUSE_RISCPC + tristate "Acorn RiscPC mouse" +- depends on ARCH_ACORN && INPUT && INPUT_MOUSE ++ depends on ARCH_ACORN && INPUT && INPUT_MOUSE && !COOPERATIVE + help + Say Y here if you have the Acorn RiscPC computer and want its + native mouse supported. +@@ -118,7 +130,7 @@ + + config MOUSE_VSXXXAA + tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet" +- depends on INPUT && INPUT_MOUSE ++ depends on INPUT && INPUT_MOUSE && !COOPERATIVE + select SERIO + help + Say Y (or M) if you want to use a DEC VSXXX-AA (hockey +diff -urN a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile +--- a/drivers/input/mouse/Makefile ++++ b/drivers/input/mouse/Makefile +@@ -11,6 +11,7 @@ + obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o + obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o + obj-$(CONFIG_MOUSE_PS2) += psmouse.o ++obj-$(CONFIG_MOUSE_COOPERATIVE) += comouse.o + obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o + obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o + +diff -urN a/drivers/input/mouse/comouse.c b/drivers/input/mouse/comouse.c +--- a/drivers/input/mouse/comouse.c ++++ b/drivers/input/mouse/comouse.c +@@ -0,0 +1,74 @@ ++/* ++ * Virtual mouse driver for Linux ++ * ++ * Skeleton based on: ++ * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $ ++ * ++ * Copyright (c) 1999-2001 Vojtech Pavlik ++ * ++ * Copyright (c) 2004 Dan Aloni ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/input.h> ++#include <linux/config.h> ++#include <linux/serio.h> ++#include <linux/init.h> ++ ++MODULE_AUTHOR("Dan Aloni <da-x@colinux.org>"); ++MODULE_DESCRIPTION("Virtual mouse driver"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * comouse_interrupt() handles incoming characters, either gathering them into ++ * packets or passing them to the command routine as command output. ++ */ ++ ++static irqreturn_t comouse_interrupt(struct serio *serio, ++ unsigned char data, unsigned int flags, struct pt_regs *regs) ++{ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * comouse_disconnect() cleans up after we don't want talk ++ * to the mouse anymore. ++ */ ++ ++static void comouse_disconnect(struct serio *serio) ++{ ++} ++ ++/* ++ * comouse_connect() is a callback form the serio module when ++ * an unhandled serio port is found. ++ */ ++ ++static void comouse_connect(struct serio *serio, struct serio_driver *dev) ++{ ++} ++ ++static struct serio_driver comouse_dev = { ++ .interrupt = comouse_interrupt, ++ .connect = comouse_connect, ++ .disconnect = comouse_disconnect, ++ .driver = { ++ .name = "comouse", ++ }, ++}; ++ ++int __init comouse_init(void) ++{ ++ serio_register_driver(&comouse_dev); ++ return 0; ++} ++ ++void __exit comouse_exit(void) ++{ ++ serio_unregister_driver(&comouse_dev); ++} ++ ++module_init(comouse_init); ++module_exit(comouse_exit); +diff -urN a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig +--- a/drivers/input/serio/Kconfig ++++ b/drivers/input/serio/Kconfig +@@ -20,7 +20,7 @@ + tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 + default y + select SERIO +- depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K ++ depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K && !COOPERATIVE + ---help--- + i8042 is the chip over which the standard AT keyboard and PS/2 + mouse are connected to the computer. If you use these devices, +@@ -132,17 +132,22 @@ + module will be called maceps2. + + config SERIO_RAW +- tristate "Raw access to serio ports" +- depends on SERIO +- help +- Say Y here if you want to have raw access to serio ports, such as +- AUX ports on i8042 keyboard controller. Each serio port that is +- bound to this driver will be accessible via a char device with +- major 10 and dynamically allocated minor. The driver will try +- allocating minor 1 (that historically corresponds to /dev/psaux) +- first. To bind this driver to a serio port use sysfs interface: +- +- echo -n "serio_raw" > /sys/bus/serio/devices/serioX/driver +- +- To compile this driver as a module, choose M here: the +- module will be called serio_raw. ++ tristate "Raw access to serio ports" ++ depends on SERIO ++ help ++ Say Y here if you want to have raw access to serio ports, such as ++ AUX ports on i8042 keyboard controller. Each serio port that is ++ bound to this driver will be accessible via a char device with ++ major 10 and dynamically allocated minor. The driver will try ++ allocating minor 1 (that historically corresponds to /dev/psaux) ++ first. To bind this driver to a serio port use sysfs interface: ++ ++ echo -n "serio_raw" > /sys/bus/serio/devices/serioX/driver ++ ++ To compile this driver as a module, choose M here: the ++ module will be called serio_raw. ++ ++config SERIO_COKBD ++ tristate "Cooperative Linux virtual keyboard controller driver" ++ depends on COOPERATIVE ++ default y +diff -urN a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile +--- a/drivers/input/serio/Makefile ++++ b/drivers/input/serio/Makefile +@@ -18,3 +18,4 @@ + obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o + obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o + obj-$(CONFIG_SERIO_RAW) += serio_raw.o ++obj-$(CONFIG_SERIO_COKBD) += cokbd.o +\ No newline at end of file +diff -urN a/drivers/input/serio/cokbd.c b/drivers/input/serio/cokbd.c +--- a/drivers/input/serio/cokbd.c ++++ b/drivers/input/serio/cokbd.c +@@ -0,0 +1,155 @@ ++/* ++ * Cooperative Linux virtual keyboard controller driver ++ * ++ * Copyright (c) 1999-2002 Dan Aloni <da-x@colinux.org) ++ * Based on 98kbd-io.c written by Osamu Tomita> ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/delay.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++#include <linux/serio.h> ++#include <linux/sched.h> ++#include <linux/cooperative_internal.h> ++ ++#include <asm/io.h> ++ ++MODULE_AUTHOR("Dan Aloni <da-x@colinux.org>"); ++MODULE_DESCRIPTION("Cooperative Linux virtual keyboard controller driver"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * Names. ++ */ ++ ++#define COKBD_PHYS_DESC "cokbd" ++ ++static struct serio cokbd_port; ++ ++static irqreturn_t cokbdio_interrupt(int irq, void *dev_id, struct pt_regs *regs); ++ ++/* ++ * cokbd_flush() flushes all data that may be in the keyboard buffers ++ */ ++ ++static int cokbd_flush(void) ++{ ++#if (0) ++ co_linux_message_t *message; ++ ++ while (co_get_message(&message, CO_DEVICE_KEYBOARD)) { ++ co_free_message(message); ++ } ++#endif ++ return 0; ++} ++ ++/* ++ * cokbd_write() sends a byte out through the keyboard interface. ++ */ ++ ++#define ATKBD_CMD_GETID 0x02f2 ++ ++static void cokbd_receive(struct serio *port, unsigned char c) ++{ ++ struct pt_regs regs= {0, }; ++ ++ serio_interrupt(port, c, 0, ®s); ++} ++ ++static int cokbd_write(struct serio *port, unsigned char c) ++{ ++ return 0; ++} ++ ++/* ++ * cokbd_open() is called when a port is open by the higher layer. ++ * It allocates the interrupt and enables in in the chip. ++ */ ++ ++static int cokbd_open(struct serio *port) ++{ ++ cokbd_flush(); ++ ++ if (request_irq(KEYBOARD_IRQ, cokbdio_interrupt, 0, "cokbd", NULL)) { ++ printk(KERN_ERR "cobkd.c: Can't get irq %d for %s, unregistering the port.\n", KEYBOARD_IRQ, "KBD"); ++ serio_unregister_port(port); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void cokbd_close(struct serio *port) ++{ ++ printk(KERN_INFO "cokbd closed\n"); ++ ++ free_irq(KEYBOARD_IRQ, NULL); ++ ++ cokbd_flush(); ++} ++ ++/* ++ * Structures for registering the devices in the serio.c module. ++ */ ++ ++static struct serio cokbd_port = ++{ ++ .type = SERIO_8042_XL, ++ .write = cokbd_write, ++ .open = cokbd_open, ++ .close = cokbd_close, ++ .name = "cokbd port", ++ .phys = COKBD_PHYS_DESC, ++}; ++ ++/* ++ * cokbdio_interrupt() is the most important function in this driver - ++ * it handles the interrupts from keyboard, and sends incoming bytes ++ * to the upper layers. ++ */ ++ ++static irqreturn_t cokbdio_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ co_message_node_t *node_message; ++ while (co_get_message(&node_message, CO_DEVICE_KEYBOARD)) { ++ co_linux_message_t *message = (co_linux_message_t *)&node_message->msg.data; ++ co_scan_code_t *sc = (co_scan_code_t *)message->data; ++ unsigned long scancode = sc->code; ++ ++ if (!sc->down) ++ scancode |= 0x80; ++ ++ cokbd_receive(&cokbd_port, scancode); ++ ++ co_free_message(node_message); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++int __init cokbdio_init(void) ++{ ++ serio_register_port(&cokbd_port); ++ ++ printk(KERN_INFO "serio: cokbd at irq %d\n", KEYBOARD_IRQ); ++ ++ return 0; ++} ++ ++void __exit cokbdio_exit(void) ++{ ++ serio_unregister_port(&cokbd_port); ++} ++ ++module_init(cokbdio_init); ++module_exit(cokbdio_exit); +diff -urN a/drivers/net/Kconfig b/drivers/net/Kconfig +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -127,6 +127,10 @@ + + If you don't know what to use this for, you don't need it. + ++config COOPERATIVE_CONET ++ tristate 'Cooperative Virtual Ethernet driver support' ++ depends on COOPERATIVE ++ + config NET_SB1000 + tristate "General Instruments Surfboard 1000" + depends on NETDEVICES && PNP +diff -urN a/drivers/net/Makefile b/drivers/net/Makefile +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -152,6 +152,7 @@ + + # This is also a 82596 and should probably be merged + obj-$(CONFIG_LP486E) += lp486e.o ++obj-$(CONFIG_COOPERATIVE_CONET) += conet.o + + obj-$(CONFIG_ETH16I) += eth16i.o + obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o +diff -urN a/drivers/net/conet.c b/drivers/net/conet.c +--- a/drivers/net/conet.c ++++ b/drivers/net/conet.c +@@ -0,0 +1,305 @@ ++/* ++ * Copyright (C) 2003-2004 Dan Aloni <da-x@gmx.net> ++ * Copyright (C) 2004 Pat Erley ++ * Copyright (C) 2004 George Boutwell ++ * ++ * Cooperative Linux Network Device implementation ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++ ++#include <linux/kernel.h> ++ ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/ethtool.h> ++ ++#include <linux/cooperative_internal.h> ++#include <asm/irq.h> ++ ++struct conet_priv { ++ struct net_device_stats stats; ++ int status; ++ int unit; ++ int enabled; ++ int handling; ++}; ++ ++struct net_device *conet_dev[CO_MODULE_MAX_CONET]; ++ ++irqreturn_t conet_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr); ++ ++static int conet_get_mac(int unit, char *address) ++{ ++ unsigned long flags = 0; ++ co_network_request_t *net_request; ++ int result = 0; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_NETWORK; ++ net_request = (typeof(net_request))&co_passage_page->params[1]; ++ net_request->unit = unit; ++ net_request->type = CO_NETWORK_GET_MAC; ++ co_switch_wrapper(); ++ memcpy(address, net_request->mac_address, ETH_ALEN); ++ result = net_request->result; ++ co_passage_page_release(flags); ++ ++ return result; ++} ++ ++int conet_open(struct net_device *dev) ++{ ++ struct conet_priv *priv = (struct conet_priv *)dev->priv; ++ ++ if (priv->enabled) ++ return 0; ++ ++ conet_get_mac(priv->unit, dev->dev_addr); ++ ++ priv->enabled = 1; ++ ++ netif_start_queue(dev); ++ ++ return 0; ++} ++ ++int conet_stop(struct net_device *dev) ++{ ++ struct conet_priv *priv = (struct conet_priv *)dev->priv; ++ ++ priv->enabled = 0; ++ ++ netif_stop_queue(dev); /* can't transmit any more */ ++ ++ return 0; ++} ++ ++int conet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ int len; ++ char *data; ++ struct conet_priv *priv = (struct conet_priv *)dev->priv; ++ ++ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; ++ data = skb->data; ++ ++ dev->trans_start = jiffies; /* save the timestamp */ ++ ++ co_send_message(CO_MODULE_LINUX, ++ CO_MODULE_CONET0 + priv->unit, ++ CO_PRIORITY_DISCARDABLE, ++ CO_MESSAGE_TYPE_OTHER, ++ len, ++ data); ++ ++ priv->stats.tx_bytes+=skb->len; ++ priv->stats.tx_packets++; ++ ++ dev_kfree_skb(skb); ++ ++ return 0; ++} ++ ++static void conet_rx(struct net_device *dev, co_linux_message_t *message) ++{ ++ struct sk_buff *skb; ++ struct conet_priv *priv = (struct conet_priv *)dev->priv; ++ int len; ++ unsigned char *buf; ++ ++ len = message->size; ++ buf = message->data; ++ ++ /* ++ * The packet has been retrieved from the transmission ++ * medium. Build an skb around it, so upper layers can handle it ++ */ ++ skb = dev_alloc_skb(len+2); ++ if (!skb) { ++ printk("conet rx: low on mem - packet dropped\n"); ++ priv->stats.rx_dropped++; ++ return; ++ } ++ ++ memcpy(skb_put(skb, len), buf, len); ++ ++ /* Write metadata, and then pass to the receive level */ ++ skb->dev = dev; ++ skb->protocol = eth_type_trans(skb, dev); ++ skb->ip_summed = CHECKSUM_NONE; /* make the kernel calculate and verify ++ the checksum */ ++ ++ priv->stats.rx_bytes += len; ++ priv->stats.rx_packets++; ++ ++ netif_rx(skb); ++ return; ++} ++ ++irqreturn_t conet_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr) ++{ ++ co_message_node_t *node_message; ++ while (co_get_message(&node_message, CO_DEVICE_NETWORK)) { ++ struct net_device *dev; ++ struct conet_priv *priv; ++ co_linux_message_t *message; ++ ++ message = (co_linux_message_t *)&node_message->msg.data; ++ if (message->unit < 0 || message->unit >= CO_MODULE_MAX_CONET) { ++ printk("conet intrrupt: buggy network reception\n"); ++ return IRQ_HANDLED; ++ } ++ ++ dev = conet_dev[message->unit]; ++ if (!dev) { ++ co_free_message(node_message); ++ continue; ++ } ++ ++ if (!netif_running(dev)) { ++ co_free_message(node_message); ++ continue; ++ } ++ ++ priv = (struct conet_priv *)dev->priv; ++ if (priv->handling) { ++ co_free_message(node_message); ++ continue; ++ } ++ ++ priv->handling = 1; ++ conet_rx(dev, message); ++ co_free_message(node_message); ++ priv->handling = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++struct net_device_stats* conet_get_stats(struct net_device *dev) ++{ ++ return (struct net_device_stats *)dev->priv; ++} ++ ++int conet_init(struct net_device *dev) ++{ ++ struct conet_priv *priv = (struct conet_priv *)dev->priv; ++ ++ memset(&priv->stats, 0, sizeof(priv->stats)); ++ ++ ether_setup(dev); ++ ++ dev->open = conet_open; ++ dev->stop = conet_stop; ++ dev->hard_start_xmit = conet_hard_start_xmit; ++ dev->get_stats = conet_get_stats; ++ dev->irq = NETWORK_IRQ; ++ ++ SET_MODULE_OWNER(dev); ++ ++ return 0; ++} ++ ++void conet_uninit(struct net_device *dev) ++{ ++} ++ ++static struct net_device *conet_create(int unit) ++{ ++ struct net_device *dev; ++ struct conet_priv *priv; ++ int result = 0; ++ ++ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); ++ if (!dev) { ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ memset(dev, 0, sizeof(struct net_device)); ++ ++ priv = kmalloc(sizeof(struct conet_priv), GFP_KERNEL); ++ if (priv == NULL) { ++ kfree(dev); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ memset(priv, 0, sizeof(struct conet_priv)); ++ priv->unit = unit; ++ ++ dev->priv = priv; ++ dev->init = conet_init; ++ dev->uninit = conet_uninit; ++ strcpy(dev->name, "eth%d"); ++ ++ result = register_netdev(dev); ++ if (result) { ++ printk("conet: error %d registering device \"%s\"\n", result, dev->name); ++ kfree(dev->priv); ++ kfree(dev); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ printk("conet%d: initialized\n", priv->unit); ++ ++ return dev; ++} ++ ++static void conet_destroy(struct net_device *dev) ++{ ++ struct conet_priv *priv = (struct conet_priv *) dev->priv; ++ ++ printk("conet%d: freed\n", priv->unit); ++ ++ unregister_netdev(dev); ++ kfree(dev->priv); ++ kfree(dev); ++} ++ ++static int __init conet_init_module(void) ++{ ++ int unit = 0, result; ++ struct net_device *dev; ++ char mac_address[6]; ++ ++ result = request_irq(NETWORK_IRQ, &conet_interrupt, 0, "conet", NULL); ++ ++ printk("conet: loaded (max %d devices)\n", CO_MODULE_MAX_CONET); ++ ++ for (unit=0; unit < CO_MODULE_MAX_CONET; unit++) { ++ conet_dev[unit] = NULL; ++ ++ result = conet_get_mac(unit, mac_address); ++ if (!result) ++ continue; ++ ++ dev = conet_create(unit); ++ if (!IS_ERR(dev)) ++ conet_dev[unit] = dev; ++ } ++ ++ return result; ++} ++ ++static void __exit conet_cleanup_module(void) ++{ ++ int unit = 0; ++ ++ free_irq(NETWORK_IRQ, NULL); ++ ++ for (unit=0; unit < CO_MODULE_MAX_CONET; unit++) { ++ if (!conet_dev[unit]) ++ continue; ++ ++ conet_destroy(conet_dev[unit]); ++ } ++} ++ ++module_init(conet_init_module); ++module_exit(conet_cleanup_module); +diff -urN a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig +--- a/drivers/video/console/Kconfig ++++ b/drivers/video/console/Kconfig +@@ -6,7 +6,7 @@ + + config VGA_CONSOLE + bool "VGA text console" if EMBEDDED || !X86 +- depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC32 && !SPARC64 && !M68K && !PARISC ++ depends on !COOPERATIVE && !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC32 && !SPARC64 && !M68K && !PARISC + default y + help + Saying Y here will allow you to use Linux in text mode through a +@@ -26,6 +26,14 @@ + # fi + # fi + ++config COOPERATIVE_CONSOLE ++ bool 'coLinux Pseudo-VGA text console' if COOPERATIVE ++ depends on !VGA_CONSOLE && COOPERATIVE ++ default y ++ help ++ You need to say Y here if you compile a Linux kernel in cooperative ++ mode. ++ + config VIDEO_SELECT + bool "Video mode selection support" + depends on (X86 || X86_64) && VGA_CONSOLE +@@ -99,7 +107,7 @@ + + config DUMMY_CONSOLE + bool +- depends on PROM_CONSOLE!=y || VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y ++ depends on PROM_CONSOLE!=y || (COOPERATIVE_CONSOLE!=y && VGA_CONSOLE!=y) || SGI_NEWPORT_CONSOLE!=y + default y + + config FRAMEBUFFER_CONSOLE +diff -urN a/drivers/video/console/Makefile b/drivers/video/console/Makefile +--- a/drivers/video/console/Makefile ++++ b/drivers/video/console/Makefile +@@ -23,6 +23,7 @@ + obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o + obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o + obj-$(CONFIG_VGA_CONSOLE) += vgacon.o ++obj-$(CONFIG_COOPERATIVE_CONSOLE) += cocon.o + obj-$(CONFIG_MDA_CONSOLE) += mdacon.o + obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o + obj-$(CONFIG_FB_TILEBLITTING) += tileblit.o +diff -urN a/drivers/video/console/cocon.c b/drivers/video/console/cocon.c +--- a/drivers/video/console/cocon.c ++++ b/drivers/video/console/cocon.c +@@ -0,0 +1,464 @@ ++/* ++ * linux/drivers/video/cocon.c -- Cooperative Linux console VGA driver ++ * ++ * 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. ++ * ++ * Based on code copied from vgacon.c. ++ * ++ * Dan Aloni <da-x@gmx.net>, 2003-2004 (c) ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/sched.h> ++#include <linux/fs.h> ++#include <linux/kernel.h> ++#include <linux/tty.h> ++#include <linux/console.h> ++#include <linux/string.h> ++#include <linux/kd.h> ++#include <linux/slab.h> ++#include <linux/vt_kern.h> ++#include <linux/selection.h> ++#include <linux/init.h> ++ ++#include <linux/cooperative_internal.h> ++ ++/* ++ * Interface used by the world ++ */ ++ ++static const char *cocon_startup(void); ++static void cocon_init(struct vc_data *c, int init); ++static void cocon_deinit(struct vc_data *c); ++static void cocon_clear(struct vc_data *c, int, int, int, int); ++static void cocon_cursor(struct vc_data *c, int mode); ++static int cocon_switch(struct vc_data *c); ++static int cocon_blank(struct vc_data *c, int blank, int mode_switch); ++/* static int cocon_font_op(struct vc_data *c, struct console_font_op *op); */ ++static int cocon_set_palette(struct vc_data *c, unsigned char *table); ++static int cocon_scrolldelta(struct vc_data *c, int lines); ++static int cocon_set_origin(struct vc_data *c); ++static void cocon_save_screen(struct vc_data *c); ++static int cocon_scroll(struct vc_data *c, int t, int b, int dir, int lines); ++static u8 cocon_build_attr(struct vc_data *c, u8 color, u8 intensity, u8 blink, u8 underline, u8 reverse); ++static void cocon_invert_region(struct vc_data *c, u16 *p, int count); ++ ++static const char __init *cocon_startup(void) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_STARTUP; ++ co_send_message_restore(flags); ++ ++ return "CoCON"; ++} ++ ++static void cocon_init(struct vc_data *c, int init) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ /* We cannot be loaded as a module, therefore init is always 1 */ ++ c->vc_can_do_color = 1; ++ c->vc_cols = 80; ++ c->vc_rows = 25; ++ c->vc_complement_mask = 0x7700; ++ c->vc_visible_origin = 0; ++ c->vc_origin = 0; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_INIT; ++ co_send_message_restore(flags); ++} ++ ++static void cocon_deinit(struct vc_data *c) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_DEINIT; ++ co_send_message_restore(flags); ++ ++} ++ ++static void cocon_clear(struct vc_data *c, int top, int left, int rows, int cols) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->clear + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_CLEAR; ++ message->clear.top = top; ++ message->clear.left = left; ++ message->clear.bottom = top + rows - 1; ++ message->clear.right = left + cols - 1; ++ message->clear.charattr = c->vc_video_erase_char; ++ co_send_message_restore(flags); ++} ++ ++static void cocon_putc(struct vc_data *c, int charattr, int y, int x) ++{ ++ unsigned long flags; ++ co_message_t *co_message; ++ co_console_message_t *message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->putc + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_PUTC; ++ message->putc.x = x; ++ message->putc.y = y; ++ message->putc.charattr = charattr; ++ co_send_message_restore(flags); ++} ++ ++ ++static void cocon_putcs(struct vc_data *conp, ++ const unsigned short *s, int count, int yy, int xx) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ if (count > CO_MAX_PARAM_SIZE/2 - 16) ++ return; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->putcs + 1)) - ((char *)message) + ++ count * sizeof(unsigned short); ++ message->type = CO_OPERATION_CONSOLE_PUTCS; ++ message->putcs.x = xx; ++ message->putcs.y = yy; ++ message->putcs.count = count; ++ memcpy(&message->putcs.data, s, count * sizeof(unsigned short)); ++ co_send_message_restore(flags); ++} ++ ++static u8 cocon_build_attr(struct vc_data *c, u8 color, u8 intensity, u8 blink, u8 underline, u8 reverse) ++{ ++ u8 attr = color; ++ ++ if (underline) ++ attr = (attr & 0xf0) | c->vc_ulcolor; ++ else if (intensity == 0) ++ attr = (attr & 0xf0) | c->vc_halfcolor; ++ if (reverse) ++ attr = ((attr) & 0x88) | ((((attr) >> 4) | ((attr) << 4)) & 0x77); ++ if (blink) ++ attr ^= 0x80; ++ if (intensity == 2) ++ attr ^= 0x08; ++ ++ return attr; ++} ++ ++static void cocon_invert_region(struct vc_data *c, u16 *p, int count) ++{ ++ unsigned long flags; ++ co_message_t *co_message; ++ co_console_message_t *message; ++ unsigned long x = (unsigned long)(p - c->vc_origin); // UPDATE: vc_origin = 0; but not yet ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->invert + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_INVERT_REGION; ++ message->invert.y = ((unsigned)x)/c->vc_cols; ++ message->invert.x = ((unsigned)x)-(message->invert.y); ++ message->invert.count = count; ++ co_send_message_restore(flags); ++ ++ while (count--) { ++ u16 a = scr_readw(p); ++ a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); ++ scr_writew(a, p++); ++ } ++ ++} ++ ++static void cocon_cursor(struct vc_data *c, int mode) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->cursor + 1)) - ((char *)message);; ++ if (mode==CM_ERASE) { ++ message->type = CO_OPERATION_CONSOLE_CURSOR_ERASE; ++ message->cursor.height = 0; ++ co_send_message_restore(flags); ++ return; ++ } ++ ++ if(mode==CM_MOVE) { ++ message->type = CO_OPERATION_CONSOLE_CURSOR_MOVE; ++ } else /*(mode==CM_DRAW)*/ { ++ message->type = CO_OPERATION_CONSOLE_CURSOR_DRAW; ++ } ++ message->cursor.x = c->vc_x; ++ message->cursor.y = c->vc_y; ++ ++ switch (c->vc_cursor_type & CUR_HWMASK) { ++ case CUR_UNDERLINE: ++ message->cursor.height = 5; ++ break; ++ case CUR_TWO_THIRDS: ++ message->cursor.height = 66; ++ break; ++ case CUR_LOWER_THIRD: ++ message->cursor.height = 33; ++ break; ++ case CUR_LOWER_HALF: ++ message->cursor.height = 50; ++ break; ++ case CUR_NONE: ++ message->cursor.height = 0; ++ break; ++ default: ++ message->cursor.height = 5; ++ break; ++ } ++ ++ co_send_message_restore(flags); ++} ++ ++static int cocon_switch(struct vc_data *c) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_SWITCH; ++ co_send_message_restore(flags); ++ ++ return 1; /* Redrawing not needed */ ++} ++ ++static int cocon_set_palette(struct vc_data *c, unsigned char *table) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_SET_PALETTE; ++ co_send_message_restore(flags); ++ ++ return 1; ++} ++ ++static int cocon_blank(struct vc_data *c, int blank, int mode_switchg) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_BLANK; ++ co_send_message_restore(flags); ++ ++ return 1; ++} ++ ++ ++static int cocon_scrolldelta(struct vc_data *c, int lines) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_SCROLLDELTA; ++ co_send_message_restore(flags); ++ ++ return 1; ++} ++ ++static int cocon_set_origin(struct vc_data *c) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_SET_ORIGIN; ++ co_send_message_restore(flags); ++ ++ return 1; ++} ++ ++static void cocon_save_screen(struct vc_data *c) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->type + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_SAVE_SCREEN; ++ co_send_message_restore(flags); ++} ++ ++static int cocon_scroll(struct vc_data *c, int t, int b, int dir, int lines) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->scroll + 1)) - ((char *)message); ++ if (dir == SM_UP) ++ message->type = CO_OPERATION_CONSOLE_SCROLL_UP; ++ else ++ message->type = CO_OPERATION_CONSOLE_SCROLL_DOWN; ++ message->scroll.top = t; ++ message->scroll.bottom = b-1; ++ message->scroll.lines = lines; ++ co_send_message_restore(flags); ++ ++ return 0; ++} ++ ++static void cocon_bmove(struct vc_data *c, int sy, int sx, int dy, int dx, int h, int w) ++{ ++ unsigned long flags; ++ co_console_message_t *message; ++ co_message_t *co_message; ++ ++ co_message = co_send_message_save(&flags); ++ message = (co_console_message_t *)co_message->data; ++ co_message->from = CO_MODULE_LINUX; ++ co_message->to = CO_MODULE_CONSOLE; ++ co_message->priority = CO_PRIORITY_DISCARDABLE; ++ co_message->type = CO_MESSAGE_TYPE_STRING; ++ co_message->size = ((char *)(&message->bmove + 1)) - ((char *)message); ++ message->type = CO_OPERATION_CONSOLE_BMOVE; ++ message->bmove.row = dy; ++ message->bmove.column = dx; ++ message->bmove.top = sy; ++ message->bmove.left = sx; ++ message->bmove.bottom = sy + h - 1; ++ message->bmove.right = sx + w - 1; ++ co_send_message_restore(flags); ++} ++ ++/* ++ * The console `switch' structure for the VGA based console ++ */ ++ ++const struct consw colinux_con = { ++ con_startup: cocon_startup, ++ con_init: cocon_init, ++ con_deinit: cocon_deinit, ++ con_clear: cocon_clear, ++ con_putc: cocon_putc, ++ con_putcs: cocon_putcs, ++ con_cursor: cocon_cursor, ++ con_scroll: cocon_scroll, ++ con_bmove: cocon_bmove, ++ con_switch: cocon_switch, ++ con_blank: cocon_blank, ++ con_set_palette: cocon_set_palette, ++ con_scrolldelta: cocon_scrolldelta, ++ con_set_origin: cocon_set_origin, ++ con_save_screen: cocon_save_screen, ++ con_build_attr: cocon_build_attr, ++ con_invert_region: cocon_invert_region, ++}; ++ ++MODULE_LICENSE("GPL"); +diff -urN a/fs/Kconfig b/fs/Kconfig +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -1098,6 +1098,19 @@ + containing the directory /) cannot be compiled as a module. + + ++config COFUSE_FS ++ tristate "Cooperative Host file system support (COFUSE)" ++ depends on COOPERATIVE ++ default y ++ help ++ In Cooperative mode, this file system allows you to mount an host ++ directory structure to a local mountpoint. ++ COFUSE (Cooperative FUSE) is based on the original FUSE ++ (File System in User Space). ++ ++ To compile the cofuse support as a module, choose M here: the ++ module will be called cofusefs. ++ + + config EFS_FS + tristate "EFS file system support (read only) (EXPERIMENTAL)" +diff -urN a/fs/Makefile b/fs/Makefile +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -94,3 +94,5 @@ + obj-$(CONFIG_BEFS_FS) += befs/ + obj-$(CONFIG_HOSTFS) += hostfs/ + obj-$(CONFIG_HPPFS) += hppfs/ ++obj-$(CONFIG_COFUSE_FS) += cofusefs/ ++ +diff -urN a/fs/cofusefs/Makefile b/fs/cofusefs/Makefile +--- a/fs/cofusefs/Makefile ++++ b/fs/cofusefs/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the Linux cofuse filesystem routines. ++# ++ ++obj-$(CONFIG_COFUSE_FS) += cofusefs.o ++ ++cofusefs-objs := inode.o dir.o file.o util.o dev.o ++ +diff -urN a/fs/cofusefs/dev.c b/fs/cofusefs/dev.c +--- a/fs/cofusefs/dev.c ++++ b/fs/cofusefs/dev.c +@@ -0,0 +1,889 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++#include "fuse_i.h" ++ ++#include <linux/poll.h> ++#include <linux/proc_fs.h> ++#include <linux/file.h> ++ ++#ifndef CONFIG_COOPERATIVE ++ ++/* If more requests are outstanding, then the operation will block */ ++#define MAX_OUTSTANDING 10 ++ ++static struct proc_dir_entry *proc_fs_fuse; ++struct proc_dir_entry *proc_fuse_dev; ++static kmem_cache_t *fuse_req_cachep; ++ ++static struct fuse_req *request_new(void) ++{ ++ struct fuse_req *req; ++ ++ req = (struct fuse_req *) kmem_cache_alloc(fuse_req_cachep, SLAB_NOFS); ++ if(req) { ++ INIT_LIST_HEAD(&req->list); ++ req->issync = 0; ++ req->locked = 0; ++ req->interrupted = 0; ++ req->sent = 0; ++ req->finished = 0; ++ req->in = NULL; ++ req->out = NULL; ++ init_waitqueue_head(&req->waitq); ++ } ++ ++ return req; ++} ++ ++static void request_free(struct fuse_req *req) ++{ ++ kmem_cache_free(fuse_req_cachep, req); ++} ++ ++static int request_restartable(enum fuse_opcode opcode) ++{ ++ switch(opcode) { ++ case FUSE_LOOKUP: ++ case FUSE_GETATTR: ++ case FUSE_READLINK: ++ case FUSE_GETDIR: ++ case FUSE_OPEN: ++ case FUSE_READ: ++ case FUSE_WRITE: ++ return 1; ++ ++ default: ++ return 0; ++ } ++} ++ ++/* Called with fuse_lock held. Releases, and then reaquires it. */ ++static void request_wait_answer(struct fuse_req *req) ++{ ++ int intr; ++ ++ spin_unlock(&fuse_lock); ++ intr = wait_event_interruptible(req->waitq, req->finished); ++ spin_lock(&fuse_lock); ++ if(!intr) ++ return; ++ ++ /* Request interrupted... Wait for it to be unlocked */ ++ if(req->locked) { ++ req->interrupted = 1; ++ spin_unlock(&fuse_lock); ++ wait_event(req->waitq, !req->locked); ++ spin_lock(&fuse_lock); ++ } ++ ++ /* Operations which modify the filesystem cannot safely be ++ restarted, because it is uncertain whether the operation has ++ completed or not... */ ++ if(req->sent && !request_restartable(req->in->h.opcode)) ++ req->out->h.error = -EINTR; ++ else ++ req->out->h.error = -ERESTARTSYS; ++} ++ ++static int get_unique(struct fuse_conn *fc) ++{ ++ do fc->reqctr++; ++ while(!fc->reqctr); ++ return fc->reqctr; ++} ++ ++/* Must be called with fuse_lock held, and unlocks it */ ++static void request_end(struct fuse_conn *fc, struct fuse_req *req) ++{ ++ fuse_reqend_t endfunc = req->end; ++ ++ if(!endfunc) { ++ wake_up(&req->waitq); ++ spin_unlock(&fuse_lock); ++ } else { ++ spin_unlock(&fuse_lock); ++ endfunc(fc, req->in, req->out, req->data); ++ request_free(req); ++ up(&fc->outstanding); ++ } ++} ++ ++void request_send(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out) ++{ ++ struct fuse_req *req; ++ ++ out->h.error = -ERESTARTSYS; ++ if(down_interruptible(&fc->outstanding)) ++ return; ++ ++ out->h.error = -ENOMEM; ++ req = request_new(); ++ if(req) { ++ req->in = in; ++ req->out = out; ++ req->issync = 1; ++ req->end = NULL; ++ ++ spin_lock(&fuse_lock); ++ out->h.error = -ENOTCONN; ++ if(fc->file) { ++ in->h.unique = get_unique(fc); ++ list_add_tail(&req->list, &fc->pending); ++ wake_up(&fc->waitq); ++ request_wait_answer(req); ++ list_del(&req->list); ++ } ++ spin_unlock(&fuse_lock); ++ request_free(req); ++ } ++ ++ up(&fc->outstanding); ++} ++ ++ ++static inline void destroy_request(struct fuse_req *req) ++{ ++ if(req) { ++ kfree(req->in); ++ request_free(req); ++ } ++} ++ ++/* This one is currently only used for sending FORGET and RELEASE, ++ which are kernel initiated request. So the outstanding semaphore ++ is not used. */ ++int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in) ++{ ++ struct fuse_req *req; ++ ++ req = request_new(); ++ if(!req) ++ return -ENOMEM; ++ ++ req->in = in; ++ req->issync = 0; ++ ++ spin_lock(&fuse_lock); ++ if(!fc->file) { ++ spin_unlock(&fuse_lock); ++ request_free(req); ++ return -ENOTCONN; ++ } ++ ++ list_add_tail(&req->list, &fc->pending); ++ wake_up(&fc->waitq); ++ spin_unlock(&fuse_lock); ++ return 0; ++} ++ ++int request_send_nonblock(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out, fuse_reqend_t end, void *data) ++{ ++ int err; ++ struct fuse_req *req; ++ ++ BUG_ON(!end); ++ ++ if(down_trylock(&fc->outstanding)) ++ return -EWOULDBLOCK; ++ ++ err = -ENOMEM; ++ req = request_new(); ++ if(req) { ++ req->in = in; ++ req->out = out; ++ req->issync = 1; ++ req->end = end; ++ req->data = data; ++ ++ spin_lock(&fuse_lock); ++ err = -ENOTCONN; ++ if(fc->file) { ++ in->h.unique = get_unique(fc); ++ list_add_tail(&req->list, &fc->pending); ++ wake_up(&fc->waitq); ++ spin_unlock(&fuse_lock); ++ return 0; ++ } ++ spin_unlock(&fuse_lock); ++ request_free(req); ++ } ++ up(&fc->outstanding); ++ return err; ++} ++ ++static void request_wait(struct fuse_conn *fc) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ add_wait_queue_exclusive(&fc->waitq, &wait); ++ while(fc->sb != NULL && list_empty(&fc->pending)) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if(signal_pending(current)) ++ break; ++ ++ spin_unlock(&fuse_lock); ++ schedule(); ++ spin_lock(&fuse_lock); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&fc->waitq, &wait); ++} ++ ++static inline int copy_in_one(const void *src, size_t srclen, char **dstp, ++ size_t *dstlenp) ++{ ++ if(*dstlenp < srclen) { ++ printk("fuse_dev_read: buffer too small\n"); ++ return -EINVAL; ++ } ++ ++ if(copy_to_user(*dstp, src, srclen)) ++ return -EFAULT; ++ ++ *dstp += srclen; ++ *dstlenp -= srclen; ++ ++ return 0; ++} ++ ++static inline int copy_in_args(struct fuse_in *in, char *buf, size_t nbytes) ++{ ++ int err; ++ int i; ++ size_t orignbytes = nbytes; ++ ++ err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes); ++ if(err) ++ return err; ++ ++ for(i = 0; i < in->numargs; i++) { ++ struct fuse_in_arg *arg = &in->args[i]; ++ err = copy_in_one(arg->value, arg->size, &buf, &nbytes); ++ if(err) ++ return err; ++ } ++ ++ return orignbytes - nbytes; ++} ++ ++static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes, ++ loff_t *off) ++{ ++ ssize_t ret; ++ struct fuse_conn *fc = DEV_FC(file); ++ struct fuse_req *req = NULL; ++ ++ spin_lock(&fuse_lock); ++ request_wait(fc); ++ if(fc->sb != NULL && !list_empty(&fc->pending)) { ++ req = list_entry(fc->pending.next, struct fuse_req, list); ++ list_del_init(&req->list); ++ req->locked = 1; ++ } ++ spin_unlock(&fuse_lock); ++ if(fc->sb == NULL) ++ return -ENODEV; ++ if(req == NULL) ++ return -EINTR; ++ ++ ret = copy_in_args(req->in, buf, nbytes); ++ spin_lock(&fuse_lock); ++ if(req->issync) { ++ if(ret < 0) { ++ req->out->h.error = -EPROTO; ++ req->finished = 1; ++ } else { ++ list_add_tail(&req->list, &fc->processing); ++ req->sent = 1; ++ } ++ req->locked = 0; ++ if(ret < 0 || req->interrupted) ++ /* Unlocks fuse_lock: */ ++ request_end(fc, req); ++ else ++ spin_unlock(&fuse_lock); ++ } else { ++ spin_unlock(&fuse_lock); ++ destroy_request(req); ++ } ++ return ret; ++} ++ ++static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique) ++{ ++ struct list_head *entry; ++ struct fuse_req *req = NULL; ++ ++ list_for_each(entry, &fc->processing) { ++ struct fuse_req *tmp; ++ tmp = list_entry(entry, struct fuse_req, list); ++ if(tmp->in->h.unique == unique) { ++ req = tmp; ++ break; ++ } ++ } ++ ++ return req; ++} ++ ++static void process_getdir(struct fuse_req *req) ++{ ++ struct fuse_getdir_out *arg; ++ arg = (struct fuse_getdir_out *) req->out->args[0].value; ++ arg->file = fget(arg->fd); ++} ++ ++static inline int copy_out_one(struct fuse_out_arg *arg, const char **srcp, ++ size_t *srclenp, int allowvar) ++{ ++ size_t dstlen = arg->size; ++ if(*srclenp < dstlen) { ++ if(!allowvar) { ++ printk("fuse_dev_write: write is short\n"); ++ return -EINVAL; ++ } ++ dstlen = *srclenp; ++ } ++ ++ if(dstlen) { ++ if(copy_from_user(arg->value, *srcp, dstlen)) ++ return -EFAULT; ++ } ++ ++ *srcp += dstlen; ++ *srclenp -= dstlen; ++ arg->size = dstlen; ++ ++ return 0; ++} ++ ++static inline int copy_out_args(struct fuse_out *out, const char *buf, ++ size_t nbytes) ++{ ++ int err; ++ int i; ++ ++ buf += sizeof(struct fuse_out_header); ++ nbytes -= sizeof(struct fuse_out_header); ++ ++ if(!out->h.error) { ++ for(i = 0; i < out->numargs; i++) { ++ struct fuse_out_arg *arg = &out->args[i]; ++ int allowvar; ++ ++ if(out->argvar && i == out->numargs - 1) ++ allowvar = 1; ++ else ++ allowvar = 0; ++ ++ err = copy_out_one(arg, &buf, &nbytes, allowvar); ++ if(err) ++ return err; ++ } ++ } ++ ++ if(nbytes != 0) { ++ printk("fuse_dev_write: write is long\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline int copy_out_header(struct fuse_out_header *oh, const char *buf, ++ size_t nbytes) ++{ ++ if(nbytes < sizeof(struct fuse_out_header)) { ++ printk("fuse_dev_write: write is short\n"); ++ return -EINVAL; ++ } ++ ++ if(copy_from_user(oh, buf, sizeof(struct fuse_out_header))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++#ifdef KERNEL_2_6 ++static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh) ++{ ++ struct inode *inode = ilookup(fc->sb, uh->ino); ++ if (!inode) ++ return -ENOENT; ++ invalidate_inode_pages(inode->i_mapping); ++ iput(inode); ++ return 0; ++} ++#else ++static int fuse_invalidate(struct fuse_conn *fc, struct fuse_user_header *uh) ++{ ++ struct inode *inode = iget(fc->sb, uh->ino); ++ int err = -ENOENT; ++ if(inode) { ++ if(inode->u.generic_ip) { ++ invalidate_inode_pages(inode); ++ err = 0; ++ } ++ iput(inode); ++ } ++ return err; ++} ++#endif ++ ++static int fuse_user_request(struct fuse_conn *fc, const char *buf, ++ size_t nbytes) ++{ ++ struct fuse_user_header uh; ++ int err; ++ ++ if (nbytes < sizeof(struct fuse_user_header)) { ++ printk("fuse_dev_write: write is short\n"); ++ return -EINVAL; ++ } ++ ++ if(copy_from_user(&uh, buf, sizeof(struct fuse_out_header))) ++ return -EFAULT; ++ ++ switch(uh.opcode) { ++ case FUSE_INVALIDATE: ++ err = fuse_invalidate(fc, &uh); ++ break; ++ ++ default: ++ err = -ENOSYS; ++ } ++ return err; ++} ++ ++ ++static ssize_t fuse_dev_write(struct file *file, const char *buf, ++ size_t nbytes, loff_t *off) ++{ ++ int err; ++ struct fuse_conn *fc = DEV_FC(file); ++ struct fuse_req *req; ++ struct fuse_out_header oh; ++ ++ if(!fc->sb) ++ return -EPERM; ++ ++ err = copy_out_header(&oh, buf, nbytes); ++ if(err) ++ return err; ++ ++ if (!oh.unique) { ++ err = fuse_user_request(fc, buf, nbytes); ++ goto out; ++ } ++ ++ if (oh.error <= -512 || oh.error > 0) { ++ printk("fuse_dev_write: bad error value\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock(&fuse_lock); ++ req = request_find(fc, oh.unique); ++ if(req != NULL) { ++ list_del_init(&req->list); ++ req->locked = 1; ++ } ++ spin_unlock(&fuse_lock); ++ if(!req) ++ return -ENOENT; ++ ++ req->out->h = oh; ++ err = copy_out_args(req->out, buf, nbytes); ++ ++ spin_lock(&fuse_lock); ++ if(err) ++ req->out->h.error = -EPROTO; ++ else { ++ /* fget() needs to be done in this context */ ++ if(req->in->h.opcode == FUSE_GETDIR && !oh.error) ++ process_getdir(req); ++ } ++ req->finished = 1; ++ req->locked = 0; ++ /* Unlocks fuse_lock: */ ++ request_end(fc, req); ++ ++ out: ++ if(!err) ++ return nbytes; ++ else ++ return err; ++} ++ ++ ++static unsigned int fuse_dev_poll(struct file *file, poll_table *wait) ++{ ++ struct fuse_conn *fc = DEV_FC(file); ++ unsigned int mask = POLLOUT | POLLWRNORM; ++ ++ if(!fc->sb) ++ return -EPERM; ++ ++ poll_wait(file, &fc->waitq, wait); ++ ++ spin_lock(&fuse_lock); ++ if (!list_empty(&fc->pending)) ++ mask |= POLLIN | POLLRDNORM; ++ spin_unlock(&fuse_lock); ++ ++ return mask; ++} ++ ++static struct fuse_conn *new_conn(void) ++{ ++ struct fuse_conn *fc; ++ ++ fc = kmalloc(sizeof(*fc), GFP_KERNEL); ++ if(fc != NULL) { ++ fc->sb = NULL; ++ fc->file = NULL; ++ fc->flags = 0; ++ fc->uid = 0; ++ fc->oldrelease = 0; ++ init_waitqueue_head(&fc->waitq); ++ INIT_LIST_HEAD(&fc->pending); ++ INIT_LIST_HEAD(&fc->processing); ++ sema_init(&fc->outstanding, MAX_OUTSTANDING); ++ fc->reqctr = 1; ++ } ++ return fc; ++} ++ ++static int fuse_dev_open(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc; ++ ++ fc = new_conn(); ++ if(!fc) ++ return -ENOMEM; ++ ++ fc->file = file; ++ file->private_data = fc; ++ ++ return 0; ++} ++ ++static void end_requests(struct fuse_conn *fc, struct list_head *head) ++{ ++ while(!list_empty(head)) { ++ struct fuse_req *req; ++ req = list_entry(head->next, struct fuse_req, list); ++ list_del_init(&req->list); ++ if(req->issync) { ++ req->out->h.error = -ECONNABORTED; ++ req->finished = 1; ++ /* Unlocks fuse_lock: */ ++ request_end(fc, req); ++ spin_lock(&fuse_lock); ++ } else ++ destroy_request(req); ++ } ++} ++ ++static int fuse_dev_release(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = DEV_FC(file); ++ ++ spin_lock(&fuse_lock); ++ fc->file = NULL; ++ end_requests(fc, &fc->pending); ++ end_requests(fc, &fc->processing); ++ release_conn(fc); ++ spin_unlock(&fuse_lock); ++ return 0; ++} ++ ++static struct file_operations fuse_dev_operations = { ++ .owner = THIS_MODULE, ++ .read = fuse_dev_read, ++ .write = fuse_dev_write, ++ .poll = fuse_dev_poll, ++ .open = fuse_dev_open, ++ .release = fuse_dev_release, ++}; ++ ++int fuse_dev_init() ++{ ++ int ret; ++ ++ proc_fs_fuse = NULL; ++ proc_fuse_dev = NULL; ++ ++ fuse_req_cachep = kmem_cache_create("cofuser_request", ++ sizeof(struct fuse_req), ++ 0, 0, NULL, NULL); ++ if(!fuse_req_cachep) ++ return -ENOMEM; ++ ++ ret = -ENOMEM; ++ proc_fs_fuse = proc_mkdir("fuse", proc_root_fs); ++ if(!proc_fs_fuse) { ++ printk("fuse: failed to create directory in /proc/fs\n"); ++ goto err; ++ } ++ ++ proc_fs_fuse->owner = THIS_MODULE; ++ proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | 0600, proc_fs_fuse); ++ if(!proc_fuse_dev) { ++ printk("fuse: failed to create entry in /proc/fs/fuse\n"); ++ goto err; ++ } ++ ++ proc_fuse_dev->proc_fops = &fuse_dev_operations; ++ ++ return 0; ++ ++ err: ++ fuse_dev_cleanup(); ++ return ret; ++} ++ ++void fuse_dev_cleanup() ++{ ++ if (cooperative_mode_enabled()) { ++ kmem_cache_destroy(fuse_req_cachep); ++ return; ++ } ++ ++ if(proc_fs_fuse) { ++ remove_proc_entry("dev", proc_fs_fuse); ++ remove_proc_entry("fuse", proc_root_fs); ++ } ++ ++ kmem_cache_destroy(fuse_req_cachep); ++} ++ ++#else ++ ++struct fuse_conn *cofs_volumes[CO_MODULE_MAX_COFS] = {NULL, }; ++ ++static void cofuse_request_start(unsigned long *flags, struct fuse_conn *fc, struct fuse_in *in) ++{ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = fc->cofs_unit; ++ co_passage_page->params[2] = in->h.opcode; ++ co_passage_page->params[3] = in->h.ino; ++ co_passage_page->params[4] = 0; ++} ++ ++static void cofuse_request_end(unsigned long flags, struct fuse_out *out) ++{ ++ unsigned long ret; ++ ret = co_passage_page->params[4]; ++ co_passage_page_release(flags); ++ out->h.error = ret; ++} ++ ++void request_send(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out) ++{ ++ unsigned long flags; ++ char *str; ++ ++ switch ((unsigned long)in->h.opcode) { ++ case FUSE_STATFS: { ++ struct fuse_statfs_out *arg; ++ ++ arg = (struct fuse_statfs_out *)out->args[0].value; ++ ++ cofuse_request_start(&flags, fc, in); ++ co_switch_wrapper(); ++ *arg = *(struct fuse_statfs_out *)&co_passage_page->params[5]; ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_OPEN: { ++ struct fuse_open_in *opin = (struct fuse_open_in *)in->args[0].value; ++ ++ cofuse_request_start(&flags, fc, in); ++ co_passage_page->params[5] = opin->flags; ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_WRITE: { ++ struct fuse_write_in *write_in = (struct fuse_write_in *)in->args[0].value; ++ unsigned long long *offset_passage = (unsigned long long *)&co_passage_page->params[5]; ++ ++ cofuse_request_start(&flags, fc, in); ++ *offset_passage = write_in->offset; ++ co_passage_page->params[7] = write_in->size; ++ co_passage_page->params[8] = (unsigned long)in->args[1].value; ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_READ: { ++ struct fuse_read_in *read_in = (struct fuse_read_in *)in->args[0].value; ++ unsigned long long *offset_passage = (unsigned long long *)&co_passage_page->params[5]; ++ ++ cofuse_request_start(&flags, fc, in); ++ *offset_passage = read_in->offset; ++ co_passage_page->params[7] = read_in->size; ++ co_passage_page->params[8] = (unsigned long)out->args[0].value; ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_LOOKUP: { ++ struct fuse_lookup_out *arg; ++ ++ arg = (struct fuse_lookup_out *)out->args[0].value; ++ str = (char *)&co_passage_page->params[30]; ++ ++ cofuse_request_start(&flags, fc, in); ++ memcpy(str, (char *)in->args[0].value, in->args[0].size); ++ co_switch_wrapper(); ++ *arg = *(struct fuse_lookup_out *)&co_passage_page->params[5]; ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_RENAME: { ++ struct fuse_rename_in *arg; ++ char *str2; ++ ++ arg = (struct fuse_rename_in *)in->args[0].value; ++ str = (char *)(&co_passage_page->params[30]); ++ str2 = str + in->args[1].size; ++ ++ cofuse_request_start(&flags, fc, in); ++ co_passage_page->params[5] = arg->newdir; ++ memcpy(str, (char *)in->args[1].value, in->args[1].size); ++ memcpy(str2, (char *)in->args[2].value, in->args[2].size); ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_MKNOD: { ++ struct fuse_mknod_in *inarg; ++ struct fuse_mknod_out *outarg; ++ char *str; ++ ++ inarg = (struct fuse_mknod_in *)(in->args[0].value); ++ outarg = (struct fuse_mknod_out *)(out->args[0].value); ++ ++ cofuse_request_start(&flags, fc, in); ++ co_passage_page->params[5] = inarg->mode; ++ co_passage_page->params[6] = inarg->rdev; ++ str = (char *)&co_passage_page->params[30]; ++ memcpy(str, (char *)in->args[1].value, in->args[1].size); ++ co_switch_wrapper(); ++ outarg->ino = co_passage_page->params[7]; ++ outarg->attr = *(struct fuse_attr *)(&co_passage_page->params[8]); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_SETATTR: { ++ struct fuse_setattr_in *inarg; ++ struct fuse_setattr_out *outarg; ++ struct fuse_attr *attr; ++ ++ inarg = (struct fuse_setattr_in *)(in->args[0].value); ++ outarg = (struct fuse_setattr_out *)(out->args[0].value); ++ attr = (struct fuse_attr *)(&co_passage_page->params[6]); ++ ++ cofuse_request_start(&flags, fc, in); ++ co_passage_page->params[5] = inarg->valid; ++ *attr = inarg->attr; ++ co_switch_wrapper(); ++ outarg->attr = *attr; ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_MKDIR: { ++ struct fuse_mkdir_in *arg; ++ ++ arg = (struct fuse_mkdir_in *)(in->args[0].value); ++ str = (char *)&co_passage_page->params[30]; ++ ++ cofuse_request_start(&flags, fc, in); ++ co_passage_page->params[5] = arg->mode; ++ memcpy(str, (char *)in->args[1].value, in->args[1].size); ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_UNLINK: ++ case FUSE_RMDIR: { ++ str = (char *)&co_passage_page->params[30]; ++ ++ cofuse_request_start(&flags, fc, in); ++ memcpy(str, (char *)in->args[0].value, in->args[0].size); ++ co_switch_wrapper(); ++ cofuse_request_end(flags, out); ++ return; ++ } ++ ++ case FUSE_GETATTR: { ++ struct fuse_getattr_out *arg; ++ arg = (struct fuse_getattr_out *)out->args[0].value; ++ ++ co_passage_page_assert_valid(); ++ cofuse_request_start(&flags, fc, in); ++ co_switch_wrapper(); ++ *arg = *(struct fuse_getattr_out *)&co_passage_page->params[5]; ++ cofuse_request_end(flags, out); ++ return; ++ } ++ } ++ ++ /* printk("cofuse: request_send %d\n", in->h.opcode); */ ++ out->h.error = -EIO; ++} ++ ++int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in) ++{ ++ return -EIO; ++} ++ ++int request_send_nonblock(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out, fuse_reqend_t end, void *data) ++{ ++ /* printk("cofuse: request_send_nonblock %d\n", in->h.opcode); */ ++ request_send(fc, in, out); ++ end(fc, in, out, data); ++ return 0; ++} ++ ++int fuse_dev_init() ++{ ++ return 0; ++} ++ ++void fuse_dev_cleanup() ++{ ++} ++ ++#endif ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/fs/cofusefs/dir.c b/fs/cofusefs/dir.c +--- a/fs/cofusefs/dir.c ++++ b/fs/cofusefs/dir.c +@@ -0,0 +1,961 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++#include "fuse_i.h" ++ ++#include <linux/pagemap.h> ++#include <linux/slab.h> ++#include <linux/file.h> ++ ++static struct inode_operations fuse_dir_inode_operations; ++static struct inode_operations fuse_file_inode_operations; ++static struct inode_operations fuse_symlink_inode_operations; ++ ++static struct file_operations fuse_dir_operations; ++ ++static struct dentry_operations fuse_dentry_operations; ++ ++/* FIXME: This should be user configurable */ ++#define FUSE_REVALIDATE_TIME (1 * HZ) ++ ++#ifndef KERNEL_2_6 ++#define new_decode_dev(x) (x) ++#define new_encode_dev(x) (x) ++#endif ++ ++static void change_attributes(struct inode *inode, struct fuse_attr *attr) ++{ ++ if(S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) { ++#ifdef KERNEL_2_6 ++ invalidate_inode_pages(inode->i_mapping); ++#else ++ invalidate_inode_pages(inode); ++#endif ++ } ++ ++ inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); ++ inode->i_nlink = attr->nlink; ++ inode->i_uid = attr->uid; ++ inode->i_gid = attr->gid; ++ i_size_write(inode, attr->size); ++ inode->i_blksize = PAGE_CACHE_SIZE; ++ inode->i_blocks = attr->blocks; ++#ifdef KERNEL_2_6 ++ inode->i_atime.tv_sec = attr->atime; ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = attr->mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = attr->ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_atime = attr->atime; ++ inode->i_mtime = attr->mtime; ++ inode->i_ctime = attr->ctime; ++#endif ++} ++ ++static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) ++{ ++ inode->i_mode = attr->mode & S_IFMT; ++ i_size_write(inode, attr->size); ++ if(S_ISREG(inode->i_mode)) { ++ inode->i_op = &fuse_file_inode_operations; ++ fuse_init_file_inode(inode); ++ } ++ else if(S_ISDIR(inode->i_mode)) { ++ inode->i_op = &fuse_dir_inode_operations; ++ inode->i_fop = &fuse_dir_operations; ++ } ++ else if(S_ISLNK(inode->i_mode)) { ++ inode->i_op = &fuse_symlink_inode_operations; ++ } ++ else { ++ inode->i_op = &fuse_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, ++ new_decode_dev(attr->rdev)); ++ } ++ inode->u.generic_ip = inode; ++} ++ ++struct inode *fuse_iget(struct super_block *sb, ino_t ino, ++ struct fuse_attr *attr, int version) ++{ ++ struct inode *inode; ++ ++ inode = iget(sb, ino); ++ if(inode) { ++ if(!inode->u.generic_ip) ++ fuse_init_inode(inode, attr); ++ ++ change_attributes(inode, attr); ++ inode->i_version = version; ++ } ++ ++ return inode; ++} ++ ++static int fuse_do_lookup(struct inode *dir, struct dentry *entry, ++ struct fuse_lookup_out *outarg, int *version) ++{ ++ struct fuse_conn *fc = INO_FC(dir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ ++ if (entry->d_name.len > FUSE_NAME_MAX) ++ return -ENAMETOOLONG; ++ ++ in.h.opcode = FUSE_LOOKUP; ++ in.h.ino = dir->i_ino; ++ in.numargs = 1; ++ in.args[0].size = entry->d_name.len + 1; ++ in.args[0].value = entry->d_name.name; ++ out.numargs = 1; ++ out.args[0].size = sizeof(struct fuse_lookup_out); ++ out.args[0].value = outarg; ++ request_send(fc, &in, &out); ++ ++ *version = out.h.unique; ++ return out.h.error; ++} ++ ++static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, ++ struct inode **inodep) ++{ ++ int err; ++ struct fuse_lookup_out outarg; ++ int version; ++ struct inode *inode = NULL; ++ ++ err = fuse_do_lookup(dir, entry, &outarg, &version); ++ if(!err) { ++ inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, version); ++ if(!inode) ++ return -ENOMEM; ++ } else if(err != -ENOENT) ++ return err; ++ ++ entry->d_time = jiffies; ++ entry->d_op = &fuse_dentry_operations; ++ *inodep = inode; ++ return 0; ++} ++ ++static void uncache_dir(struct inode *dir) ++{ ++ struct dentry *entry = d_find_alias(dir); ++ if (!entry) ++ dir->i_nlink = 0; ++ else { ++ entry->d_time = jiffies - FUSE_REVALIDATE_TIME - 1; ++ dput(entry); ++ } ++} ++ ++/* create needs to return a positive entry, so this is actually an ++ mknod+lookup */ ++static int _fuse_mknod(struct inode *dir, struct dentry *entry, int mode, ++ dev_t rdev) ++{ ++ struct fuse_conn *fc = INO_FC(dir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_mknod_in inarg; ++ struct fuse_mknod_out outarg; ++ struct inode *inode; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.mode = mode; ++ inarg.rdev = new_encode_dev(rdev); ++ ++ in.h.opcode = FUSE_MKNOD; ++ in.h.ino = dir->i_ino; ++ in.numargs = 2; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ in.args[1].size = entry->d_name.len + 1; ++ in.args[1].value = entry->d_name.name; ++ out.numargs = 1; ++ out.args[0].size = sizeof(outarg); ++ out.args[0].value = &outarg; ++ request_send(fc, &in, &out); ++ ++ if(out.h.error) ++ return out.h.error; ++ ++ inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr, out.h.unique); ++ if(!inode) ++ return -ENOMEM; ++ ++ /* Don't allow userspace to do really stupid things... */ ++ if((inode->i_mode ^ mode) & S_IFMT) { ++ iput(inode); ++ printk("fuse_mknod: inode has wrong type\n"); ++ return -EPROTO; ++ } ++ ++ d_instantiate(entry, inode); ++ uncache_dir(dir); ++ return 0; ++} ++ ++static int _fuse_create(struct inode *dir, struct dentry *entry, int mode) ++{ ++ return _fuse_mknod(dir, entry, mode, 0); ++} ++ ++/* knfsd needs the new entry instantiated in mkdir/symlink/link. this ++ should rather be done like mknod: attributes returned in out arg to ++ save a call to userspace */ ++static int lookup_new_entry(struct inode *dir, struct dentry *entry) ++{ ++ struct inode *inode; ++ int err = fuse_lookup_iget(dir, entry, &inode); ++ if(err || !inode) { ++ printk("fuse_mkdir: failed to look up new entry\n"); ++ return err ? err : -ENOENT; ++ } ++ d_instantiate(entry, inode); ++ uncache_dir(dir); ++ return 0; ++} ++ ++static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) ++{ ++ struct fuse_conn *fc = INO_FC(dir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_mkdir_in inarg; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.mode = mode; ++ ++ in.h.opcode = FUSE_MKDIR; ++ in.h.ino = dir->i_ino; ++ in.numargs = 2; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ in.args[1].size = entry->d_name.len + 1; ++ in.args[1].value = entry->d_name.name; ++ request_send(fc, &in, &out); ++ if(out.h.error) ++ return out.h.error; ++ ++ return lookup_new_entry(dir, entry); ++} ++ ++static int fuse_symlink(struct inode *dir, struct dentry *entry, ++ const char *link) ++{ ++ struct fuse_conn *fc = INO_FC(dir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ unsigned int len = strlen(link) + 1; ++ ++ if (len > FUSE_SYMLINK_MAX) ++ return -ENAMETOOLONG; ++ ++ in.h.opcode = FUSE_SYMLINK; ++ in.h.ino = dir->i_ino; ++ in.numargs = 2; ++ in.args[0].size = entry->d_name.len + 1; ++ in.args[0].value = entry->d_name.name; ++ in.args[1].size = len; ++ in.args[1].value = link; ++ request_send(fc, &in, &out); ++ if(out.h.error) ++ return out.h.error; ++ ++ return lookup_new_entry(dir, entry); ++} ++ ++static int fuse_remove(struct inode *dir, struct dentry *entry, ++ enum fuse_opcode op) ++{ ++ struct fuse_conn *fc = INO_FC(dir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ ++ in.h.opcode = op; ++ in.h.ino = dir->i_ino; ++ in.numargs = 1; ++ in.args[0].size = entry->d_name.len + 1; ++ in.args[0].value = entry->d_name.name; ++ request_send(fc, &in, &out); ++ ++ return out.h.error; ++} ++ ++static int fuse_unlink(struct inode *dir, struct dentry *entry) ++{ ++ int err = fuse_remove(dir, entry, FUSE_UNLINK); ++ if(!err) { ++ /* FIXME: the new i_nlink could be returned by the ++ unlink operation */ ++ err = fuse_do_getattr(entry->d_inode); ++ if(err == -ENOENT) ++ entry->d_inode->i_nlink = 0; ++ ++ uncache_dir(dir); ++ return 0; ++ } ++ return err; ++} ++ ++static int fuse_rmdir(struct inode *dir, struct dentry *entry) ++{ ++ int err = fuse_remove(dir, entry, FUSE_RMDIR); ++ if(!err) { ++ entry->d_inode->i_nlink = 0; ++ uncache_dir(dir); ++ } ++ return err; ++} ++ ++static int fuse_rename(struct inode *olddir, struct dentry *oldent, ++ struct inode *newdir, struct dentry *newent) ++{ ++ struct fuse_conn *fc = INO_FC(olddir); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_rename_in inarg; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.newdir = newdir->i_ino; ++ ++ in.h.opcode = FUSE_RENAME; ++ in.h.ino = olddir->i_ino; ++ in.numargs = 3; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ in.args[1].size = oldent->d_name.len + 1; ++ in.args[1].value = oldent->d_name.name; ++ in.args[2].size = newent->d_name.len + 1; ++ in.args[2].value = newent->d_name.name; ++ request_send(fc, &in, &out); ++ ++ if (!out.h.error) { ++ uncache_dir(olddir); ++ if (olddir != newdir) ++ uncache_dir(newdir); ++ } ++ ++ return out.h.error; ++} ++ ++static int fuse_link(struct dentry *entry, struct inode *newdir, ++ struct dentry *newent) ++{ ++ struct inode *inode = entry->d_inode; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_link_in inarg; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.newdir = newdir->i_ino; ++ ++ in.h.opcode = FUSE_LINK; ++ in.h.ino = inode->i_ino; ++ in.numargs = 2; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ in.args[1].size = newent->d_name.len + 1; ++ in.args[1].value = newent->d_name.name; ++ request_send(fc, &in, &out); ++ if(out.h.error) ++ return out.h.error; ++ ++ /* Invalidate old entry, so attributes are refreshed */ ++ d_invalidate(entry); ++ return lookup_new_entry(newdir, newent); ++} ++ ++int fuse_do_getattr(struct inode *inode) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_getattr_out arg; ++ ++ in.h.opcode = FUSE_GETATTR; ++ in.h.ino = inode->i_ino; ++ out.numargs = 1; ++ out.args[0].size = sizeof(arg); ++ out.args[0].value = &arg; ++ request_send(fc, &in, &out); ++ ++ if(!out.h.error) ++ change_attributes(inode, &arg.attr); ++ ++ return out.h.error; ++} ++ ++static int fuse_revalidate(struct dentry *entry) ++{ ++ struct inode *inode = entry->d_inode; ++ struct fuse_conn *fc = INO_FC(inode); ++ ++ if(inode->i_ino == FUSE_ROOT_INO) { ++ if(!(fc->flags & FUSE_ALLOW_OTHER) && ++ current->fsuid != fc->uid) ++ return -EACCES; ++ } else if(time_before_eq(jiffies, entry->d_time + FUSE_REVALIDATE_TIME)) ++ return 0; ++ ++ return fuse_do_getattr(inode); ++} ++ ++static int _fuse_permission(struct inode *inode, int mask) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ ++ if(!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->uid) ++ return -EACCES; ++ else if(fc->flags & FUSE_DEFAULT_PERMISSIONS) { ++ int err = generic_permission(inode, mask, NULL); ++ ++ /* If permission is denied, try to refresh file ++ attributes. This is also needed, because the root ++ node will at first have no permissions */ ++ ++ if(err == -EACCES) { ++ err = fuse_do_getattr(inode); ++ if(!err) ++ err = generic_permission(inode, mask, NULL); ++ } ++ ++ /* FIXME: Need some mechanism to revoke permissions: ++ currently if the filesystem suddenly changes the ++ file mode, we will not be informed abot that, and ++ continue to allow access to the file/directory. ++ ++ This is actually not so grave, since the user can ++ simply keep access to the file/directory anyway by ++ keeping it open... */ ++ ++ return err; ++ } ++ else ++ return 0; ++} ++ ++static int parse_dirfile(char *buf, size_t nbytes, struct file *file, ++ void *dstbuf, filldir_t filldir) ++{ ++ while(nbytes >= FUSE_NAME_OFFSET) { ++ struct fuse_dirent *dirent = (struct fuse_dirent *) buf; ++ size_t reclen = FUSE_DIRENT_SIZE(dirent); ++ int over; ++ ++ if(dirent->namelen > NAME_MAX) { ++ printk("fuse_readdir: name too long\n"); ++ return -EPROTO; ++ } ++ if(reclen > nbytes) ++ break; ++ ++ over = filldir(dstbuf, dirent->name, dirent->namelen, ++ file->f_pos, dirent->ino, dirent->type); ++ if(over) ++ break; ++ ++ buf += reclen; ++ file->f_pos += reclen; ++ nbytes -= reclen; ++ } ++ ++ return 0; ++} ++ ++#ifndef CONFIG_COOPERATIVE ++ ++#define DIR_BUFSIZE 2048 ++static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) ++{ ++ struct file *cfile = file->private_data; ++ char *buf; ++ int ret; ++ ++ if(!cfile) ++ return -EISDIR; ++ ++ buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL); ++ if(!buf) ++ return -ENOMEM; ++ ++ ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE); ++ if(ret < 0) ++ printk("fuse_readdir: failed to read container file\n"); ++ else ++ ret = parse_dirfile(buf, ret, file, dstbuf, filldir); ++ ++ kfree(buf); ++ return ret; ++} ++ ++#else ++ ++#define DIR_BUFSIZE 4096 ++ ++typedef struct { ++ struct fuse_conn *fc; ++ int inode; ++} readdir_data_t; ++ ++static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) ++{ ++ readdir_data_t *rd = file->private_data; ++ unsigned long flags; ++ int ret, size; ++ char *buf; ++ ++ buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = rd->fc->cofs_unit; ++ co_passage_page->params[2] = FUSE_DIR_READ; ++ co_passage_page->params[3] = rd->inode; ++ co_passage_page->params[5] = DIR_BUFSIZE; ++ co_passage_page->params[6] = (unsigned long)buf; ++ co_passage_page->params[8] = file->f_pos; ++ ++ co_switch_wrapper(); ++ ++ ret = co_passage_page->params[4]; ++ size = co_passage_page->params[7]; ++ ++ co_passage_page_release(flags); ++ ++ if (ret) { ++ printk("fuse_readdir: host returned error: %x\n", ret); ++ kfree(buf); ++ return ret; ++ } ++ ++ parse_dirfile(buf, size, file, dstbuf, filldir); ++ ++ ret = 0; ++ kfree(buf); ++ return ret; ++} ++ ++#endif ++ ++static char *read_link(struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ char *link; ++ ++ link = (char *) __get_free_page(GFP_KERNEL); ++ if(!link) ++ return ERR_PTR(-ENOMEM); ++ ++ in.h.opcode = FUSE_READLINK; ++ in.h.ino = inode->i_ino; ++ out.argvar = 1; ++ out.numargs = 1; ++ out.args[0].size = PAGE_SIZE - 1; ++ out.args[0].value = link; ++ request_send(fc, &in, &out); ++ if(out.h.error) { ++ free_page((unsigned long) link); ++ return ERR_PTR(out.h.error); ++ } ++ ++ link[out.args[0].size] = '\0'; ++ return link; ++} ++ ++static void free_link(char *link) ++{ ++ if(!IS_ERR(link)) ++ free_page((unsigned long) link); ++} ++ ++static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen) ++{ ++ int ret; ++ char *link; ++ ++ link = read_link(dentry); ++ ret = vfs_readlink(dentry, buffer, buflen, link); ++ free_link(link); ++ return ret; ++} ++ ++static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret; ++ char *link; ++ ++ link = read_link(dentry); ++ ret = vfs_follow_link(nd, link); ++ free_link(link); ++ return ret; ++} ++ ++#ifndef CONFIG_COOPERATIVE ++ ++static int fuse_dir_open(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_getdir_out outarg; ++ ++ in.h.opcode = FUSE_GETDIR; ++ in.h.ino = inode->i_ino; ++ out.numargs = 1; ++ out.args[0].size = sizeof(outarg); ++ out.args[0].value = &outarg; ++ request_send(fc, &in, &out); ++ if(!out.h.error) { ++ struct file *cfile = outarg.file; ++ struct inode *inode; ++ if(!cfile) { ++ printk("fuse_getdir: invalid file\n"); ++ return -EPROTO; ++ } ++ inode = cfile->f_dentry->d_inode; ++ if(!S_ISREG(inode->i_mode)) { ++ printk("fuse_getdir: not a regular file\n"); ++ fput(cfile); ++ return -EPROTO; ++ } ++ ++ file->private_data = cfile; ++ } ++ ++ return out.h.error; ++} ++ ++static int fuse_dir_release(struct inode *inode, struct file *file) ++{ ++ struct file *cfile = file->private_data; ++ ++ if(cfile) ++ fput(cfile); ++ ++ return 0; ++} ++ ++#else ++ ++static int fuse_dir_open(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ unsigned long flags; ++ readdir_data_t *rd; ++ int ret; ++ ++ rd = kmalloc(sizeof(*rd), GFP_KERNEL); ++ if (!rd) ++ return -ENOMEM; ++ ++ rd->fc = fc; ++ rd->inode = inode->i_ino; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = fc->cofs_unit; ++ co_passage_page->params[2] = FUSE_DIR_OPEN; ++ co_passage_page->params[3] = inode->i_ino; ++ co_passage_page->params[4] = 0; ++ ++ co_switch_wrapper(); ++ ++ ret = co_passage_page->params[4]; ++ ++ co_passage_page_release(flags); ++ ++ if (ret) { ++ printk("fuse_readdir: host returned error: %x\n", ret); ++ kfree(rd); ++ } else { ++ file->private_data = (void *)rd; ++ } ++ ++ return ret; ++} ++ ++static int fuse_dir_release(struct inode *inode, struct file *file) ++{ ++ readdir_data_t *rd = file->private_data; ++ unsigned long flags; ++ int ret; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = rd->fc->cofs_unit; ++ co_passage_page->params[2] = FUSE_DIR_RELEASE; ++ co_passage_page->params[3] = rd->inode; ++ co_passage_page->params[4] = 0; ++ ++ co_switch_wrapper(); ++ ++ ret = co_passage_page->params[4]; ++ ++ co_passage_page_release(flags); ++ ++ if (ret) { ++ printk("fuse_readdir: host returned error: %x\n", ret); ++ } ++ ++ kfree(rd); ++ ++ return ret; ++} ++ ++#endif ++ ++static unsigned int iattr_to_fattr(struct iattr *iattr, ++ struct fuse_attr *fattr) ++{ ++ unsigned int ivalid = iattr->ia_valid; ++ unsigned int fvalid = 0; ++ ++ memset(fattr, 0, sizeof(*fattr)); ++ ++ if(ivalid & ATTR_MODE) ++ fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode; ++ if(ivalid & ATTR_UID) ++ fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid; ++ if(ivalid & ATTR_GID) ++ fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid; ++ if(ivalid & ATTR_SIZE) ++ fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size; ++ /* You can only _set_ these together (they may change by themselves) */ ++ if((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { ++ fvalid |= FATTR_UTIME; ++#ifdef KERNEL_2_6 ++ fattr->atime = iattr->ia_atime.tv_sec; ++ fattr->mtime = iattr->ia_mtime.tv_sec; ++#else ++ fattr->atime = iattr->ia_atime; ++ fattr->mtime = iattr->ia_mtime; ++#endif ++ } ++ ++ return fvalid; ++} ++ ++static int fuse_setattr(struct dentry *entry, struct iattr *attr) ++{ ++ struct inode *inode = entry->d_inode; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_setattr_in inarg; ++ struct fuse_setattr_out outarg; ++ ++ /* FIXME: need to fix race between truncate and writepage */ ++ if (attr->ia_valid & ATTR_SIZE) ++ fuse_sync_inode(inode); ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.valid = iattr_to_fattr(attr, &inarg.attr); ++ ++ in.h.opcode = FUSE_SETATTR; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ out.numargs = 1; ++ out.args[0].size = sizeof(outarg); ++ out.args[0].value = &outarg; ++ request_send(fc, &in, &out); ++ ++ if(!out.h.error) { ++ if(attr->ia_valid & ATTR_SIZE && ++ outarg.attr.size < i_size_read(inode)) ++ vmtruncate(inode, outarg.attr.size); ++ ++ change_attributes(inode, &outarg.attr); ++ } ++ return out.h.error; ++} ++ ++static int _fuse_dentry_revalidate(struct dentry *entry) ++{ ++ if(!entry->d_inode) ++ return 0; ++ else if(time_after(jiffies, entry->d_time + FUSE_REVALIDATE_TIME)) { ++ struct inode *inode = entry->d_inode; ++ struct fuse_lookup_out outarg; ++ int version; ++ int ret; ++ ++ ret = fuse_do_lookup(entry->d_parent->d_inode, entry, &outarg, ++ &version); ++ if(ret) ++ return 0; ++ ++ if(outarg.ino != inode->i_ino) ++ return 0; ++ ++ change_attributes(inode, &outarg.attr); ++ inode->i_version = version; ++ entry->d_time = jiffies; ++ } ++ return 1; ++} ++ ++#ifdef KERNEL_2_6 ++ ++#define fuse_mknod _fuse_mknod ++ ++static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, ++ struct kstat *stat) ++{ ++ struct inode *inode = entry->d_inode; ++ int err = fuse_revalidate(entry); ++ if(!err) ++ generic_fillattr(inode, stat); ++ ++ return err; ++} ++ ++static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, ++ struct nameidata *nd) ++{ ++ struct inode *inode; ++ int err = fuse_lookup_iget(dir, entry, &inode); ++ if (err) ++ return ERR_PTR(err); ++ return d_splice_alias(inode, entry); ++} ++ ++static int fuse_create(struct inode *dir, struct dentry *entry, int mode, ++ struct nameidata *nd) ++{ ++ return _fuse_create(dir, entry, mode); ++} ++ ++static int fuse_permission(struct inode *inode, int mask, ++ struct nameidata *nd) ++{ ++ return _fuse_permission(inode, mask); ++} ++ ++static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) ++{ ++ return _fuse_dentry_revalidate(entry); ++} ++#else /* KERNEL_2_6 */ ++ ++#define fuse_create _fuse_create ++#define fuse_permission _fuse_permission ++ ++static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry) ++{ ++ struct inode *inode; ++ struct dentry *alias; ++ ++ int err = fuse_lookup_iget(dir, entry, &inode); ++ if(err) ++ return ERR_PTR(err); ++ ++ if(inode && S_ISDIR(inode->i_mode) && ++ (alias = d_find_alias(inode)) != NULL) { ++ dput(alias); ++ iput(inode); ++ printk("fuse: cannot assign an existing directory\n"); ++ return ERR_PTR(-EPROTO); ++ } ++ ++ d_add(entry, inode); ++ return NULL; ++} ++ ++static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, ++ int rdev) ++{ ++ return _fuse_mknod(dir, entry, mode, rdev); ++} ++ ++static int fuse_dentry_revalidate(struct dentry *entry, int flags) ++{ ++ return _fuse_dentry_revalidate(entry); ++} ++#endif /* KERNEL_2_6 */ ++ ++ ++static struct inode_operations fuse_dir_inode_operations = ++{ ++ .lookup = fuse_lookup, ++ .create = fuse_create, ++ .mknod = fuse_mknod, ++ .mkdir = fuse_mkdir, ++ .symlink = fuse_symlink, ++ .unlink = fuse_unlink, ++ .rmdir = fuse_rmdir, ++ .rename = fuse_rename, ++ .link = fuse_link, ++ .setattr = fuse_setattr, ++ .permission = fuse_permission, ++#ifdef KERNEL_2_6 ++ .getattr = fuse_getattr, ++#else ++ .revalidate = fuse_revalidate, ++#endif ++}; ++ ++static struct file_operations fuse_dir_operations = { ++ .read = generic_read_dir, ++ .readdir = fuse_readdir, ++ .open = fuse_dir_open, ++ .release = fuse_dir_release, ++}; ++ ++static struct inode_operations fuse_file_inode_operations = { ++ .setattr = fuse_setattr, ++ .permission = fuse_permission, ++#ifdef KERNEL_2_6 ++ .getattr = fuse_getattr, ++#else ++ .revalidate = fuse_revalidate, ++#endif ++}; ++ ++static struct inode_operations fuse_symlink_inode_operations = ++{ ++ .setattr = fuse_setattr, ++ .readlink = fuse_readlink, ++ .follow_link = fuse_follow_link, ++#ifdef KERNEL_2_6 ++ .getattr = fuse_getattr, ++#else ++ .revalidate = fuse_revalidate, ++#endif ++}; ++ ++static struct dentry_operations fuse_dentry_operations = { ++ .d_revalidate = fuse_dentry_revalidate, ++}; ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/fs/cofusefs/file.c b/fs/cofusefs/file.c +--- a/fs/cofusefs/file.c ++++ b/fs/cofusefs/file.c +@@ -0,0 +1,542 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++#include "fuse_i.h" ++ ++#include <linux/pagemap.h> ++#include <linux/slab.h> ++#ifdef KERNEL_2_6 ++#include <linux/backing-dev.h> ++#include <linux/writeback.h> ++#endif ++ ++#ifndef KERNEL_2_6 ++#define PageUptodate(page) Page_Uptodate(page) ++#endif ++ ++static int fuse_open(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_open_in inarg; ++ int err; ++ ++ err = generic_file_open(inode, file); ++ if(err) ++ return err; ++ ++ /* If opening the root node, no lookup has been performed on ++ it, so the attributes must be refreshed */ ++ if(inode->i_ino == FUSE_ROOT_INO) { ++ int err = fuse_do_getattr(inode); ++ if(err) ++ return err; ++ } ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.flags = file->f_flags & ~O_EXCL; ++ ++ in.h.opcode = FUSE_OPEN; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ request_send(fc, &in, &out); ++ if(!out.h.error && !(fc->flags & FUSE_KERNEL_CACHE)) { ++#ifdef KERNEL_2_6 ++ invalidate_inode_pages(inode->i_mapping); ++#else ++ invalidate_inode_pages(inode); ++#endif ++ } ++ ++ return out.h.error; ++} ++ ++void fuse_sync_inode(struct inode *inode) ++{ ++#ifdef KERNEL_2_6 ++ filemap_fdatawrite(inode->i_mapping); ++ filemap_fdatawait(inode->i_mapping); ++#else ++#ifndef NO_MM ++ filemap_fdatasync(inode->i_mapping); ++ filemap_fdatawait(inode->i_mapping); ++#endif ++#endif ++} ++ ++static int fuse_release_old(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in *in = NULL; ++ struct fuse_open_in *inarg = NULL; ++ unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_open_in); ++ ++ in = kmalloc(s, GFP_NOFS); ++ if(!in) ++ return -ENOMEM; ++ memset(in, 0, s); ++ inarg = (struct fuse_open_in *) (in + 1); ++ inarg->flags = file->f_flags & ~O_EXCL; ++ ++ in->h.opcode = FUSE_RELEASE; ++ in->h.ino = inode->i_ino; ++ in->numargs = 1; ++ in->args[0].size = sizeof(struct fuse_open_in); ++ in->args[0].value = inarg; ++ if(!request_send_noreply(fc, in)) ++ return 0; ++ ++ kfree(in); ++ return 0; ++} ++ ++static int fuse_release(struct inode *inode, struct file *file) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_open_in inarg; ++ ++ if(file->f_mode & FMODE_WRITE) ++ fuse_sync_inode(inode); ++ ++ if (fc->oldrelease) ++ return fuse_release_old(inode, file); ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.flags = file->f_flags & ~O_EXCL; ++ ++ in.h.opcode = FUSE_RELEASE2; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ request_send(fc, &in, &out); ++ if (out.h.error == -ENOSYS) { ++ fc->oldrelease = 1; ++ return fuse_release_old(inode, file); ++ } ++ return 0; ++} ++ ++static int fuse_fsync(struct file *file, struct dentry *de, int datasync) ++{ ++ struct inode *inode = de->d_inode; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_fsync_in inarg; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.datasync = datasync; ++ ++ in.h.opcode = FUSE_FSYNC; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ request_send(fc, &in, &out); ++ return out.h.error; ++ ++ /* FIXME: need to ensure, that all write requests issued ++ before this request are completed. Should userspace take ++ care of this? */ ++} ++ ++static int fuse_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_read_in inarg; ++ char *buffer; ++ ++ buffer = kmap(page); ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.offset = (unsigned long long) page->index << PAGE_CACHE_SHIFT; ++ inarg.size = PAGE_CACHE_SIZE; ++ ++ in.h.opcode = FUSE_READ; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ out.argvar = 1; ++ out.numargs = 1; ++ out.args[0].size = PAGE_CACHE_SIZE; ++ out.args[0].value = buffer; ++ ++ request_send(fc, &in, &out); ++ if(!out.h.error) { ++ size_t outsize = out.args[0].size; ++ if(outsize < PAGE_CACHE_SIZE) ++ memset(buffer + outsize, 0, PAGE_CACHE_SIZE - outsize); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ } ++ ++ kunmap(page); ++ unlock_page(page); ++ ++ return out.h.error; ++} ++ ++static int fuse_is_block_uptodate(struct address_space *mapping, ++ struct inode *inode, size_t bl_index) ++{ ++ size_t index = bl_index << FUSE_BLOCK_PAGE_SHIFT; ++ size_t end_index = ((bl_index + 1) << FUSE_BLOCK_PAGE_SHIFT) - 1; ++ size_t file_end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT; ++ ++ if (end_index > file_end_index) ++ end_index = file_end_index; ++ ++ for (; index <= end_index; index++) { ++ struct page *page = find_get_page(mapping, index); ++ ++ if (!page) ++ return 0; ++ ++ if (!PageUptodate(page)) { ++ page_cache_release(page); ++ return 0; ++ } ++ ++ page_cache_release(page); ++ } ++ ++ return 1; ++} ++ ++ ++static int fuse_cache_block(struct address_space *mapping, ++ struct inode *inode, char *bl_buf, ++ size_t bl_index) ++{ ++ size_t start_index = bl_index << FUSE_BLOCK_PAGE_SHIFT; ++ size_t end_index = ((bl_index + 1) << FUSE_BLOCK_PAGE_SHIFT) - 1; ++ size_t file_end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT; ++ ++ int i; ++ ++ if (end_index > file_end_index) ++ end_index = file_end_index; ++ ++ for (i = 0; start_index + i <= end_index; i++) { ++ size_t index = start_index + i; ++ struct page *page; ++ char *buffer; ++ ++ page = grab_cache_page(mapping, index); ++ if (!page) ++ return -1; ++ ++ if (!PageUptodate(page)) { ++ buffer = kmap(page); ++ memcpy(buffer, bl_buf + i * PAGE_CACHE_SIZE, ++ PAGE_CACHE_SIZE); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ kunmap(page); ++ } ++ ++ unlock_page(page); ++ page_cache_release(page); ++ } ++ ++ return 0; ++} ++ ++static int fuse_file_read_block(struct inode *inode, char *bl_buf, ++ size_t bl_index) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_read_in inarg; ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.offset = (unsigned long long) bl_index << FUSE_BLOCK_SHIFT; ++ inarg.size = FUSE_BLOCK_SIZE; ++ ++ in.h.opcode = FUSE_READ; ++ in.h.ino = inode->i_ino; ++ in.numargs = 1; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ out.argvar = 1; ++ out.numargs = 1; ++ out.args[0].size = FUSE_BLOCK_SIZE; ++ out.args[0].value = bl_buf; ++ ++ request_send(fc, &in, &out); ++ ++ if (!out.h.error) { ++ size_t outsize = out.args[0].size; ++ if (outsize < FUSE_BLOCK_SIZE) ++ memset(bl_buf + outsize, 0, FUSE_BLOCK_SIZE - outsize); ++ } ++ ++ return out.h.error; ++} ++ ++static void fuse_file_bigread(struct address_space *mapping, ++ struct inode *inode, loff_t pos, size_t count) ++{ ++ size_t bl_index = pos >> FUSE_BLOCK_SHIFT; ++ size_t bl_end_index = (pos + count) >> FUSE_BLOCK_SHIFT; ++ size_t bl_file_end_index = i_size_read(inode) >> FUSE_BLOCK_SHIFT; ++ ++ if (bl_end_index > bl_file_end_index) ++ bl_end_index = bl_file_end_index; ++ ++ while (bl_index <= bl_end_index) { ++ int res; ++ char *bl_buf = kmalloc(FUSE_BLOCK_SIZE, GFP_NOFS); ++ if (!bl_buf) ++ break; ++ res = fuse_is_block_uptodate(mapping, inode, bl_index); ++ if (!res) ++ res = fuse_file_read_block(inode, bl_buf, bl_index); ++ if (!res) ++ fuse_cache_block(mapping, inode, bl_buf, bl_index); ++ kfree(bl_buf); ++ bl_index++; ++ } ++} ++ ++static ssize_t fuse_file_read(struct file *filp, char *buf, ++ size_t count, loff_t * ppos) ++{ ++ struct address_space *mapping = filp->f_dentry->d_inode->i_mapping; ++ struct inode *inode = mapping->host; ++ struct fuse_conn *fc = INO_FC(inode); ++ ++ if(fc->flags & FUSE_LARGE_READ) { ++ /* Don't allow this to get mixed up with writes */ ++ down(&inode->i_sem); ++ fuse_file_bigread(mapping, inode, *ppos, count); ++ up(&inode->i_sem); ++ } ++ ++ return generic_file_read(filp, buf, count, ppos); ++} ++ ++static int write_buffer(struct inode *inode, struct page *page, ++ unsigned offset, size_t count) ++{ ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_write_in inarg; ++ char *buffer; ++ ++ buffer = kmap(page); ++ ++ memset(&inarg, 0, sizeof(inarg)); ++ inarg.offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + ++ offset; ++ inarg.size = count; ++ ++ in.h.opcode = FUSE_WRITE; ++ in.h.ino = inode->i_ino; ++ in.numargs = 2; ++ in.args[0].size = sizeof(inarg); ++ in.args[0].value = &inarg; ++ in.args[1].size = count; ++ in.args[1].value = buffer + offset; ++ request_send(fc, &in, &out); ++ kunmap(page); ++ if(out.h.error) ++ SetPageError(page); ++ ++ return out.h.error; ++} ++ ++static int get_write_count(struct inode *inode, struct page *page) ++{ ++ unsigned long end_index; ++ loff_t size = i_size_read(inode); ++ int count; ++ ++ end_index = size >> PAGE_CACHE_SHIFT; ++ if(page->index < end_index) ++ count = PAGE_CACHE_SIZE; ++ else { ++ count = size & (PAGE_CACHE_SIZE - 1); ++ if(page->index > end_index || count == 0) ++ return 0; ++ } ++ return count; ++} ++ ++#ifdef KERNEL_2_6 ++ ++static void write_buffer_end(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out, void *_page) ++{ ++ struct page *page = (struct page *) _page; ++ ++ if(out->h.error) { ++ SetPageError(page); ++ if(out->h.error == -ENOSPC) ++ set_bit(AS_ENOSPC, &page->mapping->flags); ++ else ++ set_bit(AS_EIO, &page->mapping->flags); ++ } ++ end_page_writeback(page); ++ kunmap(page); ++ kfree(in); ++} ++ ++static int write_buffer_nonblock(struct inode *inode, struct page *page, ++ unsigned offset, size_t count) ++{ ++ int err; ++ struct fuse_conn *fc = INO_FC(inode); ++ struct fuse_in *in = NULL; ++ struct fuse_out *out = NULL; ++ struct fuse_write_in *inarg = NULL; ++ char *buffer; ++ unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_out) + ++ sizeof(struct fuse_write_in); ++ ++ in = kmalloc(s, GFP_NOFS); ++ if(!in) ++ return -ENOMEM; ++ memset(in, 0, s); ++ out = (struct fuse_out *)(in + 1); ++ inarg = (struct fuse_write_in *)(out + 1); ++ ++ buffer = kmap(page); ++ ++ inarg->offset = ((unsigned long long) page->index << PAGE_CACHE_SHIFT) + offset; ++ inarg->size = count; ++ ++ in->h.opcode = FUSE_WRITE; ++ in->h.ino = inode->i_ino; ++ in->numargs = 2; ++ in->args[0].size = sizeof(struct fuse_write_in); ++ in->args[0].value = inarg; ++ in->args[1].size = count; ++ in->args[1].value = buffer + offset; ++ err = request_send_nonblock(fc, in, out, write_buffer_end, page); ++ if(err) { ++ if(err != -EWOULDBLOCK) ++ SetPageError(page); ++ kunmap(page); ++ kfree(in); ++ } ++ return err; ++} ++ ++static int fuse_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ int err; ++ struct inode *inode = page->mapping->host; ++ unsigned count = get_write_count(inode, page); ++ ++ err = -EINVAL; ++ if(count) { ++ /* FIXME: check sync_mode, and wait for previous writes (or ++ signal userspace to do this) */ ++ if(wbc->nonblocking) { ++ SetPageWriteback(page); ++ err = write_buffer_nonblock(inode, page, 0, count); ++ if (err) ++ ClearPageWriteback(page); ++ if(err == -EWOULDBLOCK) { ++ __set_page_dirty_nobuffers(page); ++ err = 0; ++ } ++ } else ++ err = write_buffer(inode, page, 0, count); ++ } ++ ++ unlock_page(page); ++ return err; ++} ++#else ++static int fuse_writepage(struct page *page) ++{ ++ int err; ++ struct inode *inode = page->mapping->host; ++ int count = get_write_count(inode, page); ++ err = -EINVAL; ++ if(count) ++ err = write_buffer(inode, page, 0, count); ++ ++ unlock_page(page); ++ return err; ++} ++#endif ++ ++static int fuse_prepare_write(struct file *file, struct page *page, ++ unsigned offset, unsigned to) ++{ ++ /* No op */ ++ return 0; ++} ++ ++static int fuse_commit_write(struct file *file, struct page *page, ++ unsigned offset, unsigned to) ++{ ++ int err; ++ struct inode *inode = page->mapping->host; ++ ++ err = write_buffer(inode, page, offset, to - offset); ++ if(!err) { ++ loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to; ++ if(pos > i_size_read(inode)) ++ i_size_write(inode, pos); ++ } ++ return err; ++} ++ ++static struct file_operations fuse_file_operations = { ++ .read = fuse_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .open = fuse_open, ++ .release = fuse_release, ++ .fsync = fuse_fsync, ++#ifdef KERNEL_2_6 ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++ ++static struct address_space_operations fuse_file_aops = { ++ .readpage = fuse_readpage, ++ .writepage = fuse_writepage, ++ .prepare_write = fuse_prepare_write, ++ .commit_write = fuse_commit_write, ++}; ++ ++void fuse_init_file_inode(struct inode *inode) ++{ ++#ifdef KERNEL_2_6 ++ struct fuse_conn *fc = INO_FC(inode); ++ /* Readahead somehow defeats big reads on 2.6 (says Michael ++ Grigoriev) */ ++ if(fc->flags & FUSE_LARGE_READ) ++ inode->i_mapping->backing_dev_info->ra_pages = 0; ++#endif ++ inode->i_fop = &fuse_file_operations; ++ inode->i_data.a_ops = &fuse_file_aops; ++} ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/fs/cofusefs/fuse_i.h b/fs/cofusefs/fuse_i.h +--- a/fs/cofusefs/fuse_i.h ++++ b/fs/cofusefs/fuse_i.h +@@ -0,0 +1,297 @@ ++/* ++ COFUSE: Filesystem in an host of Cooperative Linux ++ Copyright (C) 2004 Dan Aloni <da-x@colinux.org> ++ ++ based on FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++ ++#include <linux/version.h> ++#include <linux/config.h> ++ ++#ifndef CONFIG_COOPERATIVE ++#include <linux/cofuse.h> ++#else ++#include <linux/cooperative_internal.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) ++#error Kernel version 2.5.* not supported ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#define KERNEL_2_6 ++#endif ++ ++#ifndef KERNEL_2_6 ++#include <linux/config.h> ++#ifdef CONFIG_MODVERSIONS ++#define MODVERSIONS ++#include <linux/modversions.h> ++#endif ++#include <config.h> ++#ifndef HAVE_I_SIZE_FUNC ++#define i_size_read(inode) ((inode)->i_size) ++#define i_size_write(inode, size) do { (inode)->i_size = size; } while(0) ++#endif ++#endif ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/list.h> ++#include <linux/spinlock.h> ++ ++/** Read combining parameters */ ++#define FUSE_BLOCK_SHIFT 16 ++#define FUSE_BLOCK_SIZE 65536 ++#define FUSE_BLOCK_MASK 0xffff0000 ++ ++#define FUSE_BLOCK_PAGE_SHIFT (FUSE_BLOCK_SHIFT - PAGE_CACHE_SHIFT) ++ ++/** ++ * A Fuse connection. ++ * ++ * This structure is created, when the client device is opened, and is ++ * destroyed, when the client device is closed _and_ the filesystem is ++ * unmounted. ++ */ ++struct fuse_conn { ++ /** The superblock of the mounted filesystem */ ++ struct super_block *sb; ++ ++#ifndef CONFIG_COOPERATIVE ++ /** The opened client device */ ++ struct file *file; ++#else ++ int cofs_unit; ++#endif ++ /** The user id for this mount */ ++ uid_t uid; ++ ++ /** The fuse mount flags for this mount */ ++ unsigned int flags; ++ ++ /** Is the new (synchronous) release not supported by ++ userspace? */ ++ unsigned int oldrelease; ++ ++ char opt_pathname[0x80]; ++ ++#ifndef CONFIG_COOPERATIVE ++ /** Readers of the connection are waiting on this */ ++ wait_queue_head_t waitq; ++ ++ /** The list of pending requests */ ++ struct list_head pending; ++ ++ /** The list of requests being processed */ ++ struct list_head processing; ++ ++ /** Controls the maximum number of outstanding requests */ ++ struct semaphore outstanding; ++ ++ /** The next unique request id */ ++ int reqctr; ++#endif ++}; ++ ++/** One input argument of a request */ ++struct fuse_in_arg { ++ unsigned int size; ++ const void *value; ++}; ++ ++/** The request input */ ++struct fuse_in { ++ struct fuse_in_header h; ++ unsigned int numargs; ++ struct fuse_in_arg args[3]; ++}; ++ ++/** One output argument of a request */ ++struct fuse_out_arg { ++ unsigned int size; ++ void *value; ++}; ++ ++/** The request output */ ++struct fuse_out { ++ struct fuse_out_header h; ++ unsigned int argvar; ++ unsigned int numargs; ++ struct fuse_out_arg args[3]; ++}; ++ ++#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0} ++#define FUSE_OUT_INIT { {0, 0}, 0, 0} ++ ++struct fuse_req; ++typedef void (*fuse_reqend_t)(struct fuse_conn *, struct fuse_in *, ++ struct fuse_out *, void *data); ++ ++/** ++ * A request to the client ++ */ ++struct fuse_req { ++ /** The request list */ ++ struct list_head list; ++ ++ /** True if the request is synchronous */ ++ unsigned int issync:1; ++ ++ /** The request is locked */ ++ unsigned int locked:1; ++ ++ /** The request has been interrupted while it was locked */ ++ unsigned int interrupted:1; ++ ++ /* The request has been sent to the client */ ++ unsigned int sent:1; ++ ++ /* The request is finished */ ++ unsigned int finished:1; ++ ++ /** The request input */ ++ struct fuse_in *in; ++ ++ /** The request output */ ++ struct fuse_out *out; ++ ++ /** Used to wake up the task waiting for completion of request*/ ++ wait_queue_head_t waitq; ++ ++ /** Request completion callback */ ++ fuse_reqend_t end; ++ ++ /** User data */ ++ void *data; ++}; ++ ++#ifdef KERNEL_2_6 ++#define SB_FC(sb) ((sb)->s_fs_info) ++#else ++#define SB_FC(sb) ((sb)->u.generic_sbp) ++#endif ++#define INO_FC(inode) SB_FC((inode)->i_sb) ++#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data) ++ ++ ++/** ++ * The proc entry for the client device ("/proc/fs/fuse/dev") ++ */ ++extern struct proc_dir_entry *proc_fuse_dev; ++ ++/** ++ * The lock to protect fuses structures ++ */ ++extern spinlock_t cofuse_lock; ++ ++ ++/** ++ * Get a filled in inode ++ */ ++struct inode *cofuse_iget(struct super_block *sb, ino_t ino, ++ struct fuse_attr *attr, int version); ++ ++ ++/** ++ * Initialise operations on regular file ++ */ ++void cofuse_init_file_inode(struct inode *inode); ++ ++/** ++ * Check if the connection can be released, and if yes, then free the ++ * connection structure ++ */ ++void cofuse_release_conn(struct fuse_conn *fc); ++ ++/** ++ * Initialize the client device ++ */ ++int cofuse_dev_init(void); ++ ++/** ++ * Cleanup the client device ++ */ ++void cofuse_dev_cleanup(void); ++ ++/** ++ * Initialize the fuse filesystem ++ */ ++int cofuse_fs_init(void); ++ ++/** ++ * Cleanup the fuse filesystem ++ */ ++void cofuse_fs_cleanup(void); ++ ++/** ++ * Send a request ++ * ++ */ ++void cofuse_request_send(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out); ++ ++/** ++ * Send a request for which a reply is not expected ++ */ ++int cofuse_request_send_noreply(struct fuse_conn *fc, struct fuse_in *in); ++ ++ ++/** ++ * Send a synchronous request without blocking ++ */ ++int cofuse_request_send_nonblock(struct fuse_conn *fc, struct fuse_in *in, ++ struct fuse_out *out, fuse_reqend_t end, void *data); ++ ++/** ++ * Get the attributes of a file ++ */ ++int cofuse_do_getattr(struct inode *inode); ++ ++/** ++ * Write dirty pages ++ */ ++void cofuse_sync_inode(struct inode *inode); ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ ++ ++#define COFUSE_VERSION "0.1" ++#define FUSE_VERSION COFUSE_VERSION ++ ++#define fuse_init_file_inode cofuse_init_file_inode ++#define fuse_do_getattr cofuse_do_getattr ++#define fuse_sync_inode cofuse_sync_inode ++#define fuse_lock cofuse_lock ++ ++#define request_send cofuse_request_send ++#define request_send_noreply cofuse_request_send_noreply ++#define request_send_nonblock cofuse_request_send_nonblock ++#define release_conn cofuse_release_conn ++#define fuse_iget cofuse_iget ++#define fuse_dev_init cofuse_dev_init ++#define fuse_dev_cleanup cofuse_dev_cleanup ++#define fuse_fs_init cofuse_fs_init ++#define fuse_fs_cleanup cofuse_fs_cleanup ++ ++extern struct fuse_conn *cofs_volumes[CO_MODULE_MAX_COFS]; ++ ++/** Data passed to mount */ ++struct cofuse_mount_data { ++ struct fuse_mount_data *fuse; ++ int uid; ++ int gid; ++ unsigned long file_mode; ++ unsigned long dir_mode; ++ unsigned long flags; ++ char name[0x80]; ++}; +diff -urN a/fs/cofusefs/inode.c b/fs/cofusefs/inode.c +--- a/fs/cofusefs/inode.c ++++ b/fs/cofusefs/inode.c +@@ -0,0 +1,545 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001 Miklos Szeredi (miklos@szeredi.hu) ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++#include "fuse_i.h" ++ ++#include <linux/pagemap.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/file.h> ++#include <linux/ctype.h> ++#include <linux/proc_fs.h> ++#ifdef KERNEL_2_6 ++#include <linux/statfs.h> ++#endif ++ ++#define FUSE_SUPER_MAGIC 0x65735546 ++ ++#ifndef KERNEL_2_6 ++#define kstatfs statfs ++#endif ++ ++#ifndef FS_BINARY_MOUNTDATA ++#define FS_BINARY_MOUNTDATA 0 ++#endif ++ ++static void fuse_read_inode(struct inode *inode) ++{ ++ /* No op */ ++} ++ ++static void fuse_clear_inode(struct inode *inode) ++{ ++ unsigned long flags; ++ struct fuse_conn *fc = INO_FC(inode); ++ ++#ifndef CONFIG_COOPERATIVE ++ struct fuse_in *in = NULL; ++ struct fuse_forget_in *inarg = NULL; ++ unsigned int s = sizeof(struct fuse_in) + sizeof(struct fuse_forget_in); ++ ++ if(fc == NULL) ++ return; ++ ++ in = kmalloc(s, GFP_NOFS); ++ if(!in) ++ return; ++ memset(in, 0, s); ++ inarg = (struct fuse_forget_in *) (in + 1); ++ inarg->version = inode->i_version; ++ ++ in->h.opcode = FUSE_FORGET; ++ in->h.ino = inode->i_ino; ++ in->numargs = 1; ++ in->args[0].size = sizeof(struct fuse_forget_in); ++ in->args[0].value = inarg; ++ ++ if(!request_send_noreply(fc, in)) ++ return; ++ ++ kfree(in); ++#else ++ if (FUSE_ROOT_INO == inode->i_ino) ++ return; ++ ++ co_passage_page_assert_valid(); ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = fc->cofs_unit; ++ co_passage_page->params[2] = FUSE_FORGET; ++ co_passage_page->params[3] = inode->i_ino; ++ co_switch_wrapper(); ++ co_passage_page_release(flags); ++#endif ++} ++ ++static void fuse_put_super(struct super_block *sb) ++{ ++ struct fuse_conn *fc = SB_FC(sb); ++ ++ spin_lock(&fuse_lock); ++ fc->sb = NULL; ++ fc->uid = 0; ++ fc->flags = 0; ++ /* Flush all readers on this fs */ ++#ifndef CONFIG_COOPERATIVE ++ wake_up_all(&fc->waitq); ++#endif ++ release_conn(fc); ++ SB_FC(sb) = NULL; ++ spin_unlock(&fuse_lock); ++} ++ ++static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) ++{ ++ stbuf->f_type = FUSE_SUPER_MAGIC; ++ stbuf->f_bsize = attr->block_size; ++ stbuf->f_blocks = attr->blocks; ++ stbuf->f_bfree = stbuf->f_bavail = attr->blocks_free; ++ stbuf->f_files = attr->files; ++ stbuf->f_ffree = attr->files_free; ++ /* Is this field necessary? Most filesystems ignore it... ++ stbuf->f_fsid.val[0] = (FUSE_SUPER_MAGIC>>16)&0xffff; ++ stbuf->f_fsid.val[1] = FUSE_SUPER_MAGIC &0xffff; */ ++ stbuf->f_namelen = attr->namelen; ++} ++ ++static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct fuse_conn *fc = SB_FC(sb); ++ struct fuse_in in = FUSE_IN_INIT; ++ struct fuse_out out = FUSE_OUT_INIT; ++ struct fuse_statfs_out outarg; ++ ++ in.numargs = 0; ++ in.h.opcode = FUSE_STATFS; ++ out.numargs = 1; ++ out.args[0].size = sizeof(outarg); ++ out.args[0].value = &outarg; ++ request_send(fc, &in, &out); ++ if(!out.h.error) ++ convert_fuse_statfs(buf, &outarg.st); ++ ++ return out.h.error; ++} ++ ++#ifndef CONFIG_COOPERATIVE ++ ++static struct fuse_conn *get_conn(struct fuse_mount_data *d) ++{ ++ struct fuse_conn *fc = NULL; ++ struct file *file; ++ struct inode *ino; ++ ++ if(d == NULL) { ++ printk("fuse_read_super: Bad mount data\n"); ++ return NULL; ++ } ++ ++ if(d->version != FUSE_KERNEL_VERSION) { ++ printk("fuse_read_super: Bad version: %i\n", d->version); ++ return NULL; ++ } ++ ++ file = fget(d->fd); ++ ino = NULL; ++ if(file) ++ ino = file->f_dentry->d_inode; ++ ++ if(!ino || !proc_fuse_dev || proc_fuse_dev->low_ino != ino->i_ino) { ++ printk("fuse_read_super: Bad file: %i\n", d->fd); ++ goto out; ++ } ++ ++ fc = file->private_data; ++ ++ out: ++ fput(file); ++ return fc; ++} ++ ++#else ++ ++static int _atoi(const char *s, const char **out) ++{ ++ /* lib/spprintf.h */ ++ ++ int i=0; ++ ++ while (isdigit(*s)) ++ i = i*10 + *(s++) - '0'; ++ ++ *out = s; ++ ++ return i; ++} ++ ++static struct fuse_conn *co_get_conn(struct cofuse_mount_data *d) ++{ ++ int index; ++ int ret; ++ unsigned long flags; ++ struct fuse_conn *conn = NULL; ++ const char *name, *next; ++ ++ if (d == NULL) { ++ printk("cofuse_read_super: Bad mount data\n"); ++ return NULL; ++ } ++ ++ name = d->name; ++ ++ if (strncmp("cofs", name, 4) == 0) ++ name += 4; ++ ++ index = _atoi(name, &next); ++ if (index < 0 || index >= CO_MODULE_MAX_COFS) { ++ printk("cofuse_read_super: Invalid index %d\n", index); ++ return NULL; ++ } ++ ++ if (cofs_volumes[index]) ++ return cofs_volumes[index]; ++ ++ conn = kmalloc(sizeof(struct fuse_conn), GFP_KERNEL); ++ if (!conn) ++ return NULL; ++ ++ memset(conn, 0, sizeof(*conn)); ++ ++ if (*next == ':') { ++ snprintf(conn->opt_pathname, sizeof(conn->opt_pathname), "%s", next+1); ++ } ++ ++ conn->cofs_unit = index; ++ ++ co_passage_page_assert_valid(); ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_DEVICE; ++ co_passage_page->params[0] = CO_DEVICE_FILESYSTEM; ++ co_passage_page->params[1] = conn->cofs_unit; ++ co_passage_page->params[2] = FUSE_MOUNT; ++ co_passage_page->params[5] = d->uid; ++ co_passage_page->params[6] = d->gid; ++ co_passage_page->params[7] = d->dir_mode; ++ co_passage_page->params[8] = d->file_mode; ++ memcpy(&co_passage_page->params[30], conn->opt_pathname, strlen(conn->opt_pathname) + 1); ++ co_switch_wrapper(); ++ ret = co_passage_page->params[4]; ++ co_passage_page_release(flags); ++ ++ if (ret) { ++ kfree(conn); ++ conn = NULL; ++ } ++ ++ return conn; ++} ++ ++#endif ++ ++static struct inode *get_root_inode(struct super_block *sb, unsigned int mode) ++{ ++ struct fuse_attr attr; ++ memset(&attr, 0, sizeof(attr)); ++ ++ attr.mode = mode; ++ return fuse_iget(sb, 1, &attr, 0); ++} ++ ++ ++#ifdef KERNEL_2_6 ++ ++static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp) ++{ ++ __u32 *objp = vobjp; ++ unsigned long ino = objp[0]; ++ /* __u32 generation = objp[1]; */ ++ struct inode *inode; ++ struct dentry *entry; ++ ++ if(ino == 0) ++ return ERR_PTR(-ESTALE); ++ ++ inode = ilookup(sb, ino); ++ if(!inode) ++ return ERR_PTR(-ESTALE); ++ ++ entry = d_alloc_anon(inode); ++ if(!entry) { ++ iput(inode); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ return entry; ++} ++ ++static struct export_operations fuse_export_operations = { ++ .get_dentry = fuse_get_dentry, ++}; ++#endif ++ ++static struct super_operations fuse_super_operations = { ++ .read_inode = fuse_read_inode, ++ .clear_inode = fuse_clear_inode, ++ .put_super = fuse_put_super, ++ .statfs = fuse_statfs, ++}; ++ ++static int fuse_read_super(struct super_block *sb, void *data, int silent) ++{ ++ struct fuse_conn *fc; ++ struct inode *root; ++#ifndef CONFIG_COOPERATIVE ++ struct fuse_mount_data *d = data; ++#else ++ struct cofuse_mount_data *co_d = data; ++ struct fuse_mount_data *d = co_d->fuse; ++#endif ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = FUSE_SUPER_MAGIC; ++ sb->s_op = &fuse_super_operations; ++ sb->s_maxbytes = MAX_LFS_FILESIZE; ++#ifdef KERNEL_2_6 ++ sb->s_export_op = &fuse_export_operations; ++#endif ++ ++#ifndef CONFIG_COOPERATIVE ++ fc = get_conn(d); ++#else ++ fc = co_get_conn(co_d); ++#endif ++ if(fc == NULL) ++ return -EINVAL; ++ spin_lock(&fuse_lock); ++ if(fc->sb != NULL) { ++ printk("fuse_read_super: connection already mounted\n"); ++ spin_unlock(&fuse_lock); ++ return -EINVAL; ++ } ++ fc->sb = sb; ++ fc->flags = d->flags; ++ fc->uid = d->uid; ++ spin_unlock(&fuse_lock); ++ ++ /* fc is needed in fuse_init_file_inode which could be called ++ from get_root_inode */ ++ SB_FC(sb) = fc; ++ ++ root = get_root_inode(sb, d->rootmode); ++ if(root == NULL) { ++ printk("fuse_read_super: failed to get root inode\n"); ++ return -EINVAL; ++ } ++ ++ sb->s_root = d_alloc_root(root); ++ if(!sb->s_root) { ++ printk("fuse_read_super: failed to allocate root\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_COOPERATIVE ++/* ++ * cofuse_getopt and cofuse_parse_options were ++ * addopted from smb ++ */ ++ ++struct option { ++ const char *name; ++ unsigned long flag; ++ int val; ++}; ++ ++/** ++ * cofuse_getopt - option parser ++ * based on smb_getopt from fs/smbfs ++ * ++ * @caller: name of the caller, for error messages ++ * @options: the options string ++ * @opts: an array of &struct option entries controlling parser operations ++ * @optopt: output; will contain the current option ++ * @optarg: output; will contain the value (if one exists) ++ * @flag: output; may be NULL; should point to a long for or'ing flags ++ * @value: output; may be NULL; will be overwritten with the integer value ++ * of the current argument. ++ * ++ * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). ++ * Returns opts->val if a matching entry in the 'opts' array is found, ++ * 0 when no more tokens are found, -1 if an error is encountered. ++ */ ++static int cofuse_getopt(char *caller, char **options, struct option *opts, ++ char **optopt, char **optarg, unsigned long *flag, ++ unsigned long *value) ++{ ++ char *token; ++ char *val; ++ int i; ++ ++ do { ++ if ((token = strsep(options, ",")) == NULL) ++ return 0; ++ } while (*token == '\0'); ++ *optopt = token; ++ ++ *optarg = NULL; ++ if ((val = strchr (token, '=')) != NULL) { ++ *val++ = 0; ++ if (value) ++ *value = simple_strtoul(val, NULL, 0); ++ *optarg = val; ++ } ++ ++ for (i = 0; opts[i].name != NULL; i++) { ++ if (!strcmp(opts[i].name, token)) { ++ if (!opts[i].flag && (!val || !*val)) { ++ printk("%s: the %s option requires an argument\n", ++ caller, token); ++ return -1; ++ } ++ ++ if (flag && opts[i].flag) ++ *flag |= opts[i].flag; ++ ++ return opts[i].val; ++ } ++ } ++ printk("%s: Unrecognized mount option %s\n", caller, token); ++ return -1; ++} ++ ++static struct option opts[] = { ++ { "uid", 0, 'u' }, ++ { "gid", 0, 'g' }, ++ { "fmask", 0, 'f' }, ++ { "dmask", 0, 'd' }, ++ { NULL, 0, 0} ++}; ++ ++/* ++ * parse_options - based on parse_options from fs/smbfs ++ */ ++static int parse_options(struct cofuse_mount_data *mnt, char *options) ++{ ++ int c; ++ unsigned long flags; ++ unsigned long value; ++ char *optarg; ++ char *optopt; ++ ++ flags = 0; ++ while ((c = cofuse_getopt("cofuse", &options, opts, ++ &optopt, &optarg, &flags, &value)) > 0) ++ { ++ switch (c) { ++ case 1: ++ /* got a "flag" option */ ++ break; ++ case 'u': ++ mnt->uid = value; ++ break; ++ case 'g': ++ mnt->gid = value; ++ break; ++ case 'f': ++ mnt->file_mode = (value & S_IRWXUGO) | S_IFREG; ++ break; ++ case 'd': ++ mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR; ++ break; ++ default: ++ printk("cofs: Unrecognized mount option %s\n", optopt); ++ return -1; ++ } ++ } ++ ++ mnt->flags = flags; ++ return c; ++} ++#endif ++ ++#ifdef KERNEL_2_6 ++static struct super_block *fuse_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data) ++{ ++#ifdef CONFIG_COOPERATIVE ++ struct cofuse_mount_data co_md = {0, }; ++ struct fuse_mount_data md = {0, }; ++ int ret; ++ ++ co_md.uid = current->uid; ++ co_md.gid = current->gid; ++ co_md.dir_mode = FUSE_S_IRWXU | FUSE_S_IRGRP | FUSE_S_IXGRP | ++ FUSE_S_IROTH | FUSE_S_IXOTH | S_IFDIR; ++ co_md.file_mode = FUSE_S_IRWXU | FUSE_S_IRGRP | FUSE_S_IXGRP | ++ FUSE_S_IROTH | FUSE_S_IXOTH | S_IFREG; ++ ++ ret = parse_options(&co_md, raw_data); ++ if (ret == -1) ++ return ERR_PTR(-EINVAL); ++ ++ md.rootmode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; ++ md.flags = FUSE_ALLOW_OTHER | FUSE_DEFAULT_PERMISSIONS; ++ ++ co_md.fuse = &md; ++ snprintf(co_md.name, sizeof(co_md.name), "%s", dev_name); ++ ++ return get_sb_nodev(fs_type, flags, &co_md, fuse_read_super); ++#else ++ return get_sb_nodev(fs_type, flags, raw_data, fuse_read_super); ++#endif ++} ++ ++static struct file_system_type fuse_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "cofs", ++ .get_sb = fuse_get_sb, ++ .kill_sb = kill_anon_super, ++ .fs_flags = FS_BINARY_MOUNTDATA, ++}; ++#else ++static struct super_block *fuse_read_super_compat(struct super_block *sb, ++ void *data, int silent) ++{ ++ int err = fuse_read_super(sb, data, silent); ++ if(err) ++ return NULL; ++ else ++ return sb; ++} ++ ++static DECLARE_FSTYPE(fuse_fs_type, "cofs", fuse_read_super_compat, 0); ++#endif ++ ++int fuse_fs_init() ++{ ++ int res; ++ ++ res = register_filesystem(&fuse_fs_type); ++ if(res) ++ printk("fuse: failed to register filesystem\n"); ++ ++ return res; ++} ++ ++void fuse_fs_cleanup() ++{ ++ unregister_filesystem(&fuse_fs_type); ++} ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/fs/cofusefs/util.c b/fs/cofusefs/util.c +--- a/fs/cofusefs/util.c ++++ b/fs/cofusefs/util.c +@@ -0,0 +1,78 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++#include "fuse_i.h" ++ ++#include <linux/init.h> ++#include <linux/slab.h> ++ ++MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); ++MODULE_DESCRIPTION("Filesystem in Userspace"); ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++spinlock_t fuse_lock = SPIN_LOCK_UNLOCKED; ++ ++/* Must be called with the fuse lock held */ ++void release_conn(struct fuse_conn *fc) ++{ ++#ifdef CONFIG_COOPERATIVE ++ if (cooperative_mode_enabled()) { ++ cofs_volumes[fc->cofs_unit] = NULL; ++ kfree(fc); ++ return; ++ } ++#else ++ if(fc->sb == NULL && fc->file == NULL) { ++ kfree(fc); ++ } ++#endif ++} ++ ++int __init cofuse_init(void) ++{ ++ int res; ++ ++ printk(KERN_DEBUG "cofuse init %s (API version %i.%i)\n", ++ FUSE_VERSION, ++ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); ++ ++ res = fuse_fs_init(); ++ if(res) ++ goto err; ++ ++ res = fuse_dev_init(); ++ if(res) ++ goto err_fs_cleanup; ++ ++ return 0; ++ ++ err_fs_cleanup: ++ fuse_fs_cleanup(); ++ err: ++ return res; ++} ++ ++void __exit cofuse_exit(void) ++{ ++ printk(KERN_DEBUG "cofuse exit\n"); ++ ++ fuse_fs_cleanup(); ++ fuse_dev_cleanup(); ++} ++ ++module_init(cofuse_init); ++module_exit(cofuse_exit); ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/fs/namespace.c b/fs/namespace.c +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -1015,7 +1015,7 @@ + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; +- ++ + /* Basic sanity checks */ + + if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) +diff -urN a/include/asm-i386/bug.h b/include/asm-i386/bug.h +--- a/include/asm-i386/bug.h ++++ b/include/asm-i386/bug.h +@@ -10,11 +10,17 @@ + */ + + #if 1 /* Set to zero for a slightly smaller kernel */ ++#ifdef CONFIG_COOPERATIVE ++#include <linux/cooperative.h> ++extern void co_terminate(co_termination_reason_t reason); ++#define BUG() do { co_terminate(CO_TERMINATE_BUG); } while(0) ++#else + #define BUG() \ + __asm__ __volatile__( "ud2\n" \ + "\t.word %c0\n" \ + "\t.long %c1\n" \ + : : "i" (__LINE__), "i" (__FILE__)) ++#endif + #else + #define BUG() __asm__ __volatile__("ud2\n") + #endif +diff -urN a/include/asm-i386/cooperative.h b/include/asm-i386/cooperative.h +--- a/include/asm-i386/cooperative.h ++++ b/include/asm-i386/cooperative.h +@@ -0,0 +1,184 @@ ++/* ++ * linux/include/asm/cooperative.h ++ * ++ * Copyright (C) 2004 Dan Aloni ++ * ++ * This file defines the lower level interfaces between the Cooperative Linux ++ * kernel and the host OS driver. It's for both external inclusion from the ++ * and internal inclusion in the kernel sources. ++ */ ++ ++#ifndef __LINUX_ASM_COOPERATIVE_H__ ++#define __LINUX_ASM_COOPERATIVE_H__ ++ ++typedef struct { ++ unsigned short size; ++ struct x86_idt_entry *table; ++} __attribute__((packed)) x86_idt_t; ++ ++typedef struct { ++ unsigned short limit; ++ struct x86_dt_entry *base; ++} __attribute__((packed)) x86_gdt_t; ++ ++typedef struct { ++ unsigned char border2[0x4]; ++ ++ unsigned long cs; ++ #define CO_ARCH_STATE_STACK_CS "0x04" ++ ++ unsigned long ds; ++ #define CO_ARCH_STATE_STACK_DS "0x08" ++ ++ unsigned long es; ++ #define CO_ARCH_STATE_STACK_ES "0x0C" ++ ++ unsigned long cr3; ++ #define CO_ARCH_STATE_STACK_CR3 "0x10" ++ ++ unsigned long cr4; ++ #define CO_ARCH_STATE_STACK_CR4 "0x14" ++ ++ unsigned long cr2; ++ #define CO_ARCH_STATE_STACK_CR2 "0x18" ++ ++ unsigned long cr0; ++ #define CO_ARCH_STATE_STACK_CR0 "0x1C" ++ ++ x86_gdt_t gdt; ++ #define CO_ARCH_STATE_STACK_GDT "0x20" ++ ++ unsigned long fs; ++ #define CO_ARCH_STATE_STACK_FS "0x26" ++ ++ unsigned long gs; ++ #define CO_ARCH_STATE_STACK_GS "0x2A" ++ ++ unsigned short ldt; ++ #define CO_ARCH_STATE_STACK_LDT "0x2E" ++ ++ x86_idt_t idt; ++ #define CO_ARCH_STATE_STACK_IDT "0x30" ++ ++ unsigned short tr; ++ #define CO_ARCH_STATE_STACK_TR "0x36" ++ ++ unsigned long return_eip; ++ #define CO_ARCH_STATE_STACK_RETURN_EIP "0x38" ++ ++ unsigned long flags; ++ #define CO_ARCH_STATE_STACK_FLAGS "0x3C" ++ ++ unsigned long esp; ++ #define CO_ARCH_STATE_STACK_ESP "0x40" ++ ++ unsigned long ss; ++ #define CO_ARCH_STATE_STACK_SS "0x44" ++ ++ unsigned long dr0; ++ #define CO_ARCH_STATE_STACK_DR0 "0x48" ++ ++ unsigned long dr1; ++ #define CO_ARCH_STATE_STACK_DR1 "0x4C" ++ ++ unsigned long dr2; ++ #define CO_ARCH_STATE_STACK_DR2 "0x50" ++ ++ unsigned long dr3; ++ #define CO_ARCH_STATE_STACK_DR3 "0x54" ++ ++ unsigned long dr6; ++ #define CO_ARCH_STATE_STACK_DR6 "0x58" ++ ++ unsigned long dr7; ++ #define CO_ARCH_STATE_STACK_DR7 "0x5C" ++ ++ unsigned long temp_cr3; ++ #define CO_ARCH_STATE_STACK_TEMP_CR3 "0x60" ++ ++ unsigned long relocate_eip; ++ #define CO_ARCH_STATE_STACK_RELOCATE_EIP "0x64" ++ ++ unsigned long pad1; ++ #define CO_ARCH_STATE_STACK_RELOCATE_EIP_AFTER "0x68" ++ ++ unsigned long va; ++ #define CO_ARCH_STATE_STACK_VA "0x6C" ++ ++ unsigned char fxstate[0x200]; ++ #define CO_ARCH_STATE_STACK_FXSTATE "0x70" ++} __attribute__((packed)) co_arch_state_stack_t; ++ ++#define CO_MAX_PARAM_SIZE 0x400 ++ ++typedef struct co_arch_passage_page_normal_address_space { ++ unsigned long pgd[0x400]; ++ unsigned long pte[2][0x400]; ++} co_arch_passage_page_normal_address_space_t; ++ ++typedef struct co_arch_passage_page_pae_address_space { ++ unsigned long long main[0x200]; ++ unsigned long long pgd[2][0x200]; ++ unsigned long long pte[2][0x200]; ++} co_arch_passage_page_pae_address_space_t; ++ ++typedef struct co_arch_passage_page { ++ union { ++ struct { ++ union { ++ struct { ++ unsigned long self_physical_address; ++ unsigned long dr0; ++ unsigned long dr1; ++ unsigned long dr2; ++ unsigned long dr3; ++ unsigned long dr6; ++ unsigned long dr7; ++ unsigned char code[0x230]; ++ } __attribute__((packed)); ++ unsigned char pad[0x250]; /* Be careful! see NOTE below */ ++ } __attribute__((packed)); ++ ++ /* Machine states */ ++ ++ /* ++ * NOTE: *_state fields must be aligned at 16 bytes boundary since ++ * the fxsave/fxload instructions expect an aligned arugment. ++ */ ++ ++ co_arch_state_stack_t host_state; ++ co_arch_state_stack_t linuxvm_state; ++ ++ /* Control parameters */ ++ unsigned long operation; ++ unsigned long params[]; ++ } __attribute__((packed)); ++ unsigned char first_page[0x1000]; ++ }; ++ ++ /* page tables for passage address spaces */ ++ co_arch_passage_page_normal_address_space_t guest_normal; ++ union { ++ co_arch_passage_page_normal_address_space_t host_normal; ++ co_arch_passage_page_pae_address_space_t host_pae; ++ } __attribute__((packed)); ++} co_arch_passage_page_t; ++ ++/* ++ * Address space layout: ++ */ ++ ++#define CO_VPTR_BASE (0xffc00000) ++#define CO_VPTR_PHYSICAL_TO_PSEUDO_PFN_MAP (CO_VPTR_BASE - 0x1000000) ++#define CO_VPTR_PSEUDO_RAM_PAGE_TABLES (CO_VPTR_BASE - 0x1100000) ++#define CO_VPTR_PASSAGE_PAGE (CO_VPTR_BASE - 0x1101000) ++#define CO_VPTR_IO_AREA_SIZE (0x10000) ++#define CO_VPTR_IO_AREA_START (CO_VPTR_BASE - 0x1200000) ++#define CO_VPTR_SELF_MAP (CO_VPTR_BASE - 0x1400000) ++ ++typedef struct { ++ unsigned long kernel_cs; ++ unsigned long kernel_ds; ++} __attribute__((packed)) co_arch_info_t; ++ ++#endif +diff -urN a/include/asm-i386/cooperative_internal.h b/include/asm-i386/cooperative_internal.h +--- a/include/asm-i386/cooperative_internal.h ++++ b/include/asm-i386/cooperative_internal.h +@@ -0,0 +1,33 @@ ++/* ++ * linux/include/asm/cooperative_internal.h ++ * ++ * Copyright (C) 2004 Dan Aloni ++ */ ++ ++#ifndef __LINUX_ASM_COOPERATIVE_INTERNAL_H__ ++#define __LINUX_ASM_COOPERATIVE_INTERNAL_H__ ++ ++#include <linux/config.h> ++#include <asm/ptrace.h> ++ ++#ifdef CONFIG_COOPERATIVE ++ ++extern void co_kernel_breakpoint(struct pt_regs * regs); ++extern int co_kernel_debug(struct pt_regs * regs, long error_code, unsigned int condition); ++ ++fastcall unsigned int do_IRQ(struct pt_regs *regs); ++ ++#else ++ ++static inline void co_kernel_breakpoint(struct pt_regs * regs) ++{ ++} ++ ++static inline int co_kernel_debug(struct pt_regs * regs, long error_code, unsigned int condition) ++{ ++ return 0; ++} ++ ++#endif ++ ++#endif +diff -urN a/include/asm-i386/dma.h b/include/asm-i386/dma.h +--- a/include/asm-i386/dma.h ++++ b/include/asm-i386/dma.h +@@ -268,6 +268,7 @@ + * + * Assumes DMA flip-flop is clear. + */ ++#ifndef CONFIG_COOPERATIVE + static __inline__ int get_dma_residue(unsigned int dmanr) + { + unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE +@@ -281,6 +282,7 @@ + + return (dmanr<=3)? count : (count<<1); + } ++#endif + + + /* These are in kernel/dma.c: */ +diff -urN a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h +--- a/include/asm-i386/fixmap.h ++++ b/include/asm-i386/fixmap.h +@@ -31,6 +31,7 @@ + #include <linux/threads.h> + #include <asm/kmap_types.h> + #endif ++#include <asm/cooperative.h> + + /* + * Here we define all the compile-time 'special' virtual +diff -urN a/include/asm-i386/io.h b/include/asm-i386/io.h +--- a/include/asm-i386/io.h ++++ b/include/asm-i386/io.h +@@ -104,12 +104,15 @@ + * address. + */ + +-static inline void __iomem * ioremap(unsigned long offset, unsigned long size) ++static inline void * __iomem ioremap (unsigned long offset, unsigned long size) + { ++#ifdef CONFIG_COOPERATIVE ++ panic("ioremap %ld:%ld\n", offset, size); ++#endif + return __ioremap(offset, size, 0); + } + +-extern void __iomem * ioremap_nocache(unsigned long offset, unsigned long size); ++extern void * __iomem ioremap_nocache (unsigned long offset, unsigned long size); + extern void iounmap(volatile void __iomem *addr); + + /* +@@ -280,7 +283,7 @@ + + #endif /* __KERNEL__ */ + +-#ifdef SLOW_IO_BY_JUMPING ++#if SLOW_IO_BY_JUMPING || CONFIG_COOPERATIVE + #define __SLOW_DOWN_IO "jmp 1f; 1: jmp 1f; 1:" + #else + #define __SLOW_DOWN_IO "outb %%al,$0x80;" +diff -urN a/include/asm-i386/mach-default/irq_vectors.h b/include/asm-i386/mach-default/irq_vectors.h +--- a/include/asm-i386/mach-default/irq_vectors.h ++++ b/include/asm-i386/mach-default/irq_vectors.h +@@ -67,6 +67,11 @@ + + #define TIMER_IRQ 0 + ++#ifdef CONFIG_COOPERATIVE ++#define KEYBOARD_IRQ 1 ++#define NETWORK_IRQ 2 ++#endif ++ + /* + * 16 8259A IRQ's, 208 potential APIC interrupt sources. + * Right now the APIC is mostly only used for SMP. +diff -urN a/include/asm-i386/mach-default/irq_vectors_limits.h b/include/asm-i386/mach-default/irq_vectors_limits.h +--- a/include/asm-i386/mach-default/irq_vectors_limits.h ++++ b/include/asm-i386/mach-default/irq_vectors_limits.h +@@ -5,7 +5,7 @@ + #define NR_IRQS FIRST_SYSTEM_VECTOR + #define NR_IRQ_VECTORS NR_IRQS + #else +-#ifdef CONFIG_X86_IO_APIC ++#if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_X86_UP_COPIC) + #define NR_IRQS 224 + # if (224 >= 32 * NR_CPUS) + # define NR_IRQ_VECTORS NR_IRQS +diff -urN a/include/asm-i386/mc146818rtc.h b/include/asm-i386/mc146818rtc.h +--- a/include/asm-i386/mc146818rtc.h ++++ b/include/asm-i386/mc146818rtc.h +@@ -4,6 +4,7 @@ + #ifndef _ASM_MC146818RTC_H + #define _ASM_MC146818RTC_H + ++#include <linux/config.h> + #include <asm/io.h> + + #ifndef RTC_PORT +@@ -11,6 +12,8 @@ + #define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */ + #endif + ++#ifndef CONFIG_COOPERATIVE ++ + /* + * The yet supported machines all access the RTC index register via + * an ISA port access but the way to access the date register differs ... +@@ -24,6 +27,11 @@ + outb_p((val),RTC_PORT(1)); \ + }) + ++#else ++#define CMOS_READ(addr) (0) ++#define CMOS_WRITE(val, addr) do {} while(0) ++#endif ++ + #define RTC_IRQ 8 + + #endif /* _ASM_MC146818RTC_H */ +diff -urN a/include/asm-i386/mmzone.h b/include/asm-i386/mmzone.h +--- a/include/asm-i386/mmzone.h ++++ b/include/asm-i386/mmzone.h +@@ -6,7 +6,9 @@ + #ifndef _ASM_MMZONE_H_ + #define _ASM_MMZONE_H_ + ++#include <linux/config.h> + #include <asm/smp.h> ++#include <asm/cooperative.h> + + #ifdef CONFIG_DISCONTIGMEM + +@@ -116,7 +118,8 @@ + (unsigned long)(__page - __zone->zone_mem_map) \ + + __zone->zone_start_pfn; \ + }) +-#define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) ++ ++#define pmd_page(pmd) (pfn_to_page(CO_P_TO_PP(pmd_val(pmd)) >> PAGE_SHIFT)) + + #ifdef CONFIG_X86_NUMAQ /* we have contiguous memory on NUMA-Q */ + #define pfn_valid(pfn) ((pfn) < num_physpages) +diff -urN a/include/asm-i386/page.h b/include/asm-i386/page.h +--- a/include/asm-i386/page.h ++++ b/include/asm-i386/page.h +@@ -13,6 +13,7 @@ + #ifndef __ASSEMBLY__ + + #include <linux/config.h> ++#include <asm/cooperative.h> + + #ifdef CONFIG_X86_USE_3DNOW + +@@ -126,6 +127,19 @@ + #define __PAGE_OFFSET (0xC0000000UL) + #endif + ++#ifdef CONFIG_COOPERATIVE ++#define CO_PA(pfn) (((unsigned long *)CO_VPTR_PSEUDO_RAM_PAGE_TABLES)[pfn]) ++#define CO_VA_PFN(pa) (((unsigned long *)CO_VPTR_PHYSICAL_TO_PSEUDO_PFN_MAP)[((pa) >> PAGE_SHIFT)]) ++#define CO_PFN_PP_TO_P(pfn) (CO_PA(pfn) >> PAGE_SHIFT) ++#define CO_PFN_P_TO_PP(pfn) (CO_VA_PFN(pfn << PAGE_SHIFT)) ++#define CO_PP_TO_P(pa) ((CO_PFN_PP_TO_P(pa >> PAGE_SHIFT) << PAGE_SHIFT) | (pa & ~PAGE_MASK)) ++#define CO_P_TO_PP(pa) ((CO_PFN_P_TO_PP(pa >> PAGE_SHIFT) << PAGE_SHIFT) | (pa & ~PAGE_MASK)) ++#else ++#define CO_PFN_P_TO_PP(pfn) pfn ++#define CO_PFN_PP_TO_P(pfn) pfn ++#define CO_PP_TO_P(pa) pa ++#define CO_P_TO_PP(pa) pa ++#endif + + #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) + #define VMALLOC_RESERVE ((unsigned long)__VMALLOC_RESERVE) +diff -urN a/include/asm-i386/param.h b/include/asm-i386/param.h +--- a/include/asm-i386/param.h ++++ b/include/asm-i386/param.h +@@ -2,8 +2,12 @@ + #define _ASMi386_PARAM_H + + #ifdef __KERNEL__ ++# include <linux/config.h> ++# ifndef CONFIG_COOPERATIVE + # define HZ 1000 /* Internal kernel timer frequency */ ++# else + # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ ++# endif + # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ + #endif + +diff -urN a/include/asm-i386/pgalloc.h b/include/asm-i386/pgalloc.h +--- a/include/asm-i386/pgalloc.h ++++ b/include/asm-i386/pgalloc.h +@@ -6,15 +6,16 @@ + #include <asm/fixmap.h> + #include <linux/threads.h> + #include <linux/mm.h> /* for struct page */ ++#include <asm/cooperative.h> + + #define pmd_populate_kernel(mm, pmd, pte) \ +- set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) ++ set_pmd(pmd, __pmd(_PAGE_TABLE + CO_PP_TO_P(__pa(pte)))) + + static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) + { + set_pmd(pmd, __pmd(_PAGE_TABLE + +- ((unsigned long long)page_to_pfn(pte) << +- (unsigned long long) PAGE_SHIFT))); ++ ((CO_PFN_PP_TO_P((unsigned long long)page_to_pfn(pte))) << ++ (unsigned long long) PAGE_SHIFT))); + } + /* + * Allocate and free page tables. +diff -urN a/include/asm-i386/pgtable-2level.h b/include/asm-i386/pgtable-2level.h +--- a/include/asm-i386/pgtable-2level.h ++++ b/include/asm-i386/pgtable-2level.h +@@ -8,6 +8,9 @@ + #define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + ++#include <linux/config.h> ++#include <asm/cooperative.h> ++ + /* + * The "pgd_xxx()" functions here are trivial for a folded two-level + * setup: the pgd is never bad, and a pmd always exists (as it's folded +@@ -33,19 +36,21 @@ + #define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval) + + #define pgd_page(pgd) \ +-((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) ++ ((unsigned long) __va(CO_P_TO_PP(pgd_val(pgd)) & PAGE_MASK)) + + static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) + { + return (pmd_t *) dir; + } ++ + #define ptep_get_and_clear(xp) __pte(xchg(&(xp)->pte_low, 0)) + #define pte_same(a, b) ((a).pte_low == (b).pte_low) ++ + #define pte_page(x) pfn_to_page(pte_pfn(x)) ++#define pte_pfn(x) CO_PFN_P_TO_PP((unsigned long)(((x).pte_low >> PAGE_SHIFT))) ++#define pfn_pte(pfn, prot) __pte((CO_PFN_PP_TO_P(pfn) << PAGE_SHIFT) | pgprot_val(prot)) ++#define pfn_pmd(pfn, prot) __pmd((CO_PFN_PP_TO_P(pfn) << PAGE_SHIFT) | pgprot_val(prot)) + #define pte_none(x) (!(x).pte_low) +-#define pte_pfn(x) ((unsigned long)(((x).pte_low >> PAGE_SHIFT))) +-#define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) +-#define pfn_pmd(pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) + + /* + * All present user pages are user-executable: +diff -urN a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h +--- a/include/asm-i386/pgtable.h ++++ b/include/asm-i386/pgtable.h +@@ -25,6 +25,8 @@ + #include <linux/list.h> + #include <linux/spinlock.h> + ++#include <asm/cooperative.h> ++ + /* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. +@@ -294,10 +296,10 @@ + #define page_pte(page) page_pte_prot(page, __pgprot(0)) + + #define pmd_page_kernel(pmd) \ +-((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) ++((unsigned long) __va(CO_P_TO_PP(pmd_val(pmd)) & PAGE_MASK)) + + #ifndef CONFIG_DISCONTIGMEM +-#define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) ++#define pmd_page(pmd) (pfn_to_page(CO_PFN_P_TO_PP(pmd_val(pmd) >> PAGE_SHIFT))) + #endif /* !CONFIG_DISCONTIGMEM */ + + #define pmd_large(pmd) \ +diff -urN a/include/asm-i386/processor.h b/include/asm-i386/processor.h +--- a/include/asm-i386/processor.h ++++ b/include/asm-i386/processor.h +@@ -182,8 +182,7 @@ + } + + #define load_cr3(pgdir) \ +- asm volatile("movl %0,%%cr3": :"r" (__pa(pgdir))) +- ++ asm volatile("movl %0,%%cr3": :"r" (CO_PP_TO_P(__pa(pgdir)))) + + /* + * Intel CPU features in CR4 +diff -urN a/include/asm-i386/timer.h b/include/asm-i386/timer.h +--- a/include/asm-i386/timer.h ++++ b/include/asm-i386/timer.h +@@ -19,7 +19,7 @@ + struct timer_opts { + char* name; + void (*mark_offset)(void); +- unsigned long (*get_offset)(void); ++ long (*get_offset)(void); + unsigned long long (*monotonic_clock)(void); + void (*delay)(unsigned long); + }; +@@ -50,6 +50,9 @@ + #ifdef CONFIG_X86_CYCLONE_TIMER + extern struct init_timer_opts timer_cyclone_init; + #endif ++#ifdef CONFIG_COOPERATIVE ++extern struct init_timer_opts timer_cooperative_init; ++#endif + + extern unsigned long calibrate_tsc(void); + extern void init_cpu_khz(void); +diff -urN a/include/linux/console.h b/include/linux/console.h +--- a/include/linux/console.h ++++ b/include/linux/console.h +@@ -61,6 +61,7 @@ + extern const struct consw dummy_con; /* dummy console buffer */ + extern const struct consw fb_con; /* frame buffer based console */ + extern const struct consw vga_con; /* VGA text console */ ++extern const struct consw colinux_con; /* coLinux Mode text console */ + extern const struct consw newport_con; /* SGI Newport console */ + extern const struct consw prom_con; /* SPARC PROM console */ + +diff -urN a/include/linux/cooperative.h b/include/linux/cooperative.h +--- a/include/linux/cooperative.h ++++ b/include/linux/cooperative.h +@@ -0,0 +1,322 @@ ++/* ++ * linux/include/linux/cooperative.h ++ * ++ * Copyright (C) 2004 Dan Aloni ++ * ++ * This file defines the interfaces between the Cooperative Linux kernel ++ * and the host OS driver. It's for both external inclusion from the ++ * and internal inclusion in the kernel sources. ++ */ ++ ++#ifndef __LINUX_COOPERATIVE_H__ ++#define __LINUX_COOPERATIVE_H__ ++ ++#ifdef __KERNEL__ ++#ifndef CO_KERNEL ++#define CO_COLINUX_KERNEL ++#define CO_KERNEL ++#endif ++#endif ++ ++#include <asm/cooperative.h> ++ ++#define CO_LINUX_API_VERSION 8 ++ ++#pragma pack(0) ++ ++#define CO_BOOTPARAM_STRING_LENGTH 0x100 ++ ++typedef enum { ++ CO_OPERATION_EMPTY=0, ++ CO_OPERATION_START, ++ CO_OPERATION_IDLE, ++ CO_OPERATION_TERMINATE, ++ CO_OPERATION_MESSAGE_TO_MONITOR, ++ CO_OPERATION_MESSAGE_FROM_MONITOR, ++ CO_OPERATION_FORWARD_INTERRUPT, ++ CO_OPERATION_DEVICE, ++ CO_OPERATION_GET_TIME, ++ CO_OPERATION_DEBUG_LINE, ++ CO_OPERATION_GET_HIGH_PREC_TIME, ++ CO_OPERATION_TRACE_POINT, ++ CO_OPERATION_FREE_PAGES, ++ CO_OPERATION_ALLOC_PAGES, ++} co_operation_t; ++ ++#define CO_MODULE_MAX_CONET 16 ++#define CO_MODULE_MAX_COBD 32 ++#define CO_MODULE_MAX_COFS 32 ++#define CO_MODULE_MAX_SERIAL 64 ++ ++typedef enum { ++ CO_MODULE_LINUX, ++ CO_MODULE_MONITOR, ++ CO_MODULE_DAEMON, ++ CO_MODULE_IDLE, ++ CO_MODULE_KERNEL_SWITCH, ++ CO_MODULE_USER_SWITCH, ++ CO_MODULE_CONSOLE, ++ CO_MODULE_PRINTK, ++ ++ CO_MODULE_CONET0, ++ CO_MODULE_CONET_END=CO_MODULE_CONET0+CO_MODULE_MAX_CONET-1, ++ ++ CO_MODULE_COBD0, ++ CO_MODULE_COBD_END=CO_MODULE_COBD0+CO_MODULE_MAX_COBD-1, ++ ++ CO_MODULE_COFS0, ++ CO_MODULE_COFS_END=CO_MODULE_COFS0+CO_MODULE_MAX_COFS-1, ++ ++ CO_MODULE_SERIAL0, ++ CO_MODULE_SERIAL_END=CO_MODULE_SERIAL0+CO_MODULE_MAX_SERIAL-1, ++} co_module_t; ++ ++typedef enum { ++ CO_PRIORITY_DISCARDABLE=0, ++ CO_PRIORITY_IMPORTANT, ++} co_priority_t; ++ ++typedef enum { ++ CO_MESSAGE_TYPE_STRING=0, ++ CO_MESSAGE_TYPE_OTHER=1, ++} co_message_type_t; ++ ++typedef struct { ++ co_module_t from; ++ co_module_t to; ++ co_priority_t priority; ++ co_message_type_t type; ++ unsigned long size; ++ char data[0]; ++} __attribute__((packed)) co_message_t; ++ ++typedef enum { ++ CO_DEVICE_BLOCK=0, ++ CO_DEVICE_CONSOLE, ++ CO_DEVICE_KEYBOARD, ++ CO_DEVICE_NETWORK, ++ CO_DEVICE_TIMER, ++ CO_DEVICE_POWER, ++ CO_DEVICE_SERIAL, ++ CO_DEVICE_FILESYSTEM, ++ ++ CO_DEVICES_TOTAL, ++} co_device_t; ++ ++typedef struct { ++ unsigned char code; ++ int down; ++} co_scan_code_t; ++ ++typedef enum { ++ CO_LINUX_MESSAGE_POWER_ALT_CTRL_DEL=0, ++} co_linux_message_power_type_t; ++ ++typedef struct { ++ co_linux_message_power_type_t type; ++} __attribute__((packed)) co_linux_message_power_t; ++ ++typedef struct { ++ unsigned long tick_count; ++} __attribute__((packed)) co_linux_message_idle_t; ++ ++typedef struct { ++ co_device_t device; ++ unsigned long unit; ++ unsigned long size; ++ char data[]; ++} __attribute__((packed)) co_linux_message_t; ++ ++typedef enum { ++ CO_TERMINATE_END=0, ++ CO_TERMINATE_REBOOT, ++ CO_TERMINATE_POWEROFF, ++ CO_TERMINATE_PANIC, ++ CO_TERMINATE_HALT, ++ CO_TERMINATE_FORCED_OFF, ++ CO_TERMINATE_FORCED_END, ++ CO_TERMINATE_INVALID_OPERATION, ++ CO_TERMINATE_STACK_OVERFLOW, ++ CO_TERMINATE_BUG, ++} co_termination_reason_t; ++ ++typedef void (*co_switcher_t)(co_arch_passage_page_t *page, ++ unsigned char *from, ++ unsigned char *to); ++ ++#define co_passage_page_func_low(_from_,_to_) \ ++ (((co_switcher_t)(co_passage_page->code)) \ ++ (co_passage_page, \ ++ (char *)&_from_.border2, \ ++ (char *)&_to_.border2)) ++ ++#define co_passage_page_func(_from_,_to_) \ ++ co_passage_page_func_low(co_passage_page->_from_, co_passage_page->_to_) ++ ++#ifdef CO_KERNEL ++# ifdef CO_COLINUX_KERNEL ++# define co_passage_page ((co_arch_passage_page_t *)(CO_VPTR_PASSAGE_PAGE)) ++# define co_current (co_passage_page->linuxvm_state) ++# define co_other (co_passage_page->host_state) ++# else ++# define co_passage_page (cmon->passage_page) ++# define co_other (co_passage_page->linuxvm_state) ++# define co_current (co_passage_page->host_state) ++# endif ++ ++# define co_switch() co_passage_page_func_low(co_current, co_other) ++#endif ++ ++/* ++ * Defines operations on various virtual devices. ++ */ ++ ++typedef enum { ++ CO_OPERATION_CONSOLE_STARTUP=0, ++ CO_OPERATION_CONSOLE_INIT=1, ++ CO_OPERATION_CONSOLE_DEINIT, ++ CO_OPERATION_CONSOLE_CLEAR, ++ CO_OPERATION_CONSOLE_PUTC, ++ CO_OPERATION_CONSOLE_PUTCS, ++ CO_OPERATION_CONSOLE_CURSOR_DRAW, ++ CO_OPERATION_CONSOLE_CURSOR_ERASE, ++ CO_OPERATION_CONSOLE_CURSOR_MOVE, ++ CO_OPERATION_CONSOLE_SCROLL_UP, ++ CO_OPERATION_CONSOLE_SCROLL_DOWN, ++ CO_OPERATION_CONSOLE_BMOVE, ++ CO_OPERATION_CONSOLE_SWITCH, ++ CO_OPERATION_CONSOLE_BLANK, ++ CO_OPERATION_CONSOLE_FONT_OP, ++ CO_OPERATION_CONSOLE_SET_PALETTE, ++ CO_OPERATION_CONSOLE_SCROLLDELTA, ++ CO_OPERATION_CONSOLE_SET_ORIGIN, ++ CO_OPERATION_CONSOLE_SAVE_SCREEN, ++ CO_OPERATION_CONSOLE_INVERT_REGION, ++} co_operation_console_t; ++ ++ ++typedef char co_console_code; ++typedef unsigned short co_console_character; ++typedef unsigned short co_console_unit; ++ ++typedef struct { ++ co_console_unit x; ++ co_console_unit y; ++ co_console_unit height; ++} __attribute__((packed)) co_cursor_pos_t; ++ ++typedef struct { ++ co_operation_console_t type; ++ union { ++ struct { ++ co_console_unit top; ++ co_console_unit bottom; ++ co_console_unit lines; ++ } scroll; ++ struct { ++ co_console_unit y; ++ co_console_unit x; ++ co_console_unit count; ++ co_console_character data[]; ++ } putcs; ++ struct { ++ co_console_unit x; ++ co_console_unit y; ++ co_console_character charattr; ++ } putc; ++ struct { ++ co_console_unit top; ++ co_console_unit left; ++ co_console_unit bottom; ++ co_console_unit right; ++ co_console_character charattr; ++ } clear; ++ struct { ++ co_console_unit y; ++ co_console_unit x; ++ co_console_unit count; ++ } invert; ++ struct { ++ co_console_unit row; ++ co_console_unit column; ++ co_console_unit top; ++ co_console_unit left; ++ co_console_unit bottom; ++ co_console_unit right; ++ } bmove; ++ co_cursor_pos_t cursor; ++ }; ++} __attribute__((packed)) co_console_message_t; ++ ++typedef struct { ++ unsigned long index; ++ unsigned long flags; ++ unsigned long func; ++ unsigned long pid; ++} __attribute__((packed)) co_trace_point_info_t; ++ ++typedef enum { ++ CO_BLOCK_OPEN=0, ++ CO_BLOCK_STAT, ++ CO_BLOCK_READ, ++ CO_BLOCK_WRITE, ++ CO_BLOCK_CLOSE, ++ CO_BLOCK_GET_ALIAS, ++} co_block_request_type_t; ++ ++typedef enum { ++ CO_NETWORK_GET_MAC=0, ++} co_network_request_type_t; ++ ++#ifdef CO_KERNEL ++/* If we are compiling kernel code (Linux or Host Driver) */ ++# ifdef CO_COLINUX_KERNEL ++/* Inside Linux, vm_ptr_t considered a valid pointer in its virtual address space */ ++typedef void *vm_ptr_t; ++# else ++/* But inside the host, the type is considered not to be a pointer in its own address space */ ++typedef unsigned long vm_ptr_t; ++# endif ++ ++typedef struct { ++ co_block_request_type_t type; ++ long rc; ++ union { ++ struct { ++ unsigned long long offset; ++ unsigned long long size; ++ unsigned long long disk_size; ++ vm_ptr_t address; ++ }; ++ struct { ++ char alias[20]; ++ }; ++ }; ++} __attribute__((packed)) co_block_request_t; ++ ++typedef struct { ++ co_network_request_type_t type; ++ unsigned long unit; ++ char mac_address[6]; ++ char _pad[2]; ++ int result; ++} __attribute__((packed)) co_network_request_t; ++ ++#endif ++ ++typedef struct { ++ unsigned long api_version; ++ unsigned long compiler_major; ++ unsigned long compiler_minor; ++} __attribute__((packed)) co_info_t; ++ ++#ifndef COLINUX_TRACE ++#define CO_TRACE_STOP ++#define CO_TRACE_CONTINUE ++#endif ++ ++#pragma pack() ++ ++#include "cooperative_fs.h" ++ ++#endif +diff -urN a/include/linux/cooperative_fs.h b/include/linux/cooperative_fs.h +--- a/include/linux/cooperative_fs.h ++++ b/include/linux/cooperative_fs.h +@@ -0,0 +1,267 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2004 Miklos Szeredi <miklos@szeredi.hu> ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++*/ ++ ++/* This file defines the kernel interface of FUSE */ ++ ++#pragma pack(0) ++ ++/** Version number of this interface */ ++#define FUSE_KERNEL_VERSION 2 ++ ++/** Minor version number of this interface */ ++#define FUSE_KERNEL_MINOR_VERSION 2 ++ ++/** The inode number of the root inode */ ++#define FUSE_ROOT_INO 1 ++ ++/** Data passed to mount */ ++struct fuse_mount_data { ++ /** The file type of the root inode */ ++ unsigned int rootmode; ++ ++ /** The user ID of the user initiating this mount */ ++ unsigned int uid; ++ ++ /** FUSE specific mount flags */ ++ unsigned int flags; ++}; ++ ++/* FUSE mount flags: */ ++ ++/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem ++module will check permissions based on the file mode. Otherwise no ++permission checking is done in the kernel */ ++#define FUSE_DEFAULT_PERMISSIONS (1 << 0) ++ ++/** If the FUSE_ALLOW_OTHER flag is given, then not only the user ++ doing the mount will be allowed to access the filesystem */ ++#define FUSE_ALLOW_OTHER (1 << 1) ++ ++/** If the FUSE_KERNEL_CACHE flag is given, then files will be cached ++ until the INVALIDATE operation is invoked */ ++#define FUSE_KERNEL_CACHE (1 << 2) ++ ++/** Allow FUSE to combine reads into 64k chunks. This is useful if ++ the filesystem is better at handling large chunks. NOTE: in ++ current implementation the raw throughput is worse for large reads ++ than for small. */ ++#define FUSE_LARGE_READ (1 << 3) ++ ++struct fuse_attr { ++ unsigned long long size; ++ unsigned int mode; ++ unsigned int nlink; ++ unsigned int uid; ++ unsigned int gid; ++ unsigned int rdev; ++ unsigned long _dummy; ++ unsigned long blocks; ++ unsigned long atime; ++ unsigned long mtime; ++ unsigned long ctime; ++}; ++ ++struct fuse_kstatfs { ++ long block_size; ++ long blocks; ++ long blocks_free; ++ long files; ++ long files_free; ++ long namelen; ++}; ++ ++#define FATTR_MODE (1 << 0) ++#define FATTR_UID (1 << 1) ++#define FATTR_GID (1 << 2) ++#define FATTR_SIZE (1 << 3) ++#define FATTR_UTIME (1 << 4) ++ ++enum fuse_opcode { ++ FUSE_LOOKUP = 1, ++ FUSE_FORGET = 2, /* no reply */ ++ FUSE_GETATTR = 3, ++ FUSE_SETATTR = 4, ++ FUSE_READLINK = 5, ++ FUSE_SYMLINK = 6, ++ FUSE_GETDIR = 7, ++ FUSE_MKNOD = 8, ++ FUSE_MKDIR = 9, ++ FUSE_UNLINK = 10, ++ FUSE_RMDIR = 11, ++ FUSE_RENAME = 12, ++ FUSE_LINK = 13, ++ FUSE_OPEN = 14, ++ FUSE_READ = 15, ++ FUSE_WRITE = 16, ++ FUSE_STATFS = 17, ++ FUSE_RELEASE = 18, /* no reply */ ++ FUSE_INVALIDATE = 19, /* user initiated */ ++ FUSE_FSYNC = 20, ++ FUSE_RELEASE2 = 21, /* reply needed after all */ ++ ++ /* Cooperative Linux does things a little differently: */ ++ FUSE_DIR_OPEN = 22, ++ FUSE_DIR_READ = 23, ++ FUSE_DIR_RELEASE = 24, ++ ++ FUSE_MOUNT = 25, ++}; ++ ++/* Conservative buffer size for the client */ ++#define FUSE_MAX_IN 8192 ++ ++#define FUSE_NAME_MAX 1024 ++#define FUSE_SYMLINK_MAX 4096 ++ ++struct fuse_lookup_out { ++ struct fuse_attr attr; ++ unsigned long ino; ++}; ++ ++struct fuse_forget_in { ++ int version; ++}; ++ ++struct fuse_getattr_out { ++ struct fuse_attr attr; ++}; ++ ++struct fuse_getdir_out { ++ int fd; ++ void *file; /* Used by kernel only */ ++}; ++ ++/* FIXME: 2.6 needs 32 bit rdev */ ++struct fuse_mknod_in { ++ unsigned short mode; ++ unsigned short rdev; ++}; ++ ++struct fuse_mknod_out { ++ struct fuse_attr attr; ++ unsigned long ino; ++}; ++ ++struct fuse_mkdir_in { ++ unsigned short mode; ++}; ++ ++struct fuse_rename_in { ++ unsigned long newdir; ++}; ++ ++struct fuse_link_in { ++ unsigned long newdir; ++}; ++ ++struct fuse_setattr_in { ++ struct fuse_attr attr; ++ unsigned int valid; ++}; ++ ++struct fuse_setattr_out { ++ struct fuse_attr attr; ++}; ++ ++struct fuse_open_in { ++ unsigned int flags; ++}; ++ ++struct fuse_read_in { ++ unsigned long long offset; ++ unsigned int size; ++}; ++ ++struct fuse_write_in { ++ unsigned long long offset; ++ unsigned int size; ++}; ++ ++struct fuse_statfs_out { ++ struct fuse_kstatfs st; ++}; ++ ++struct fuse_fsync_in { ++ int datasync; ++}; ++ ++struct fuse_in_header { ++ int unique; ++ enum fuse_opcode opcode; ++ unsigned long ino; ++ unsigned int uid; ++ unsigned int gid; ++}; ++ ++struct fuse_out_header { ++ int unique; ++ int error; ++}; ++ ++struct fuse_user_header { ++ int unique; /* zero */ ++ enum fuse_opcode opcode; ++ unsigned long ino; ++}; ++ ++struct fuse_dirent { ++ unsigned long ino; ++ unsigned short namelen; ++ unsigned char type; ++ char name[256]; ++}; ++ ++#define FUSE_S_IFMT 00170000 ++#define FUSE_S_IFSOCK 0140000 ++#define FUSE_S_IFLNK 0120000 ++#define FUSE_S_IFREG 0100000 ++#define FUSE_S_IFBLK 0060000 ++#define FUSE_S_IFDIR 0040000 ++#define FUSE_S_IFCHR 0020000 ++#define FUSE_S_IFIFO 0010000 ++#define FUSE_S_ISUID 0004000 ++#define FUSE_S_ISGID 0002000 ++#define FUSE_S_ISVTX 0001000 ++ ++#define FUSE_S_IRWXU 00700 ++#define FUSE_S_IRUSR 00400 ++#define FUSE_S_IWUSR 00200 ++#define FUSE_S_IXUSR 00100 ++ ++#define FUSE_S_IRWXG 00070 ++#define FUSE_S_IRGRP 00040 ++#define FUSE_S_IWGRP 00020 ++#define FUSE_S_IXGRP 00010 ++ ++#define FUSE_S_IRWXO 00007 ++#define FUSE_S_IROTH 00004 ++#define FUSE_S_IWOTH 00002 ++#define FUSE_S_IXOTH 00001 ++ ++#define FUSE_DT_UNKNOWN 0 ++#define FUSE_DT_FIFO 1 ++#define FUSE_DT_CHR 2 ++#define FUSE_DT_DIR 4 ++#define FUSE_DT_BLK 6 ++#define FUSE_DT_REG 8 ++#define FUSE_DT_LNK 10 ++#define FUSE_DT_SOCK 12 ++#define FUSE_DT_WHT 14 ++ ++#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name) ++#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1)) ++#define FUSE_DIRENT_SIZE(d) \ ++ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) ++#pragma pack() ++ ++/* ++ * Local Variables: ++ * indent-tabs-mode: t ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -urN a/include/linux/cooperative_internal.h b/include/linux/cooperative_internal.h +--- a/include/linux/cooperative_internal.h ++++ b/include/linux/cooperative_internal.h +@@ -0,0 +1,80 @@ ++/* ++ * linux/include/linux/cooperative.h ++ * ++ * Copyright (C) 2004 Dan Aloni ++ * ++ * This header gathers the functions and variables in Cooperative Mode ++ * when CONFIG_COOPERATIVE is defined. ++ */ ++#ifndef __LINUX_COOPERATIVE_LINUX_H__ ++#define __LINUX_COOPERATIVE_LINUX_H__ ++ ++#include <linux/config.h> ++#include <linux/cooperative.h> ++#include <linux/list.h> ++ ++#ifdef CONFIG_COOPERATIVE ++ ++typedef struct { ++ struct list_head node; ++ co_message_t msg; ++} co_message_node_t; ++ ++extern void co_debug(const char *fmt, ...); ++extern void co_printk(const char *line); ++ ++extern void co_callback(unsigned long flags); ++extern void co_switch_wrapper(void); ++extern void co_idle_processor(void); ++extern void co_terminate(co_termination_reason_t reason); ++extern void co_free_pages(unsigned long vaddr, int order); ++extern int co_alloc_pages(unsigned long vaddr, int order); ++extern void co_start_kernel(void); ++extern void co_arch_start_kernel(void); ++extern void co_handle_jiffies(long count); ++ ++extern void co_send_message(co_module_t from, ++ co_module_t to, ++ co_priority_t priority, ++ co_message_type_t type, ++ unsigned long size, ++ const char *data); ++extern unsigned long co_get_host_time(void); ++extern co_message_t *co_send_message_save(unsigned long *flags); ++extern co_message_t *co_get_message_save(unsigned long *flags); ++extern void co_send_message_restore(unsigned long flags); ++ ++extern void cocd_interrupt(void); ++ ++extern void co_handle_incoming_messages(void); ++extern void co_handle_incoming_message(co_message_node_t *message); ++extern void co_queue_incoming_message(co_message_node_t *message); ++extern int co_get_message(co_message_node_t **message, co_device_t device); ++extern void co_free_message(co_message_node_t *message); ++ ++extern int co_passage_page_held(void); ++extern void co_passage_page_acquire(unsigned long *flags); ++extern void co_passage_page_release(unsigned long flags); ++ ++#define co_passage_page_assert_valid() do { \ ++ if (co_passage_page_held()) \ ++ BUG(); \ ++} while (0); ++ ++extern char co_boot_parameters[CO_BOOTPARAM_STRING_LENGTH]; ++extern unsigned long co_core_end; ++extern unsigned long co_memory_size; ++extern void *co_initrd; ++extern unsigned long co_initrd_size; ++ ++#define cooperative_mode_enabled() 1 ++ ++#else ++ ++#define co_printk(line) do {} while (0) ++#define co_terminate(reason) do {} while (0) ++#define cooperative_mode_enabled() 0 ++ ++#endif ++ ++#endif +diff -urN a/include/linux/major.h b/include/linux/major.h +--- a/include/linux/major.h ++++ b/include/linux/major.h +@@ -130,6 +130,7 @@ + #define VIOCD_MAJOR 113 + + #define ATARAID_MAJOR 114 ++#define COLINUX_MAJOR 117 + + #define SCSI_DISK8_MAJOR 128 + #define SCSI_DISK9_MAJOR 129 +diff -urN a/init/do_mounts.c b/init/do_mounts.c +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -175,6 +175,7 @@ + for (p = s; *p; p++) + if (*p == '/') + *p = '!'; ++ + res = try_name(s, 0); + if (res) + goto done; +diff -urN a/init/main.c b/init/main.c +--- a/init/main.c ++++ b/init/main.c +@@ -537,6 +537,7 @@ + panic(panic_later, panic_param); + profile_init(); + local_irq_enable(); ++ + #ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start && !initrd_below_start_ok && + initrd_start < min_low_pfn << PAGE_SHIFT) { +diff -urN a/kernel/Makefile b/kernel/Makefile +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -26,6 +26,7 @@ + obj-$(CONFIG_KPROBES) += kprobes.o + obj-$(CONFIG_SYSFS) += ksysfs.o + obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ ++obj-$(CONFIG_COOPERATIVE) += cooperative.o + + ifneq ($(CONFIG_IA64),y) + # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is +diff -urN a/kernel/cooperative.c b/kernel/cooperative.c +--- a/kernel/cooperative.c ++++ b/kernel/cooperative.c +@@ -0,0 +1,354 @@ ++/* ++ * linux/kernel/cooperative.c ++ * ++ * Cooperative mode (coLinux) support routines. ++ * ++ * Dan Aloni <da-x@colinux.org>, 2003-2004 (C). ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/string.h> ++#include <linux/interrupt.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++#include <linux/cooperative_internal.h> ++ ++CO_TRACE_STOP; ++ ++void start_kernel(void); ++extern char _kernel_start, _end; ++ ++ ++unsigned long co_core_end = 0; ++unsigned long co_memory_size = 0; ++void *co_initrd = NULL; ++unsigned long co_initrd_size = 0; ++char co_boot_parameters[CO_BOOTPARAM_STRING_LENGTH]; ++ ++ ++typedef struct { ++ struct list_head list; ++ int num_messages; ++} co_message_queue_t; ++ ++int co_messages_active = 0; ++co_message_queue_t co_outgoing_messages; ++co_message_queue_t co_incoming_messages; ++co_message_queue_t *co_incoming_queued_messages; ++ ++void co_start_kernel(void) ++{ ++ co_core_end = co_passage_page->params[0]; ++ co_memory_size = co_passage_page->params[1]; ++ co_initrd = (void *)co_passage_page->params[2]; ++ co_initrd_size = co_passage_page->params[3]; ++ ++ memcpy(co_boot_parameters, &co_passage_page->params[10], ++ sizeof(co_boot_parameters)); ++ ++ co_arch_start_kernel(); ++ ++ /* should never be reached */ ++ co_terminate(CO_TERMINATE_END); ++} ++ ++co_message_t *co_send_message_save(unsigned long *flags) ++{ ++ co_passage_page_assert_valid(); ++ co_passage_page_acquire(flags); ++ co_passage_page->operation = CO_OPERATION_MESSAGE_TO_MONITOR; ++ return ((co_message_t *)CO_VPTR_IO_AREA_START); ++} ++ ++void co_send_message_restore(unsigned long flags) ++{ ++ co_switch_wrapper(); ++ co_passage_page_release(flags); ++} ++ ++void co_send_message_s(co_message_t *message, const char *data) ++{ ++ if ((sizeof(co_message_t) + message->size) > CO_VPTR_IO_AREA_SIZE) ++ return; ++ ++ if (co_passage_page_held()) ++ return; ++ ++ unsigned long flags; ++ co_message_t *buffer = ((co_message_t *)CO_VPTR_IO_AREA_START); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_MESSAGE_TO_MONITOR; ++ *buffer = *message; ++ memcpy(buffer->data, data, message->size); ++ co_switch_wrapper(); ++ co_passage_page_release(flags); ++} ++ ++void co_send_message(co_module_t from, ++ co_module_t to, ++ co_priority_t priority, ++ co_message_type_t type, ++ unsigned long size, ++ const char *data) ++{ ++ co_message_t params; ++ ++ params.from = from; ++ params.to = to; ++ params.priority = priority; ++ params.type = type; ++ params.size = size; ++ ++ co_send_message_s(¶ms, data); ++} ++ ++static void co_message_add_to_incoming(co_message_t *message, unsigned long size) ++{ ++ co_message_node_t *message_copy; ++ ++ message_copy = kmalloc(size + sizeof(co_message_node_t) - sizeof(co_message_t), ++ GFP_ATOMIC); ++ if (!message_copy) ++ return; ++ ++ memcpy(&message_copy->msg, message, size); ++ list_add_tail(&message_copy->node, &co_incoming_messages.list); ++} ++ ++void co_callback(unsigned long flags) ++{ ++ if (co_passage_page->operation != CO_OPERATION_MESSAGE_FROM_MONITOR) { ++ co_passage_page_release(flags); ++ return; ++ } ++ ++ long io_size = co_passage_page->params[0]; ++ unsigned long new_jiffies = co_passage_page->params[1]; ++ ++ if (co_messages_active && io_size > 0) { ++ unsigned char *io_buffer = (char *)CO_VPTR_IO_AREA_START; ++ unsigned char *io_buffer_end = io_buffer + io_size; ++ if (!(io_size > CO_VPTR_IO_AREA_SIZE)) { ++ while (io_buffer < io_buffer_end) { ++ co_message_t *message = (co_message_t *)io_buffer; ++ unsigned long size = message->size + sizeof(*message); ++ ++ co_message_add_to_incoming(message, size); ++ io_buffer += size; ++ } ++ } ++ } ++ ++ co_passage_page_release(flags); ++ ++ co_handle_jiffies(new_jiffies); ++ co_handle_incoming_messages(); ++} ++ ++void co_handle_incoming_messages(void) ++{ ++ if (!co_messages_active) ++ return; ++ ++ if (list_empty(&co_incoming_messages.list)) ++ return; ++ ++ for (;;) { ++ unsigned long flags; ++ co_message_node_t *message = NULL; ++ ++ /* ++ * Pop a message from the incoming queue. ++ */ ++ local_irq_save(flags); ++ if (!list_empty(&co_incoming_messages.list)) { ++ message = list_entry(co_incoming_messages.list.next, ++ co_message_node_t, node); ++ list_del(&message->node); ++ } ++ local_irq_restore(flags); ++ ++ if (!message) ++ break; ++ ++ /* ++ * Let the interrupt routine of the arch dependant code ++ * handle the message, and be responsible to free it. ++ */ ++ co_handle_incoming_message(message); ++ } ++} ++ ++void co_idle_processor(void) ++{ ++ unsigned long flags; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_IDLE; ++ co_switch_wrapper(); ++ co_callback(flags); ++} ++ ++void co_printk(const char *line) ++{ ++ co_send_message(CO_MODULE_LINUX, ++ CO_MODULE_PRINTK, ++ CO_PRIORITY_DISCARDABLE, ++ CO_MESSAGE_TYPE_STRING, ++ strlen(line)+1, ++ line); ++} ++ ++void co_debug_line(char *line) ++{ ++} ++ ++void co_terminate(co_termination_reason_t reason) ++{ ++ unsigned long flags; ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_TERMINATE; ++ co_passage_page->params[0] = reason; ++ co_switch_wrapper(); ++ /* This doesn't really return. This code shouldn't be running. */ ++ co_passage_page_release(flags); ++} ++ ++unsigned long co_get_host_time(void) ++{ ++ unsigned long flags; ++ unsigned long time; ++ ++ co_passage_page_assert_valid(); ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_GET_TIME; ++ co_switch_wrapper(); ++ time = co_passage_page->params[0]; ++ co_passage_page_release(flags); ++ ++ return time; ++} ++ ++void co_queue_incoming_message(co_message_node_t *node_message) ++{ ++ if (!co_messages_active) ++ return; ++ ++ co_linux_message_t *message = (co_linux_message_t *)&node_message->msg.data; ++ if (message->device < 0 || (message->device >= CO_DEVICES_TOTAL)) ++ return; ++ ++ co_message_queue_t *queue; ++ queue = &co_incoming_queued_messages[message->device]; ++ ++ /* Add to the queue */ ++ unsigned long flags; ++ local_irq_save(flags); ++ list_add(&node_message->node, &queue->list); ++ queue->num_messages++; ++ local_irq_restore(flags); ++} ++ ++int co_get_message(co_message_node_t **message, co_device_t device) ++{ ++ co_message_queue_t *queue; ++ co_message_node_t *node; ++ unsigned long flags; ++ ++ if (!co_messages_active) ++ return 0; ++ ++ local_irq_save(flags); ++ queue = &co_incoming_queued_messages[device]; ++ if (list_empty(&queue->list)) { ++ local_irq_restore(flags); ++ return 0; ++ } ++ ++ node = list_entry(queue->list.prev, co_message_node_t, node); ++ list_del(&node->node); ++ queue->num_messages--; ++ local_irq_restore(flags); ++ ++ *message = node; ++ return 1; ++} ++ ++void co_free_message(co_message_node_t *message) ++{ ++ kfree(message); ++} ++ ++co_info_t co_info = { ++ .api_version = CO_LINUX_API_VERSION, ++ .compiler_major = __GNUC__, ++ .compiler_minor = __GNUC_MINOR__, ++}; ++ ++static int __init initcall_message_queues(void) ++{ ++ int queue_index; ++ ++ INIT_LIST_HEAD(&co_outgoing_messages.list); ++ INIT_LIST_HEAD(&co_incoming_messages.list); ++ ++ co_incoming_queued_messages = kmalloc(sizeof(co_message_queue_t) * CO_DEVICES_TOTAL, ++ GFP_KERNEL); ++ if (!co_incoming_queued_messages) ++ panic("unable to allocate message queues\n"); ++ ++ for (queue_index=0; queue_index < CO_DEVICES_TOTAL; queue_index++) { ++ co_message_queue_t *queue = &co_incoming_queued_messages[queue_index]; ++ queue->num_messages = 0; ++ INIT_LIST_HEAD(&queue->list); ++ } ++ ++ co_messages_active = 1; ++ ++ return 0; ++} ++ ++ ++void co_free_pages(unsigned long vaddr, int order) ++{ ++ unsigned long flags; ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_FREE_PAGES; ++ co_passage_page->params[0] = vaddr; ++ co_passage_page->params[1] = order; ++ co_switch_wrapper(); ++ co_passage_page_release(flags); ++} ++ ++int co_alloc_pages(unsigned long vaddr, int order) ++{ ++ unsigned long flags; ++ long result; ++ ++ co_passage_page_acquire(&flags); ++ co_passage_page->operation = CO_OPERATION_ALLOC_PAGES; ++ co_passage_page->params[0] = vaddr; ++ co_passage_page->params[1] = order; ++ co_switch_wrapper(); ++ result = (long)co_passage_page->params[4]; ++ co_passage_page_release(flags); ++ ++ if (result < 0) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++__initcall(initcall_message_queues); ++ ++EXPORT_SYMBOL(co_terminate); ++ ++CO_TRACE_CONTINUE; +diff -urN a/kernel/panic.c b/kernel/panic.c +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -18,6 +18,7 @@ + #include <linux/sysrq.h> + #include <linux/interrupt.h> + #include <linux/nmi.h> ++#include <linux/cooperative_internal.h> + + int panic_timeout; + int panic_on_oops; +@@ -71,6 +72,10 @@ + printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf); + bust_spinlocks(0); + ++ if (cooperative_mode_enabled()) { ++ co_terminate(CO_TERMINATE_PANIC); ++ } ++ + #ifdef CONFIG_SMP + smp_send_stop(); + #endif +diff -urN a/kernel/printk.c b/kernel/printk.c +--- a/kernel/printk.c ++++ b/kernel/printk.c +@@ -34,6 +34,8 @@ + + #include <asm/uaccess.h> + ++#include <linux/cooperative_internal.h> ++ + #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) + + /* printk's without a loglevel use this.. */ +@@ -538,6 +540,8 @@ + /* Emit the output into the temporary buffer */ + printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); + ++ co_printk(printk_buf); ++ + /* + * Copy the output into log_buf. If the caller didn't provide + * appropriate log level tags, we insert them here +diff -urN a/localversion-cooperative b/localversion-cooperative +--- a/localversion-cooperative ++++ b/localversion-cooperative +@@ -0,0 +1 @@ ++-co- +diff -urN a/mm/bootmem.c b/mm/bootmem.c +--- a/mm/bootmem.c ++++ b/mm/bootmem.c +@@ -17,6 +17,7 @@ + #include <linux/bootmem.h> + #include <linux/mmzone.h> + #include <linux/module.h> ++#include <linux/cooperative_internal.h> + #include <asm/dma.h> + #include <asm/io.h> + +@@ -248,6 +249,23 @@ + for (i = start; i < start+areasize; i++) + if (unlikely(test_and_set_bit(i, bdata->node_bootmem_map))) + BUG(); ++ ++ if (cooperative_mode_enabled()) { ++ unsigned long alloc_address = (unsigned long)ret; ++ unsigned long alloc_size = size; ++ int result; ++ ++ alloc_size += (alloc_address & (~PAGE_MASK)); ++ alloc_address &= PAGE_MASK; ++ alloc_size = (alloc_size + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ ++ result = co_alloc_pages(alloc_address, alloc_size); ++ if (result) { ++ free_bootmem((unsigned long)ret, size); ++ return NULL; ++ } ++ } ++ + memset(ret, 0, size); + return ret; + } +diff -urN a/mm/page_alloc.c b/mm/page_alloc.c +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -32,7 +32,7 @@ + #include <linux/sysctl.h> + #include <linux/cpu.h> + #include <linux/nodemask.h> +- ++#include <linux/cooperative_internal.h> + #include <asm/tlbflush.h> + + nodemask_t node_online_map = NODE_MASK_NONE; +@@ -184,6 +184,9 @@ + { + unsigned long page_idx, index, mask; + ++ if (cooperative_mode_enabled()) ++ co_free_pages((unsigned long)page_address(page), 1 << order); ++ + if (order) + destroy_compound_page(page, order); + mask = (~0UL) << order; +@@ -738,6 +741,37 @@ + got_pg: + zone_statistics(zonelist, z); + kernel_map_pages(page, 1 << order, 1); ++ ++ if (cooperative_mode_enabled()) { ++ int result, retries_left; ++ ++ retries_left = 10; ++ ++ while (retries_left > 0) { ++ result = co_alloc_pages((unsigned long)page_address(page), 1 << order); ++ if (result) { ++ unsigned long cache_size; ++ /* ++ * Whoops, we have allocated too much of the ++ * host OS's memory, time to free some cache. ++ * cache. ++ */ ++ cache_size = get_page_cache_size()-total_swapcache_pages; ++ cache_size /= 2; ++ if (cache_size < ((1 << order)*2)) ++ cache_size = (1 << order)*2; ++ shrink_all_memory(cache_size); ++ } else ++ break; ++ retries_left--; ++ } ++ ++ if (result) { ++ __free_pages(page, order); ++ return NULL; ++ } ++ } ++ + return page; + } + +diff -urN a/mm/vmscan.c b/mm/vmscan.c +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1180,7 +1180,7 @@ + wake_up_interruptible(&zone->zone_pgdat->kswapd_wait); + } + +-#ifdef CONFIG_PM ++#if defined(CONFIG_PM) || defined(CONFIG_COOPERATIVE) + /* + * Try to free `nr_pages' of memory, system-wide. Returns the number of freed + * pages. |