Skip to contentSkip to conversation

x86 multiple user-mode privilege rings

RFC PATCH In review Luca Barbieri 4 participants 5 messages Oct 27, 2002 – Oct 28, 2002

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 x86 multiple user-mode privilege rings

Short explaination:
This patch implements a feature called "x86 multiring", which is a shorthand for x86 multiple user-mode privilege rings support. It allows user-mode programs to create DPL 1 and 2 segments and get a modifiable per-process copy of IDT.

User Mode Linux can use these features to implement a syscall mechanism identical to the one used by the kernel-mode kernel, and thus much faster than the current one, with free memory protection and with zero context switches.

Wine could also use it to achieve fast syscall-level emulation of Windows NT (and, to a lesser extent, Windows 3.1 and 9x).

Obviously there is some risk of the patch creating security holes.

System calls:
All operations are performed using the new sys_multiring syscall. The API is documented in include/asm-i386/multiring.h, that multiring applications should include.

Supervisor problems:
The most serious issue caused by the use of ring 1 and 2 is that they are intended for kernel code, which means that they count as supervisor wherever a "user/supervisor" bit is present.

This results in:
- Unavailaibility of multiring on 386 processors since they don't support supervisor WP.

- Page protection no longer working
While this may seem catastrophic, it isn't because segment-level protection can be used instead.
To enforce it, the patch modifies the GDT so that the default CS and DS have a limit at __PAGE_OFFSET - 1.
LDT and TLS interfaces are also changed to alter segment limits to avoid overlap with the kernel area. If this is impossible, multiring mode is inhibited using bad_segments mm_context_t field; if the process is already in multiring mode the operation fails.
vsyscalls will need to be put before the rest of the kernel with this scheme.

- Supervisor bit in error code in page faults not reliable (regs->xcs is used instead, with the only problem of not being able to tell f00f invalid opcodes from page faults)

- Potential minor problems for profilers since to get DPL 1/2 events, DPL 0 events also have to be enabled.

Based on my reading of Intel manuals, there should not be any other problem, but I might have missed something.

IDT functionality:
When multiring mode is entered, the default IDT is copied to a new allocated page and a pointer to the new one is stored in mm_context_t. The initial multiring IDT is identical to the default one, with the exception of the SYSCALL_VECTOR DPL which is set to 1.
The code includes a config option to put IDTs in high memory, that is however untested and not very useful anyway.

IDTs are loaded in two different ways: if CONFIG_X86_HIGHIDT is set or the processor is f00f-buggy, each CPU gets a fixmap entry that is remapped to load another IDT; otherwise, a simple lidt instruction is used.

sys_multiring allows to read, copy and set gates in the IDT. The vectors that are settable are currently 0x20-0x2f (because DOS and Windows are here), 0x80 and 0xf1-0xfa.
Set operations will fail if the user tries to set a gate to a kernel mode address which isn't the syscall one or a task or interrupt gate. The i8259 is remapped to 0x30-0x3f to accomodate this.

The multiring_mode filed is added to the thread structure, and is 1 if the thread has entered multiring mode (i.e. selectors are RPL 1-ed).

GDT functionality:
When switching to a multiring mm, the DPL in the default user CS and DS is set to 1, to prevent ring 2 and 3 to load them and thus bypass any security that the DPL 1 code might be enforcing.

LDT/TLS functionality:
When in multiring mode, LDT/TLS functions honor the new dpl field in struct user_desc that of course allows to set a custom dpl in the descriptors.
They are also changed to support segment-level protection as outlined above.

TSS functionality:
sys_multiring allows to modify the ring 1 and 2 TSS ESP and SS that are loaded on inter-privilege call.
The values are kept in the thread structure and are loaded on task switch.

clone functionality:
The CLONE_IDT flag is added, and does the obvious thing. Note that if the task is not in multiring mode, it is silently ignored. The CLONE_CLEAR_IDT flag is also added and also does the obvious thing and takes precedence over CLONE_IDT.

Entering multiring mode:
Multiring mode can be entered using sys_multiring(MULTIRING_ELEVATE). This will allocate a new IDT, fix the GDT and put RPL 1 selectors in cs/ds/es/ss/fs/gs. Note that all other threads will also get RPL 1 selectors.
RPL 1 selectors are loaded only if the selector points to the default CS or DS.

Limitations:
- Since there are only 4 privilege rings only up to 2 UMLs can be nested
- Doesn't work on 386

Potential improvements:
- Copy on write IDTs
- Reduction of IDT allocation size from 4 KB to 2 KB
- x86-64?

Diffstat:

 arch/i386/Config.help                |    7 
 arch/i386/config.in                  |    1 
 arch/i386/kernel/Makefile            |    2 
 arch/i386/kernel/cpu/common.c        |    9 
 arch/i386/kernel/cpu/intel.c         |    8 
 arch/i386/kernel/entry.S             |    1 
 arch/i386/kernel/i8259.c             |    6 
 arch/i386/kernel/ldt.c               |   88 +++++++++
 arch/i386/kernel/multiring.c         |  316 +++++++++++++++++++++++++++++++++++
 arch/i386/kernel/process.c           |   52 ++++-
 arch/i386/kernel/ptrace.c            |    8 
 arch/i386/kernel/signal.c            |   24 +-
 arch/i386/kernel/traps.c             |   44 +++-
 arch/i386/mach-generic/irq_vectors.h |   24 +-
 arch/i386/mach-visws/irq_vectors.h   |   22 +-
 arch/i386/math-emu/fpu_entry.c       |    2 
 arch/i386/mm/fault.c                 |    5 
 fs/exec.c                            |    4 
 include/asm-i386/desc.h              |   48 +++++
 include/asm-i386/fixmap.h            |   14 +
 include/asm-i386/idt.h               |  236 ++++++++++++++++++++++++++
 include/asm-i386/ldt.h               |    3 
 include/asm-i386/mmu.h               |   10 +
 include/asm-i386/mmu_context.h       |    8 
 include/asm-i386/multiring.h         |  194 +++++++++++++++++++++
 include/asm-i386/processor.h         |   26 +-
 include/asm-i386/segment.h           |   12 +
 include/asm-i386/system.h            |    6 
 include/asm-i386/unistd.h            |    1 
 include/linux/sched.h                |    8 
 kernel/fork.c                        |    6 
 31 files changed, 1103 insertions(+), 92 deletions(-)

Test program (apologies for the horrible coding style):

