diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-21 21:03:35 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2007-10-23 01:49:53 -0400 |
commit | c18acd73ffc209def08003a1927473096f66c5ad (patch) | |
tree | dd8e292ac8ca90b061b7e37ad6947231ced566e3 | |
parent | ee3db0f2b6053b65f3b70253f5f810d9a3d67b28 (diff) |
Allow guest to specify syscall vector to use.
(Based on Ron Minnich's LGUEST_PLAN9_SYSCALL patch).
This patch allows Guests to specify what system call vector they want,
and we try to reserve it. We only allow one non-Linux system call
vector, to try to avoid DoS on the Host.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r-- | arch/x86/lguest/boot.c | 1 | ||||
-rw-r--r-- | drivers/lguest/core.c | 30 | ||||
-rw-r--r-- | drivers/lguest/interrupts_and_traps.c | 49 | ||||
-rw-r--r-- | drivers/lguest/lg.h | 3 | ||||
-rw-r--r-- | drivers/lguest/x86/core.c | 4 | ||||
-rw-r--r-- | include/linux/lguest.h | 3 |
6 files changed, 79 insertions, 11 deletions
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 1040f9b2f997..3a06b51c98ad 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
@@ -87,6 +87,7 @@ struct lguest_data lguest_data = { | |||
87 | .noirq_start = (u32)lguest_noirq_start, | 87 | .noirq_start = (u32)lguest_noirq_start, |
88 | .noirq_end = (u32)lguest_noirq_end, | 88 | .noirq_end = (u32)lguest_noirq_end, |
89 | .blocked_interrupts = { 1 }, /* Block timer interrupts */ | 89 | .blocked_interrupts = { 1 }, /* Block timer interrupts */ |
90 | .syscall_vec = SYSCALL_VECTOR, | ||
90 | }; | 91 | }; |
91 | static cycle_t clock_base; | 92 | static cycle_t clock_base; |
92 | 93 | ||
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 02556bae9e9f..41b26e592d38 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c | |||
@@ -281,37 +281,47 @@ static int __init init(void) | |||
281 | /* First we put the Switcher up in very high virtual memory. */ | 281 | /* First we put the Switcher up in very high virtual memory. */ |
282 | err = map_switcher(); | 282 | err = map_switcher(); |
283 | if (err) | 283 | if (err) |
284 | return err; | 284 | goto out; |
285 | 285 | ||
286 | /* Now we set up the pagetable implementation for the Guests. */ | 286 | /* Now we set up the pagetable implementation for the Guests. */ |
287 | err = init_pagetables(switcher_page, SHARED_SWITCHER_PAGES); | 287 | err = init_pagetables(switcher_page, SHARED_SWITCHER_PAGES); |
288 | if (err) { | 288 | if (err) |
289 | unmap_switcher(); | 289 | goto unmap; |
290 | return err; | ||
291 | } | ||
292 | 290 | ||
293 | /* The I/O subsystem needs some things initialized. */ | 291 | /* The I/O subsystem needs some things initialized. */ |
294 | lguest_io_init(); | 292 | lguest_io_init(); |
295 | 293 | ||
294 | /* We might need to reserve an interrupt vector. */ | ||
295 | err = init_interrupts(); | ||
296 | if (err) | ||
297 | goto free_pgtables; | ||
298 | |||
296 | /* /dev/lguest needs to be registered. */ | 299 | /* /dev/lguest needs to be registered. */ |
297 | err = lguest_device_init(); | 300 | err = lguest_device_init(); |
298 | if (err) { | 301 | if (err) |
299 | free_pagetables(); | 302 | goto free_interrupts; |
300 | unmap_switcher(); | ||
301 | return err; | ||
302 | } | ||
303 | 303 | ||
304 | /* Finally we do some architecture-specific setup. */ | 304 | /* Finally we do some architecture-specific setup. */ |
305 | lguest_arch_host_init(); | 305 | lguest_arch_host_init(); |
306 | 306 | ||
307 | /* All good! */ | 307 | /* All good! */ |
308 | return 0; | 308 | return 0; |
309 | |||
310 | free_interrupts: | ||
311 | free_interrupts(); | ||
312 | free_pgtables: | ||
313 | free_pagetables(); | ||
314 | unmap: | ||
315 | unmap_switcher(); | ||
316 | out: | ||
317 | return err; | ||
309 | } | 318 | } |
310 | 319 | ||
311 | /* Cleaning up is just the same code, backwards. With a little French. */ | 320 | /* Cleaning up is just the same code, backwards. With a little French. */ |
312 | static void __exit fini(void) | 321 | static void __exit fini(void) |
313 | { | 322 | { |
314 | lguest_device_remove(); | 323 | lguest_device_remove(); |
324 | free_interrupts(); | ||
315 | free_pagetables(); | 325 | free_pagetables(); |
316 | unmap_switcher(); | 326 | unmap_switcher(); |
317 | 327 | ||
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index fdefc0afc38c..a57d757eab6e 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c | |||
@@ -12,8 +12,14 @@ | |||
12 | * them first, so we also have a way of "reflecting" them into the Guest as if | 12 | * them first, so we also have a way of "reflecting" them into the Guest as if |
13 | * they had been delivered to it directly. :*/ | 13 | * they had been delivered to it directly. :*/ |
14 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/module.h> | ||
15 | #include "lg.h" | 17 | #include "lg.h" |
16 | 18 | ||
19 | /* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */ | ||
20 | static unsigned int syscall_vector = SYSCALL_VECTOR; | ||
21 | module_param(syscall_vector, uint, 0444); | ||
22 | |||
17 | /* The address of the interrupt handler is split into two bits: */ | 23 | /* The address of the interrupt handler is split into two bits: */ |
18 | static unsigned long idt_address(u32 lo, u32 hi) | 24 | static unsigned long idt_address(u32 lo, u32 hi) |
19 | { | 25 | { |
@@ -183,6 +189,47 @@ void maybe_do_interrupt(struct lguest *lg) | |||
183 | * timer interrupt. */ | 189 | * timer interrupt. */ |
184 | write_timestamp(lg); | 190 | write_timestamp(lg); |
185 | } | 191 | } |
192 | /*:*/ | ||
193 | |||
194 | /* Linux uses trap 128 for system calls. Plan9 uses 64, and Ron Minnich sent | ||
195 | * me a patch, so we support that too. It'd be a big step for lguest if half | ||
196 | * the Plan 9 user base were to start using it. | ||
197 | * | ||
198 | * Actually now I think of it, it's possible that Ron *is* half the Plan 9 | ||
199 | * userbase. Oh well. */ | ||
200 | static bool could_be_syscall(unsigned int num) | ||
201 | { | ||
202 | /* Normal Linux SYSCALL_VECTOR or reserved vector? */ | ||
203 | return num == SYSCALL_VECTOR || num == syscall_vector; | ||
204 | } | ||
205 | |||
206 | /* The syscall vector it wants must be unused by Host. */ | ||
207 | bool check_syscall_vector(struct lguest *lg) | ||
208 | { | ||
209 | u32 vector; | ||
210 | |||
211 | if (get_user(vector, &lg->lguest_data->syscall_vec)) | ||
212 | return false; | ||
213 | |||
214 | return could_be_syscall(vector); | ||
215 | } | ||
216 | |||
217 | int init_interrupts(void) | ||
218 | { | ||
219 | /* If they want some strange system call vector, reserve it now */ | ||
220 | if (syscall_vector != SYSCALL_VECTOR | ||
221 | && test_and_set_bit(syscall_vector, used_vectors)) { | ||
222 | printk("lg: couldn't reserve syscall %u\n", syscall_vector); | ||
223 | return -EBUSY; | ||
224 | } | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | void free_interrupts(void) | ||
229 | { | ||
230 | if (syscall_vector != SYSCALL_VECTOR) | ||
231 | clear_bit(syscall_vector, used_vectors); | ||
232 | } | ||
186 | 233 | ||
187 | /*H:220 Now we've got the routines to deliver interrupts, delivering traps | 234 | /*H:220 Now we've got the routines to deliver interrupts, delivering traps |
188 | * like page fault is easy. The only trick is that Intel decided that some | 235 | * 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) | |||
224 | { | 271 | { |
225 | /* Hardware interrupts don't go to the Guest at all (except system | 272 | /* Hardware interrupts don't go to the Guest at all (except system |
226 | * call). */ | 273 | * call). */ |
227 | if (num >= FIRST_EXTERNAL_VECTOR && num != SYSCALL_VECTOR) | 274 | if (num >= FIRST_EXTERNAL_VECTOR && !could_be_syscall(num)) |
228 | return 0; | 275 | return 0; |
229 | 276 | ||
230 | /* The Host needs to see page faults (for shadow paging and to save the | 277 | /* 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 f921684dbe5c..7408cebe995e 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, | |||
141 | const unsigned long *def); | 141 | const unsigned long *def); |
142 | void guest_set_clockevent(struct lguest *lg, unsigned long delta); | 142 | void guest_set_clockevent(struct lguest *lg, unsigned long delta); |
143 | void init_clockdev(struct lguest *lg); | 143 | void init_clockdev(struct lguest *lg); |
144 | bool check_syscall_vector(struct lguest *lg); | ||
145 | int init_interrupts(void); | ||
146 | void free_interrupts(void); | ||
144 | 147 | ||
145 | /* segments.c: */ | 148 | /* segments.c: */ |
146 | void setup_default_gdt_entries(struct lguest_ro_state *state); | 149 | 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 84c09082f27f..a125109446dc 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) | |||
530 | if (put_user(tsc_speed, &lg->lguest_data->tsc_khz)) | 530 | if (put_user(tsc_speed, &lg->lguest_data->tsc_khz)) |
531 | return -EFAULT; | 531 | return -EFAULT; |
532 | 532 | ||
533 | /* The interrupt code might not like the system call vector. */ | ||
534 | if (!check_syscall_vector(lg)) | ||
535 | kill_guest(lg, "bad syscall vector"); | ||
536 | |||
533 | return 0; | 537 | return 0; |
534 | } | 538 | } |
535 | /* Now we've examined the hypercall code; our Guest can make requests. There | 539 | /* 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 9ddac2f0a97c..083052236db9 100644 --- a/include/linux/lguest.h +++ b/include/linux/lguest.h | |||
@@ -48,6 +48,9 @@ struct lguest_data | |||
48 | /* Fields initialized by the Guest at boot: */ | 48 | /* Fields initialized by the Guest at boot: */ |
49 | /* Instruction range to suppress interrupts even if enabled */ | 49 | /* Instruction range to suppress interrupts even if enabled */ |
50 | unsigned long noirq_start, noirq_end; | 50 | unsigned long noirq_start, noirq_end; |
51 | |||
52 | /* The vector to try to use for system calls (0x40 or 0x80). */ | ||
53 | unsigned int syscall_vec; | ||
51 | }; | 54 | }; |
52 | extern struct lguest_data lguest_data; | 55 | extern struct lguest_data lguest_data; |
53 | #endif /* __ASSEMBLY__ */ | 56 | #endif /* __ASSEMBLY__ */ |