diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 1040f9b2f997813ec21e881d5117e7a2983e1845..3a06b51c98ad7aac428aba56b2b933aaf1a8ee86 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -87,6 +87,7 @@ struct lguest_data lguest_data = { .noirq_start = (u32)lguest_noirq_start, .noirq_end = (u32)lguest_noirq_end, .blocked_interrupts = { 1 }, /* Block timer interrupts */ + .syscall_vec = SYSCALL_VECTOR, }; static cycle_t clock_base; diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 02556bae9e9f30a2f4fafbb77be5a787489ed78c..41b26e592d3843a5ef34a8668817ec27e75f1154 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -281,37 +281,47 @@ static int __init init(void) /* First we put the Switcher up in very high virtual memory. */ err = map_switcher(); if (err) - return err; + goto out; /* Now we set up the pagetable implementation for the Guests. */ err = init_pagetables(switcher_page, SHARED_SWITCHER_PAGES); - if (err) { - unmap_switcher(); - return err; - } + if (err) + goto unmap; /* The I/O subsystem needs some things initialized. */ lguest_io_init(); + /* We might need to reserve an interrupt vector. */ + err = init_interrupts(); + if (err) + goto free_pgtables; + /* /dev/lguest needs to be registered. */ err = lguest_device_init(); - if (err) { - free_pagetables(); - unmap_switcher(); - return err; - } + if (err) + goto free_interrupts; /* Finally we do some architecture-specific setup. */ lguest_arch_host_init(); /* All good! */ return 0; + +free_interrupts: + free_interrupts(); +free_pgtables: + free_pagetables(); +unmap: + unmap_switcher(); +out: + return err; } /* Cleaning up is just the same code, backwards. With a little French. */ static void __exit fini(void) { lguest_device_remove(); + free_interrupts(); free_pagetables(); unmap_switcher(); diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index fdefc0afc38c5d6853fb7eaa3312aa107d38a815..a57d757eab6e16caae0b4cccbe152a43b00f1245 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -12,8 +12,14 @@ * them first, so we also have a way of "reflecting" them into the Guest as if * they had been delivered to it directly. :*/ #include +#include +#include #include "lg.h" +/* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */ +static unsigned int syscall_vector = SYSCALL_VECTOR; +module_param(syscall_vector, uint, 0444); + /* The address of the interrupt handler is split into two bits: */ static unsigned long idt_address(u32 lo, u32 hi) { @@ -183,6 +189,47 @@ void maybe_do_interrupt(struct lguest *lg) * timer interrupt. */ write_timestamp(lg); } +/*:*/ + +/* Linux uses trap 128 for system calls. Plan9 uses 64, and Ron Minnich sent + * me a patch, so we support that too. It'd be a big step for lguest if half + * the Plan 9 user base were to start using it. + * + * Actually now I think of it, it's possible that Ron *is* half the Plan 9 + * userbase. Oh well. */ +static bool could_be_syscall(unsigned int num) +{ + /* Normal Linux SYSCALL_VECTOR or reserved vector? */ + return num == SYSCALL_VECTOR || num == syscall_vector; +} + +/* The syscall vector it wants must be unused by Host. */ +bool check_syscall_vector(struct lguest *lg) +{ + u32 vector; + + if (get_user(vector, &lg->lguest_data->syscall_vec)) + return false; + + return could_be_syscall(vector); +} + +int init_interrupts(void) +{ + /* If they want some strange system call vector, reserve it now */ + if (syscall_vector != SYSCALL_VECTOR + && test_and_set_bit(syscall_vector, used_vectors)) { + printk("lg: couldn't reserve syscall %u\n", syscall_vector); + return -EBUSY; + } + return 0; +} + +void free_interrupts(void) +{ + if (syscall_vector != SYSCALL_VECTOR) + clear_bit(syscall_vector, used_vectors); +} /*H:220 Now we've got the routines to deliver interrupts, delivering traps * like page fault is easy. The only trick is that Intel decided that some @@ -224,7 +271,7 @@ static int direct_trap(unsigned int num) { /* Hardware interrupts don't go to the Guest at all (except system * call). */ - if (num >= FIRST_EXTERNAL_VECTOR && num != SYSCALL_VECTOR) + if (num >= FIRST_EXTERNAL_VECTOR && !could_be_syscall(num)) return 0; /* The Host needs to see page faults (for shadow paging and to save the diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h index f921684dbe5cc7205aa70a96af9d403fc8acece7..7408cebe995e60422b017c19050978ed964eefd3 100644 --- a/drivers/lguest/lg.h +++ b/drivers/lguest/lg.h @@ -141,6 +141,9 @@ void copy_traps(const struct lguest *lg, struct desc_struct *idt, const unsigned long *def); void guest_set_clockevent(struct lguest *lg, unsigned long delta); void init_clockdev(struct lguest *lg); +bool check_syscall_vector(struct lguest *lg); +int init_interrupts(void); +void free_interrupts(void); /* segments.c: */ void setup_default_gdt_entries(struct lguest_ro_state *state); diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 84c09082f27fb65bfac64d0ef09eef9aac16af8e..a125109446dcfec80ccbaede0ce0886bebf967d4 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -530,6 +530,10 @@ int lguest_arch_init_hypercalls(struct lguest *lg) if (put_user(tsc_speed, &lg->lguest_data->tsc_khz)) return -EFAULT; + /* The interrupt code might not like the system call vector. */ + if (!check_syscall_vector(lg)) + kill_guest(lg, "bad syscall vector"); + return 0; } /* Now we've examined the hypercall code; our Guest can make requests. There diff --git a/include/linux/lguest.h b/include/linux/lguest.h index 9ddac2f0a97ce6292042f50fe55d3fa55e086d15..083052236db976abd3d03785a2dfabc433daf2e1 100644 --- a/include/linux/lguest.h +++ b/include/linux/lguest.h @@ -48,6 +48,9 @@ struct lguest_data /* Fields initialized by the Guest at boot: */ /* Instruction range to suppress interrupts even if enabled */ unsigned long noirq_start, noirq_end; + + /* The vector to try to use for system calls (0x40 or 0x80). */ + unsigned int syscall_vec; }; extern struct lguest_data lguest_data; #endif /* __ASSEMBLY__ */