/*
multiring-test.c: example program for Linux multiring support

Copyright (C) 2002 Luca Barbieri <[email protected]>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <errno.h>
#include "/home/ldb/src/linux-2.5.44_multiring/include/asm-i386/unistd.h" #include "/home/ldb/src/linux-2.5.44_multiring/include/asm-i386/multiring.h" #include "/home/ldb/src/linux-2.5.44_multiring/include/asm-i386/ldt.h" #include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>
#define CLONE_IDT 0x00800000

unsigned ring;
unsigned xcs;

typedef char intbuf_t[4];

intbuf_t test_entry_intbuf;
typedef int (*test_entry_int_t)(unsigned param) __attribute__((regparm(3))); #define test_entry_int ((test_entry_int_t)test_entry_intbuf)

intbuf_t kernel_syscall_intbuf;

char lower_ring_msg[] = "from lower ring! [should be XXXm...]\n";

void make_int(intbuf_t p, unsigned vec)

{
	p[0] = 0xcd;
	p[1] = vec;
	p[2] = 0xc3;
}

void test_entry();
void test_entry2();
void syscall_entry();

int kernel_syscall(unsigned num, unsigned arg1, unsigned arg2, unsigned arg3)

{
	unsigned ret;
	__asm__ __volatile__ (
	"pushl %%ebx\n\t"
	"movl %2, %%ebx\n\t"
	"call kernel_syscall_intbuf\n\t"
	"popl %%ebx" : "=a" (ret) : "0" (num), "r" (arg1), "c" (arg2), "d" (arg3));
	return ret;
}

int do_test_entry(unsigned param)

{
	printf("test_entry called with: %u\n", param);
	if(param == 99)
	{
		unsigned vec;
		int ret;		
		int testvec;
		vec = multiring_copy_free(0x80);
		printf("copy_free(0x80) ret: %i errno: %i\n", vec, errno);

		printf("alloc_all ");
		for(;;)
		{
			ret = multiring_copy_free(0x80);
			if(ret >= 0)
				printf("%x ", testvec = ret);
			else
			{
				printf("\nret: %i errno: %i [should be -1/28]\n", ret, errno);
				break;
			}
		}
		ret = multiring_free(testvec);
		printf("free testvec: %i ret: %i errno: %i\n", testvec, ret, errno);
		ret = multiring_copy_free(0x80);
		printf("copy_free ret: %i errno: %i\n", ret, errno);
		
		printf("about to multiring_set %x %x\n", (xcs << 16) | ((unsigned long)test_entry2 & 0xffff), ((unsigned long)test_entry2 & 0xffff0000) | 0x8f00 | ((ring + 1) << 13));
		multiring_set(0x80, (xcs << 16) | ((unsigned long)syscall_entry & 0xffff), ((unsigned long)syscall_entry & 0xffff0000) | 0x8f00 | ((ring + 1) << 13));
		make_int(kernel_syscall_intbuf, vec);		
	}
	return param * 2;
}

int do_syscall_entry(unsigned num, unsigned arg1, unsigned arg2, unsigned arg3)

{
	if((num == __NR_write) && (arg1 == 1))
	{
		char* ptr = (char*)arg2;
		ptr[0] = 'X';
		ptr[1] = 'X';
		ptr[2] = 'X';
		return kernel_syscall(num, 1, arg2, arg3);
	}
	else if(num == __NR_exit)
	{
		kernel_syscall(__NR_write, 1, (unsigned long)"quitting\n", strlen("quitting\n"));
		return kernel_syscall(__NR_exit, 0, 0, 0);
	}
	else
	{
		kernel_syscall(__NR_write, 1, (unsigned long)"syscall not allowed\n", strlen("syscall not allowed\n"));
		return -ENOSYS;
	}
}

asm("
test_entry:
pushl %eax
call do_test_entry
addl $4, %esp
iret

test_entry2:
cld
pushl %es
pushl %ds
pushl %eax
movw %ss, %ax
movw %ax, %ds
movw %ax, %es
call do_test_entry
addl $4, %esp
popl %ds
popl %es
iret

syscall_entry:
cld
pushl %es
pushl %ds
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movw %ss, %ax
movw %ax, %ds
movw %ax, %es

call do_syscall_entry

addl $4, %esp
popl %ebx
popl %ecx
popl %edx
popl %esi
popl %edi
popl %ebp
popl %ds
popl %es
iret

");

char* entry_stack;
char* lower_stack;
char* clone_stack;

#define ldt_sel(num, ring) (((num) << 3) | (1 << 2) | (ring))

static void interprivilege_jump(unsigned ss, unsigned esp, unsigned cs, unsigned eip) __attribute__((noreturn));

static void interprivilege_jump(unsigned ss, unsigned esp, unsigned cs, unsigned eip)

{
	__asm__ __volatile__(
	"movl %0, %%ds\n\t"
	"movl %0, %%es\n\t"	
	"pushl %0\n\t"
	"pushl %1\n\t"
	"pushfl\n\t"
	"pushl %2\n\t"
	"pushl %3\n\t"
	"iret" : : "r" (ss), "r" (esp), "r" (cs), "r" (eip));
	abort();
}

void lower_ring_start()

{
	test_entry_int(10);
#ifdef CRASH_INT
	/* int 0x80 has DPL 1, so this should crash */
	write(1, lower_ring_msg, strlen(lower_ring_msg));
#endif
	test_entry_int(99);
	write(1, lower_ring_msg, strlen(lower_ring_msg));
#ifdef CRASH_SEGV
	*(unsigned long*)0 = 0;
#endif
	_exit(0);
}

/* this should reboot the processor if we have access to kernel mode memory */ void triple_fault(void)

{
	struct
	{
		unsigned short a;
		struct
		{
			unsigned short lim;
			unsigned long addr;
		} m48;
	} desc;
	/* find the IDT address */
	__asm__ __volatile__("sidt %0" : "=m" (desc.m48));

	/* clear the IDT (or crash, if the kernel works properly) */
	memset((void*)desc.m48.addr, 0, desc.m48.lim + 1);

	/* triple fault */
	*(unsigned long*)0 = 0;
}

#define idt_gate(sel, addr, ring) (((sel) << 16) | ((unsigned long)addr & 0xffff)), (((unsigned long)addr & 0xffff0000) | 0x8f00 | ((ring) << 13)) static inline pid_t syscall_clone(unsigned long flags)

{
	pid_t pid;
	asm volatile("pushl %%ebx\n\tmovl %1, %%ebx\n\tmovl %%esp, %%ecx\n\tint $0x80\n\tpopl %%ebx" : "=a" (pid) : "r" (flags | SIGCHLD), "0" (__NR_clone) : "edx", "memory");
	return pid;
}

unsigned test_vec;

void clone_vm_child(void)

{
	int ret;
	unsigned ccs;
	unsigned css;
	unsigned cring;
	struct multiring_gate gate;	
	ret = multiring_check();
	printf("vm check ret: %i errno: %i\n", ret, errno);
	
	__asm__ __volatile__("movl %%cs, %0" : "=r" (ccs));
	__asm__ __volatile__("movl %%ss, %0" : "=r" (css));
	printf("vm cs: %x ss: %x\n", ccs, css);
	cring = xcs & 3;

	ret = multiring_set(test_vec, idt_gate(ccs, 0x33333333, cring));
	printf("vm set 33333333 ret: %i errno: %i\n", ret, errno);
	ret = multiring_get(test_vec, &gate);
	printf("vm get ret: %i errno: %i a: %x b: %x\n", ret, errno, gate.a, gate.b);
		
	_exit(0);
}

int main(int argc, char** argv)

{
	int ret;
	unsigned xss;
	unsigned test_entry_vec;
	struct multiring_gate gate;
	pid_t pid;
	struct user_desc ldt;
	ret = multiring_elevate();
	printf("elevate ret: %i errno: %i\n", ret, errno);
#ifdef ELEVATE
	return 0;
#endif
	__asm__ __volatile__("movl %%cs, %0" : "=r" (xcs));

	ret = multiring_check();
	printf("check ret: %i errno: %i\n", ret, errno);
	
	printf("cs: %x\n", xcs);
	__asm__ __volatile__("movl %%ss, %0" : "=r" (xss));
	printf("ss: %x\n", xss);
	ring = xcs & 3;
	if(ring == 3)
	{
		printf("we are still at ring 3 :( - exiting\n");
		return 0;
	}

	test_vec = multiring_set_free(idt_gate(xcs, 0x11111111, ring));
	printf("set_free 11111111 ret: %i errno: %i\n", test_vec, errno);
	ret = multiring_get(test_vec, &gate);
	printf("get ret: %i errno: %i a: %x b: %x\n", ret, errno, gate.a, gate.b);
	pid = fork();
	printf("fork pid: %i errno: %i\n", pid, errno);
	if(!pid)
	{
		//for(;;) {}
		ret = multiring_check();
		printf("fork check ret: %i errno: %i\n", ret, errno);

		__asm__ __volatile__("movl %%cs, %0" : "=r" (xcs));
		__asm__ __volatile__("movl %%ss, %0" : "=r" (xss));
		printf("fork cs: %x ss: %x\n", xcs, xss);
		ring = xcs & 3;

		ret = multiring_set(test_vec, idt_gate(xcs, 0x22222222, ring));
		printf("fork set 22222222 ret: %i errno: %i\n", ret, errno);
		ret = multiring_get(test_vec, &gate);
		printf("fork get ret: %i errno: %i a: %x b: %x\n", ret, errno, gate.a, gate.b);
		
		_exit(0);
	}
	else
	{
		int status;
		waitpid(pid, &status, 0);
		printf("status: %x\n", status);
	}
	
	ret = multiring_get(test_vec, &gate);
	printf("get ret: %i errno: %i a: %x b: %x [should be desc for 11111111]\n", ret, errno, gate.a, gate.b);

	pid = clone(clone_vm_child, (char*)malloc(65536) + 65536, CLONE_VM|SIGCHLD, 0);
	printf("CLONE_VM pid: %i errno: %i\n", pid, errno);
	{
		int status;
		waitpid(pid, &status, 0);
		printf("status: %x\n", status);
	}
	
	ret = multiring_get(test_vec, &gate);
	printf("get ret: %i errno: %i a: %x b: %x [should be desc for 33333333]\n", ret, errno, gate.a, gate.b);

	pid = syscall_clone(CLONE_IDT);
	printf("CLONE_IDT pid: %i errno: %i\n", pid, errno);
	if(!pid)
	{
		//for(;;) {}
		ret = multiring_check();
		printf("idt check ret: %i errno: %i\n", ret, errno);
	
		__asm__ __volatile__("movl %%cs, %0" : "=r" (xcs));
		__asm__ __volatile__("movl %%ss, %0" : "=r" (xss));
		printf("idt cs: %x ss: %x\n", xcs, xss);
		ring = xcs & 3;

		ret = multiring_set(test_vec, idt_gate(xcs, 0x44444444, ring));
		printf("idt set 44444444 ret: %i errno: %i\n", ret, errno);
		ret = multiring_get(test_vec, &gate);
		printf("idt get ret: %i errno: %i a: %x b: %x\n", ret, errno, gate.a, gate.b);
		
		_exit(0);
	}
	else
	{
		int status;
		waitpid(pid, &status, 0);
		printf("status: %x\n", status);
	}

	ret = multiring_get(test_vec, &gate);
	printf("get ret: %i errno: %i a: %x b: %x [should be desc for 44444444]\n", ret, errno, gate.a, gate.b);
#ifdef DO_EXEC
	execlp("ls", "ls", 0);

#endif

#ifdef EXPLOIT_GDT
	printf("about to exploit with sel = %x\n", xss);
	triple_fault();

#endif

	multiring_set_free(idt_gate(xcs, 0xeeeeeeee, 0));
	printf("multiring_set(gate_to_kernel) ret: %i errno: %i\n", ret, errno);
#ifdef EXPLOIT_IDT
	test_entry_vec = multiring_set_free(idt_gate(xcs, 0xeeeeeeee, 1));
	printf("set_free(to_karea) ret: %i errno: %i\n", test_entry_vec, errno);
#else
	test_entry_vec = multiring_set_free(idt_gate(xcs, test_entry, ring));
	printf("set_free ret: %i errno: %i\n", test_entry_vec, errno);

#endif

	make_int(test_entry_intbuf, test_entry_vec);
	ret = test_entry_int(42);
	printf("test_entry_int: %u\n", ret);

	/* we could use the initial stack, but it's more difficult */
	entry_stack = malloc(65536);

	ret = multiring_set_espss(ring, (unsigned long)(entry_stack + 65536), xss);
	printf("set_espss ret: %i errno %i esp: %x ss: %x\n", ret, errno, (unsigned long)(entry_stack + 65536), xss);
	{
		unsigned tesp, tss;
		ret = multiring_get_espss(ring, &tesp, &tss);
		printf("get_espss ret: %i errno %i esp: %x ss: %x\n", ret, errno, tesp, tss);
	}
	ret = multiring_set(test_entry_vec, idt_gate(xcs, test_entry2, ring + 1));
	printf("multiring_set(test_entry_vec) ret: %i errno: %i\n", ret, errno);

	/* addresses should not be hardcoded in real programs */
	
	ldt.entry_number = 1;
	ldt.base_addr = 0;
	ldt.limit = 0xaffff;
	ldt.seg_32bit = 1;
	ldt.contents = MODIFY_LDT_CONTENTS_DATA;
	ldt.read_exec_only = 0;
	ldt.limit_in_pages = 1;
	ldt.seg_not_present = 0;
	ldt.useable = 0;
#ifdef EXPLOIT_LDT
	ldt.dpl = ring;
	ldt.limit = 0xfffff;
	ret = modify_ldt(0x11, &ldt, sizeof(struct user_desc));
	printf("modify_ldt(exploit) ret: %i errno: %i\n", test_entry_vec, errno);

	__asm__ __volatile__("movl %0, %%ds\n\tmovl %0, %%es" : : "r" (ldt_sel(1, ring)));
	printf("about to exploit with sel = %x\n", ldt_sel(1, ring));

	triple_fault();
#endif
	ldt.dpl = ring + 1;
	ret = modify_ldt(0x11, &ldt, sizeof(struct user_desc));
	printf("modify_ldt(data) ret: %i errno: %i\n", test_entry_vec, errno);	

	ldt.entry_number = 2;
	ldt.contents = MODIFY_LDT_CONTENTS_CODE;
	modify_ldt(0x11, &ldt, sizeof(struct user_desc));
	printf("modify_ldt(code) ret: %i errno: %i\n", test_entry_vec, errno);		

	lower_stack = malloc(65536);

	interprivilege_jump(ldt_sel(1, ring + 1), (unsigned long)(lower_stack + 65536), ldt_sel(2, ring + 1), (unsigned long)lower_ring_start);
	return 0;
}

Patch:

arch/i386/Config.help +7 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/Config.help linux-2.5.44_multiring/arch/i386/Config.help
@@ -165,6 +165,13 @@ CONFIG_HIGHPTE
   low memory.  Setting this option will put user-space page table
   entries in high memory.
 
+CONFIG_HIGHIDT
+  The kernel uses 4KB for each multiring process (e.g. User Mode Linux).
+  Say Y to allocate those 4KB in high memory. This is only useful if you
+  plan to run hundreds of thousands of multiring processes.
+
+  If unsure, say N.
+
 CONFIG_HIGHMEM4G
   Select this if you have a 32-bit processor and between 1 and 4
   gigabytes of physical RAM.
arch/i386/config.in +1 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/config.in linux-2.5.44_multiring/arch/i386/config.in
@@ -236,6 +236,7 @@ fi
 
 if [ "$CONFIG_HIGHMEM4G" = "y" -o "$CONFIG_HIGHMEM64G" = "y" ]; then
    bool 'Allocate 3rd-level pagetables from highmem' CONFIG_HIGHPTE
+   bool 'Allocate multiring IDTs from highmem (EXPERIMENTAL)' CONFIG_X86_HIGHIDT
 fi
 
 bool 'Math emulation' CONFIG_MATH_EMULATION
arch/i386/kernel/cpu/common.c +9 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/cpu/common.c linux-2.5.44_multiring/arch/i386/kernel/cpu/common.c
@@ -243,6 +243,8 @@ void __init generic_identify(struct cpui
 	}
 }
 
+extern void load_idt_table_init(unsigned cpu, pgprot_t prot);
+
 /*
  * This does the hard work of actually picking apart the CPU stuff...
  */
@@ -257,6 +259,7 @@ void __init identify_cpu(struct cpuinfo_
 	c->x86_model = c->x86_mask = 0;	/* So far unknown... */
 	c->x86_vendor_id[0] = '\0'; /* Unset */
 	c->x86_model_id[0] = '\0';  /* Unset */
+	c->f00f_bug = 0;
 	memset(&c->x86_capability, 0, sizeof c->x86_capability);
 
 	if (!have_cpuid_p()) {
@@ -348,6 +351,9 @@ void __init identify_cpu(struct cpuinfo_
 		/* AND the already accumulated flags with these */
 		for ( i = 0 ; i < NCAPINTS ; i++ )
 			boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
+
+		if(c->f00f_bug)
+			boot_cpu_data.f00f_bug = 1;
 	}
 
 	printk(KERN_DEBUG "CPU:             Common caps: %08lx %08lx %08lx %08lx\n",
@@ -355,6 +361,9 @@ void __init identify_cpu(struct cpuinfo_
 	       boot_cpu_data.x86_capability[1],
 	       boot_cpu_data.x86_capability[2],
 	       boot_cpu_data.x86_capability[3]);
+
+	/* load the IDT */
+	load_idt_table_init((c == &boot_cpu_data) ? 0 : (c - cpu_data), boot_cpu_data.f00f_bug ? PAGE_KERNEL_RO : PAGE_KERNEL);	
 }
 /*
  *	Perform early boot up checks for a valid TSC. See arch/i386/kernel/time.c
arch/i386/kernel/cpu/intel.c +3 -5
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/cpu/intel.c linux-2.5.44_multiring/arch/i386/kernel/cpu/intel.c
@@ -169,15 +169,13 @@ static void __init init_intel(struct cpu
 	 * have the F0 0F bug, which lets nonpriviledged users lock up the system.
 	 * Note that the workaround only should be initialized once...
 	 */
-	c->f00f_bug = 0;
 	if ( c->x86 == 5 ) {
-		static int f00f_workaround_enabled = 0;
+		static int f00f_workaround_message = 0;
 
 		c->f00f_bug = 1;
-		if ( !f00f_workaround_enabled ) {
-			trap_init_f00f_bug();
+		if ( !f00f_workaround_message ) {
 			printk(KERN_NOTICE "Intel Pentium with F0 0F bug - workaround enabled.\n");
-			f00f_workaround_enabled = 1;
+			f00f_workaround_message = 1;
 		}
 	}
 #endif
arch/i386/kernel/entry.S +1 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/entry.S linux-2.5.44_multiring/arch/i386/kernel/entry.S
@@ -737,6 +737,7 @@ ENTRY(sys_call_table)
 	.long sys_free_hugepages
 	.long sys_exit_group
 	.long sys_lookup_dcookie
+	.long sys_multiring
 
 	.rept NR_syscalls-(.-sys_call_table)/4
 		.long sys_ni_syscall
arch/i386/kernel/i8259.c +4 -2
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/i8259.c linux-2.5.44_multiring/arch/i386/kernel/i8259.c
@@ -282,7 +282,8 @@ void init_8259A(int auto_eoi)
 	 * outb_p - this has to work on a wide range of PC hardware.
 	 */
 	outb_p(0x11, 0x20);	/* ICW1: select 8259A-1 init */
-	outb_p(0x20 + 0, 0x21);	/* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
+	/* ICW2: 8259A-1 IR0-7 mapped to FIRST_EXTERNAL_VECTOR-FIRST_EXTERNAL_VECTOR+7 */
+	outb_p(FIRST_EXTERNAL_VECTOR + 0, 0x21);
 	outb_p(0x04, 0x21);	/* 8259A-1 (the master) has a slave on IR2 */
 	if (auto_eoi)
 		outb_p(0x03, 0x21);	/* master does Auto EOI */
@@ -290,7 +291,8 @@ void init_8259A(int auto_eoi)
 		outb_p(0x01, 0x21);	/* master expects normal EOI */
 
 	outb_p(0x11, 0xA0);	/* ICW1: select 8259A-2 init */
-	outb_p(0x20 + 8, 0xA1);	/* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
+	/* ICW2: 8259A-2 IR0-7 mapped to FIRST_EXTERNAL_VECTOR+8-FIRST_EXTERNAL_VECTOR+f */
+	outb_p(FIRST_EXTERNAL_VECTOR + 8, 0xA1);
 	outb_p(0x02, 0xA1);	/* 8259A-2 is a slave on master's IR2 */
 	outb_p(0x01, 0xA1);	/* (slave's support for AEOI in flat mode
 				    is to be investigated) */
arch/i386/kernel/ldt.c +83 -5

136 lines not on this page · raw message

arch/i386/kernel/Makefile +1 -1
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/Makefile linux-2.5.44_multiring/arch/i386/kernel/Makefile
@@ -9,7 +9,7 @@ export-objs     := mca.o i386_ksyms.o ti
 obj-y	:= process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
 		ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
 		pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \
-		bootflag.o
+		bootflag.o multiring.o
 
 obj-y				+= cpu/
 obj-y				+= timers/
arch/i386/kernel/multiring.c +316 -0

316 lines not on this page · raw message

arch/i386/kernel/process.c +38 -14

145 lines not on this page · raw message

arch/i386/kernel/ptrace.c +4 -4
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/ptrace.c linux-2.5.44_multiring/arch/i386/kernel/ptrace.c
@@ -76,24 +76,24 @@ static int putreg(struct task_struct *ch
 {
 	switch (regno >> 2) {
 		case FS:
-			if (value && (value & 3) != 3)
+			if (value && !(value & 3))
 				return -EIO;
 			child->thread.fs = value;
 			return 0;
 		case GS:
-			if (value && (value & 3) != 3)
+			if (value && !(value & 3))
 				return -EIO;
 			child->thread.gs = value;
 			return 0;
 		case DS:
 		case ES:
-			if (value && (value & 3) != 3)
+			if (value && !(value & 3))
 				return -EIO;
 			value &= 0xffff;
 			break;
 		case SS:
 		case CS:
-			if ((value & 3) != 3)
+			if (!(value & 3))
 				return -EIO;
 			value &= 0xffff;
 			break;
arch/i386/kernel/signal.c +13 -11
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/kernel/signal.c linux-2.5.44_multiring/arch/i386/kernel/signal.c
@@ -162,7 +162,7 @@ restore_sigcontext(struct pt_regs *regs,
 #define COPY_SEG_STRICT(seg)						\
 	{ unsigned short tmp;						\
 	  err |= __get_user(tmp, &sc->seg);				\
-	  regs->x##seg = tmp|3; }
+	  regs->x##seg = (tmp & 3) ? tmp : (tmp | USER_RING); }
 
 #define GET_SEG(seg)							\
 	{ unsigned short tmp;						\
@@ -338,7 +338,7 @@ get_sigframe(struct k_sigaction *ka, str
 	}
 
 	/* This is the legacy signal stack switching. */
-	else if ((regs->xss & 0xffff) != __USER_DS &&
+	else if ((regs->xss & 0xfffc) != USER_DS_RPL(0) &&
 		 !(ka->sa.sa_flags & SA_RESTORER) &&
 		 ka->sa.sa_restorer) {
 		esp = (unsigned long) ka->sa.sa_restorer;
@@ -350,6 +350,7 @@ get_sigframe(struct k_sigaction *ka, str
 static void setup_frame(int sig, struct k_sigaction *ka,
 			sigset_t *set, struct pt_regs * regs)
 {
+	unsigned ring;
 	struct sigframe *frame;
 	int err = 0;
 
@@ -398,10 +399,10 @@ static void setup_frame(int sig, struct 
 	regs->eip = (unsigned long) ka->sa.sa_handler;
 
 	set_fs(USER_DS);
-	regs->xds = __USER_DS;
-	regs->xes = __USER_DS;
-	regs->xss = __USER_DS;
-	regs->xcs = __USER_CS;
+	ring = get_user_ring();
+	regs->xds = regs->xes = regs->xss = USER_DS_RPL(ring);
+	regs->xcs = USER_CS_RPL(ring);
+	put_user_ring();
 	regs->eflags &= ~TF_MASK;
 
 #if DEBUG_SIG
@@ -422,6 +423,7 @@ static void setup_rt_frame(int sig, stru
 {
 	struct rt_sigframe *frame;
 	int err = 0;
+	unsigned ring;
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
 
@@ -473,10 +475,10 @@ static void setup_rt_frame(int sig, stru
 	regs->eip = (unsigned long) ka->sa.sa_handler;
 
 	set_fs(USER_DS);
-	regs->xds = __USER_DS;
-	regs->xes = __USER_DS;
-	regs->xss = __USER_DS;
-	regs->xcs = __USER_CS;
+	ring = get_user_ring();
+	regs->xds = regs->xes = regs->xss = USER_DS_RPL(ring);
+	regs->xcs = USER_CS_RPL(ring);
+	put_user_ring();
 	regs->eflags &= ~TF_MASK;
 
 #if DEBUG_SIG
@@ -556,7 +558,7 @@ int do_signal(struct pt_regs *regs, sigs
 	 * kernel mode. Just return without doing anything
 	 * if so.
 	 */
-	if ((regs->xcs & 3) != 3)
+	if (!(regs->xcs & 3))
 		return 1;
 
 	if (current->flags & PF_FREEZE) {
arch/i386/kernel/traps.c +30 -14

68 lines not on this page · raw message

arch/i386/mach-generic/irq_vectors.h +15 -9
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/mach-generic/irq_vectors.h linux-2.5.44_multiring/arch/i386/mach-generic/irq_vectors.h
@@ -22,16 +22,19 @@
 #ifndef _ASM_IRQ_VECTORS_H
 #define _ASM_IRQ_VECTORS_H
 
+#define MULTIRING_SPECIAL_FIRST_VECTOR	0x20
+#define MULTIRING_SPECIAL_LAST_VECTOR	0x2f
+
 /*
  * IDT vectors usable for external interrupt sources start
- * at 0x20:
+ * at 0x30:
  */
-#define FIRST_EXTERNAL_VECTOR	0x20
+#define FIRST_EXTERNAL_VECTOR	0x30
 
 #define SYSCALL_VECTOR		0x80
 
 /*
- * Vectors 0x20-0x2f are used for ISA interrupts.
+ * Vectors 0x30-0x3f are used for ISA interrupts.
  */
 
 /*
@@ -49,6 +52,9 @@
 #define RESCHEDULE_VECTOR	0xfc
 #define CALL_FUNCTION_VECTOR	0xfb
 
+#define MULTIRING_AUTO_LAST_VECTOR	0xfa
+#define MULTIRING_AUTO_FIRST_VECTOR	0xf1
+
 #define THERMAL_APIC_VECTOR	0xf0
 /*
  * Local APIC timer IRQ vector is on a different priority level,
@@ -58,26 +64,26 @@
 #define LOCAL_TIMER_VECTOR	0xef
 
 /*
- * First APIC vector available to drivers: (vectors 0x30-0xee)
- * we start at 0x31 to spread out vectors evenly between priority
+ * First APIC vector available to drivers: (vectors 0x41-0xee)
+ * we start at 0x41 to spread out vectors evenly between priority
  * levels. (0x80 is the syscall vector)
  */
-#define FIRST_DEVICE_VECTOR	0x31
+#define FIRST_DEVICE_VECTOR	0x41
 #define FIRST_SYSTEM_VECTOR	0xef
 
 #define TIMER_IRQ 0
 
 /*
- * 16 8259A IRQ's, 208 potential APIC interrupt sources.
+ * 16 8259A IRQ's, 192 potential APIC interrupt sources.
  * Right now the APIC is mostly only used for SMP.
  * 256 vectors is an architectural limit. (we can have
  * more than 256 devices theoretically, but they will
  * have to use shared interrupts)
  * Since vectors 0x00-0x1f are used/reserved for the CPU,
- * the usable vector space is 0x20-0xff (224 vectors)
+ * the usable vector space is 0x30-0xff (208 vectors)
  */
 #ifdef CONFIG_X86_IO_APIC
-#define NR_IRQS 224
+#define NR_IRQS 208
 #else
 #define NR_IRQS 16
 #endif
arch/i386/mach-visws/irq_vectors.h +14 -8
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/mach-visws/irq_vectors.h linux-2.5.44_multiring/arch/i386/mach-visws/irq_vectors.h
@@ -1,11 +1,14 @@
 #ifndef _ASM_IRQ_VECTORS_H
 #define _ASM_IRQ_VECTORS_H
 
+#define MULTIRING_SPECIAL_FIRST_VECTOR	0x20
+#define MULTIRING_SPECIAL_LAST_VECTOR	0x2f
+
 /*
  * IDT vectors usable for external interrupt sources start
- * at 0x20:
+ * at 0x30:
  */
-#define FIRST_EXTERNAL_VECTOR	0x20
+#define FIRST_EXTERNAL_VECTOR	0x30
 
 #define SYSCALL_VECTOR		0x80
 
@@ -28,6 +31,9 @@
 #define RESCHEDULE_VECTOR	0xfc
 #define CALL_FUNCTION_VECTOR	0xfb
 
+#define MULTIRING_AUTO_LAST_VECTOR	0xfa
+#define MULTIRING_AUTO_FIRST_VECTOR	0xf1
+
 #define THERMAL_APIC_VECTOR	0xf0
 /*
  * Local APIC timer IRQ vector is on a different priority level,
@@ -37,26 +43,26 @@
 #define LOCAL_TIMER_VECTOR	0xef
 
 /*
- * First APIC vector available to drivers: (vectors 0x30-0xee)
- * we start at 0x31 to spread out vectors evenly between priority
+ * First APIC vector available to drivers: (vectors 0x41-0xee)
+ * we start at 0x41 to spread out vectors evenly between priority
  * levels. (0x80 is the syscall vector)
  */
-#define FIRST_DEVICE_VECTOR	0x31
+#define FIRST_DEVICE_VECTOR	0x41
 #define FIRST_SYSTEM_VECTOR	0xef
 
 #define TIMER_IRQ 0
 
 /*
- * 16 8259A IRQ's, 208 potential APIC interrupt sources.
+ * 16 8259A IRQ's, 192 potential APIC interrupt sources.
  * Right now the APIC is mostly only used for SMP.
  * 256 vectors is an architectural limit. (we can have
  * more than 256 devices theoretically, but they will
  * have to use shared interrupts)
  * Since vectors 0x00-0x1f are used/reserved for the CPU,
- * the usable vector space is 0x20-0xff (224 vectors)
+ * the usable vector space is 0x30-0xff (208 vectors)
  */
 #ifdef CONFIG_X86_IO_APIC
-#define NR_IRQS 224
+#define NR_IRQS 208
 #else
 #define NR_IRQS 16
 #endif
arch/i386/math-emu/fpu_entry.c +1 -1
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/math-emu/fpu_entry.c linux-2.5.44_multiring/arch/i386/math-emu/fpu_entry.c
@@ -171,7 +171,7 @@ asmlinkage void math_emulate(long arg)
       FPU_EIP += code_base = FPU_CS << 4;
       code_limit = code_base + 0xffff;  /* Assumes code_base <= 0xffff0000 */
     }
-  else if ( FPU_CS == __USER_CS && FPU_DS == __USER_DS )
+  else if ( (FPU_CS | 3) == USER_CS_RPL(3) && (FPU_DS | 3) == USER_DS_RPL(3) )
     {
       addr_modes.default_mode = 0;
     }
arch/i386/mm/fault.c +5 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/arch/i386/mm/fault.c linux-2.5.44_multiring/arch/i386/mm/fault.c
@@ -157,6 +157,10 @@ asmlinkage void do_page_fault(struct pt_
 
 	tsk = current;
 
+	/* User code at DPL 1 and 2 will also cause the U/S bit to be unset */
+	if(current->thread.multiring_mode && (regs->xcs & 3))
+		error_code |= 4;
+	
 	/*
 	 * We fault-in kernel-space virtual memory on-demand. The
 	 * 'reference' page table is init_mm.pgd.
@@ -270,6 +274,7 @@ bad_area:
 	up_read(&mm->mmap_sem);
 
 	/* User mode accesses just cause a SIGSEGV */
+	/* Note: F0 0F C7 C8 in DPL 1 or 2 will cause the if body to be executed (seems unavoidable) */
 	if (error_code & 4) {
 		tsk->thread.cr2 = address;
 		tsk->thread.error_code = error_code;
fs/exec.c +4 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/fs/exec.c linux-2.5.44_multiring/fs/exec.c
@@ -1021,7 +1021,11 @@ int do_execve(char * filename, char ** a
 	if (!bprm.mm)
 		goto out_file;
 
+#ifdef init_new_context_flags
+	retval = init_new_context_flags(current, bprm.mm, CLONE_EXEC);
+#else
 	retval = init_new_context(current, bprm.mm);
+#endif
 	if (retval < 0)
 		goto out_mm;
 
include/asm-i386/desc.h +46 -2

77 lines not on this page · raw message

include/asm-i386/fixmap.h +12 -2
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/fixmap.h linux-2.5.44_multiring/include/asm-i386/fixmap.h
@@ -48,6 +48,10 @@
  * future, say framebuffers for the console driver(s) could be
  * fix-mapped?
  */
+
+extern int __fix_idt_begin_should_have_been_optimized_away(void);
+extern int __fix_idt_end_should_have_been_optimized_away(void);
+
 enum fixed_addresses {
 #ifdef CONFIG_X86_LOCAL_APIC
 	FIX_APIC_BASE,	/* local (CPU) APIC) -- required for SMP or not */
@@ -62,9 +66,15 @@ enum fixed_addresses {
 	FIX_LI_PCIA,	/* Lithium PCI Bridge A */
 	FIX_LI_PCIB,	/* Lithium PCI Bridge B */
 #endif
-#ifdef CONFIG_X86_F00F_BUG
-	FIX_F00F_IDT,	/* Virtual mapping for IDT */
+
+#if defined(CONFIG_X86_F00F_BUG) || defined(CONFIG_X86_HIGHIDT)
+	FIX_IDT_BEGIN, /* Virtual mapping for IDT */
+	FIX_IDT_END = FIX_IDT_BEGIN + NR_CPUS - 1,
+#else
+#define FIX_IDT_BEGIN __fix_idt_begin_should_have_been_optimized_away()
+#define FIX_IDT_END __fix_idt_end_should_have_been_optimized_away()	
 #endif
+
 #ifdef CONFIG_X86_CYCLONE
 	FIX_CYCLONE_TIMER, /*cyclone timer register*/
 #endif 
include/asm-i386/idt.h +236 -0

236 lines not on this page · raw message

include/asm-i386/ldt.h +3 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/ldt.h linux-2.5.44_multiring/include/asm-i386/ldt.h
@@ -22,6 +22,9 @@ struct user_desc {
 	unsigned int  limit_in_pages:1;
 	unsigned int  seg_not_present:1;
 	unsigned int  useable:1;
+
+	/* has effect only in multiring mode, but is returned in any mode */
+	unsigned int  dpl:2; 
 };
 
 #define MODIFY_LDT_CONTENTS_DATA	0
include/asm-i386/mmu_context.h +7 -1
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/mmu_context.h linux-2.5.44_multiring/include/asm-i386/mmu_context.h
@@ -6,12 +6,15 @@
 #include <asm/atomic.h>
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
+#include <asm/idt.h>
 
 /*
  * possibly do the LDT unload here?
  */
 #define destroy_context(mm)		do { } while(0)
-int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
+int init_new_context_flags(struct task_struct *tsk, struct mm_struct *mm, unsigned flags);
+#define init_new_context_flags init_new_context_flags
+#define init_new_context(tsk, mm) init_new_context_flags(tsk, mm, 0)
 
 #ifdef CONFIG_SMP
 
@@ -45,6 +48,9 @@ static inline void switch_mm(struct mm_s
 		 */
 		if (unlikely(prev->context.ldt != next->context.ldt))
 			load_LDT_nolock(&next->context, cpu);
+
+		if (unlikely(prev->context.idt.opaque != next->context.idt.opaque))
+			load_IDT_nolock_inline(tsk, &next->context, cpu);
 	}
 #ifdef CONFIG_SMP
 	else {
include/asm-i386/mmu.h +10 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/mmu.h linux-2.5.44_multiring/include/asm-i386/mmu.h
@@ -7,10 +7,20 @@
  *
  * cpu_vm_mask is used to optimize ldt flushing.
  */
+
+union idt
+{
+	struct desc_struct* addr;
+	struct page* page;
+	unsigned long opaque;
+};
+
 typedef struct { 
 	int size;
 	struct semaphore sem;
 	void *ldt;
+	union idt idt;
+	unsigned char bad_segments;
 } mm_context_t;
 
 #endif
include/asm-i386/multiring.h +194 -0

194 lines not on this page · raw message

include/asm-i386/processor.h +16 -10
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/processor.h linux-2.5.44_multiring/include/asm-i386/processor.h
@@ -332,14 +332,17 @@ typedef struct {
 	unsigned long seg;
 } mm_segment_t;
 
+struct espss
+{
+	unsigned long esp;
+	unsigned long ss;
+};
+
 struct tss_struct {
 	unsigned short	back_link,__blh;
 	unsigned long	esp0;
 	unsigned short	ss0,__ss0h;
-	unsigned long	esp1;
-	unsigned short	ss1,__ss1h;
-	unsigned long	esp2;
-	unsigned short	ss2,__ss2h;
+	struct espss espss12[2];
 	unsigned long	__cr3;
 	unsigned long	eip;
 	unsigned long	eflags;
@@ -367,10 +370,12 @@ struct thread_struct {
 /* cached TLS descriptors. */
 	struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
 	unsigned long	esp0;
+	struct espss espss12[2];
 	unsigned long	eip;
 	unsigned long	esp;
 	unsigned long	fs;
 	unsigned long	gs;
+	unsigned char	multiring_mode;	
 /* Hardware debugging registers */
 	unsigned long	debugreg[8];  /* %%db0-7 debug registers */
 /* fault info */
@@ -388,7 +393,8 @@ struct thread_struct {
 #define INIT_THREAD  {						\
 	{ { 0, 0 } , },						\
 	0,							\
-	0, 0, 0, 0, 						\
+	{ {0, 0}, {0, 0} },					\
+	0, 0, 0, 0, 0, 						\
 	{ [0 ... 7] = 0 },	/* debugging registers */	\
 	0, 0, 0,						\
 	{ { 0, }, },		/* 387 state */			\
@@ -400,7 +406,7 @@ struct thread_struct {
 	0,0, /* back_link, __blh */				\
 	sizeof(init_stack) + (long) &init_stack, /* esp0 */	\
 	__KERNEL_DS, 0, /* ss0 */				\
-	0,0,0,0,0,0, /* stack1, stack2 */			\
+	{ {0, 0}, {0, 0} }, /* stack1, stack2 */			\
 	0, /* cr3 */						\
 	0,0, /* eip,eflags */					\
 	0,0,0,0, /* eax,ecx,edx,ebx */				\
@@ -415,10 +421,10 @@ struct thread_struct {
 #define start_thread(regs, new_eip, new_esp) do {		\
 	__asm__("movl %0,%%fs ; movl %0,%%gs": :"r" (0));	\
 	set_fs(USER_DS);					\
-	regs->xds = __USER_DS;					\
-	regs->xes = __USER_DS;					\
-	regs->xss = __USER_DS;					\
-	regs->xcs = __USER_CS;					\
+	regs->xds = USER_DS_RPL(USER_RING);					\
+	regs->xes = USER_DS_RPL(USER_RING);					\
+	regs->xss = USER_DS_RPL(USER_RING);					\
+	regs->xcs = USER_CS_RPL(USER_RING);					\
 	regs->eip = new_eip;					\
 	regs->esp = new_esp;					\
 } while (0)
include/asm-i386/segment.h +10 -2
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/segment.h linux-2.5.44_multiring/include/asm-i386/segment.h
@@ -1,6 +1,12 @@
 #ifndef _ASM_SEGMENT_H
 #define _ASM_SEGMENT_H
 
+/* The default user privilege level */
+#define USER_RING 3
+
+/* The multiring most privileged user level */
+#define MULTIRING_USER_RING 1
+
 /*
  * The layout of the per-CPU GDT under Linux:
  *
@@ -43,10 +49,10 @@
 #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES * 8)
 
 #define GDT_ENTRY_DEFAULT_USER_CS	4
-#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)
+#define USER_CS_RPL(ring) (GDT_ENTRY_DEFAULT_USER_CS * 8 + (ring))
 
 #define GDT_ENTRY_DEFAULT_USER_DS	5
-#define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)
+#define USER_DS_RPL(ring) (GDT_ENTRY_DEFAULT_USER_DS * 8 + (ring))
 
 #define GDT_ENTRY_KERNEL_BASE	12
 
@@ -76,4 +82,6 @@
  */
 #define IDT_ENTRIES 256
 
+#define IDT_SIZE (IDT_ENTRIES * 8)
+
 #endif
include/asm-i386/system.h +6 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/system.h linux-2.5.44_multiring/include/asm-i386/system.h
@@ -95,6 +95,12 @@ static inline unsigned long _get_base(ch
 		: :"m" (*(unsigned int *)&(value)))
 
 /*
+ * Save a segment.
+ */
+#define savesegment(seg,value) \
+	asm volatile("movl %%" #seg ",%0":"=m" (*(int *)&(value)))
+
+/*
  * Clear and set 'TS' bit respectively
  */
 #define clts() __asm__ __volatile__ ("clts")
include/asm-i386/unistd.h +1 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/asm-i386/unistd.h linux-2.5.44_multiring/include/asm-i386/unistd.h
@@ -258,6 +258,7 @@
 #define __NR_free_hugepages	251
 #define __NR_exit_group		252
 #define __NR_lookup_dcookie	253
+#define __NR_multiring		254
   
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
include/linux/sched.h +8 -0
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/include/linux/sched.h linux-2.5.44_multiring/include/linux/sched.h
@@ -51,6 +51,14 @@ struct exec_domain;
 #define CLONE_SETTID	0x00100000	/* write the TID back to userspace */
 #define CLONE_CLEARTID	0x00200000	/* clear the userspace TID */
 #define CLONE_DETACHED	0x00400000	/* parent wants no child-exit signal */
+#ifdef __i386__
+#define CLONE_IDT	0x00800000	/* set if IDT is shared between processes (note: CLONE_VM implies CLONE_IDT) */
+#define CLONE_CLEAR_IDT	0x01000000	/* set to clear the IDT */
+#define CLONE_EXEC CLONE_CLEAR_IDT
+#else
+#define CLONE_EXEC 0
+#endif
+
 
 /*
  * List of flags we want to share for kernel threads,
kernel/fork.c +5 -1
diff --exclude-from=/home/ldb/src/linux-exclude -urNdp linux-2.5.44/kernel/fork.c linux-2.5.44_multiring/kernel/fork.c
@@ -435,7 +435,11 @@ static int copy_mm(unsigned long clone_f
 	if (!mm_init(mm))
 		goto fail_nomem;
 
-	if (init_new_context(tsk,mm))
+#ifdef init_new_context_flags
+	if (init_new_context_flags(tsk, mm, clone_flags))
+#else
+	if (init_new_context(tsk, mm))
+#endif		
 		goto free_pt;
 
 	down_write(&oldmm->mmap_sem);
#1 lore raw
Quoted 10 lines · Luca Barbieri

Short explaination:
This patch implements a feature called "x86 multiring", which is a shorthand for x86 multiple user-mode privilege rings support. It allows user-mode programs to create DPL 1 and 2 segments and get a modifiable per-process copy of IDT.

User Mode Linux can use these features to implement a syscall mechanism identical to the one used by the kernel-mode kernel, and thus much faster than the current one, with free memory protection and with zero context switches.

But there are privilege switches.

Quoted 4 lines

Wine could also use it to achieve fast syscall-level emulation of Windows NT (and, to a lesser extent, Windows 3.1 and 9x).

Obviously there is some risk of the patch creating security holes.

Let me get the gist of the idea.
To accelerate UML, and wine type applications:
1) setup segments with restricted limits, so their children cannot write into their supervisor process even though they share a mm.
2) load a special system call table that switches processor modes when any system call is activated.

Unless I am mistaken all of the above can be accomplished without using the cpus multiple rings of privilege. Which would allow nesting only limited by the address space reduction of each task.

Eric

#2 lore raw
Quoted 1 line

But there are privilege switches.

Of course, they are unavoidable. However, they are as fast as the one needed to make kernel syscalls.

Quoted 10 lines

Let me get the gist of the idea.
To accelerate UML, and wine type applications:
1) setup segments with restricted limits, so their children cannot write into their supervisor process even though they share a mm.
2) load a special system call table that switches processor modes when any system call is activated.

Unless I am mistaken all of the above can be accomplished without using the cpus multiple rings of privilege. Which would allow nesting only limited by the address space reduction of each task.

You also need:
3) Prevent less privileged subtasks from loading segments belonging to more privileged ones

This can be done in hardware using the x86 privilege rings, at the cost of limitations on the number of subtasks and the inability to have protected pairs of subtasks where none is more privileged than the other.

Of course it is also possible to do this in the kernel, or in a privileged user-mode task using LDT/TLS system calls, by modifying descriptor tables on interprivilege jumps but this is obviously significantly slower.

Anyway hardware-based and kernel-based privilege separation can perfectly coexist.

#3 lore raw
In reply to Luca Barbieri
Quoted 6 lines · Luca Barbieri

Short explaination:
This patch implements a feature called "x86 multiring", which is a shorthand for x86 multiple user-mode privilege rings support. It allows user-mode programs to create DPL 1 and 2 segments and get a modifiable per-process copy of IDT.

Quoted 7 lines

User Mode Linux can use these features to implement a syscall mechanism identical to the one used by the kernel-mode kernel, and thus much faster than the current one, with free memory protection and with zero context switches.

Wine could also use it to achieve fast syscall-level emulation of Windows NT (and, to a lesser extent, Windows 3.1 and 9x).

Karim once talked about doing a flavor of Adeos that would drop a running kernel into ring 1 as a result of insmodding an Adeos module, which would allow Adeos to combine an unmodified Linux kernel with a realtime executive.

Signature · 1 line
Daniel
#4 lore raw
Quoted 3 lines · Daniel Phillips

Karim once talked about doing a flavor of Adeos that would drop a running kernel into ring 1 as a result of insmodding an Adeos module, which would allow Adeos to combine an unmodified Linux kernel with a realtime executive.

Yes. The initial Adeos design (http://www.opersys.com/adeos/) spelled out the details for shoving Linux out of ring 0 and into ring 1 without modifying it. It would still have access to its page tables, but it wouldn't be allowed to use some key instructions (including cli/sti). In that scenario, the nanokernel would be the only thing running at ring 0, everything else would run in ring 1 and above. This includes all non-Linux OSes (see the Adeos paper for complete details).

Though this is fine, it is very hardware dependent. Last I checked, for example, few archs have 4-level rings. If we're assuming all archs are going to act/look like x86, it may be worth the effort, but I'm not sure this is a safe bet. (Which doesn't mean some people can't find this useful, there's been at least one debugger that follows this method:
http://marc.theaimsgroup.com/?l=linux-kernel&m=102675847422778&w=2)

Instead, it's more interesting to run each OS copy in its own separate physical address space in priviliged mode over Adeos. This implies a few assumptions, but "in Linux we trust" (i.e. it's not doing any random physical accesses, and if it is, then it needs to be fixed). The other OSes, such as emulated WinXYZ, can also have their own physically separate address space and run in unpriviliged mode (ring 1 or worse; depending on your willingnes to implement appropriate handlers for the faults generated by the OS not running in its intended ring 0). Have a look at the "Practical SMP clusters document" at the URL above for a discussion of a relatively simple method to get multiple copies of Linux running side-by-side each in their own separate physical address space and all linked through Adeos.

Karim

===================================================
Karim Yaghmour
[email protected]
Embedded and Real-Time Linux Expert
===================================================