interrupts_and_traps.c

来自「linux 内核源代码」· C语言 代码 · 共 509 行 · 第 1/2 页

C
509
字号
	 * bogus one in): if we fail here, the Guest will be killed. */	if (!idt_present(lg->arch.idt[num].a, lg->arch.idt[num].b))		return 0;	set_guest_interrupt(lg, lg->arch.idt[num].a, lg->arch.idt[num].b,			    has_err(num));	return 1;}/*H:250 Here's the hard part: returning to the Host every time a trap happens * and then calling deliver_trap() and re-entering the Guest is slow. * Particularly because Guest userspace system calls are traps (usually trap * 128). * * So we'd like to set up the IDT to tell the CPU to deliver traps directly * into the Guest.  This is possible, but the complexities cause the size of * this file to double!  However, 150 lines of code is worth writing for taking * system calls down from 1750ns to 270ns.  Plus, if lguest didn't do it, all * the other hypervisors would beat it up at lunchtime. * * This routine indicates if a particular trap number could be delivered * directly. */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 && !could_be_syscall(num))		return 0;	/* The Host needs to see page faults (for shadow paging and to save the	 * fault address), general protection faults (in/out emulation) and	 * device not available (TS handling), and of course, the hypercall	 * trap. */	return num != 14 && num != 13 && num != 7 && num != LGUEST_TRAP_ENTRY;}/*:*//*M:005 The Guest has the ability to turn its interrupt gates into trap gates, * if it is careful.  The Host will let trap gates can go directly to the * Guest, but the Guest needs the interrupts atomically disabled for an * interrupt gate.  It can do this by pointing the trap gate at instructions * within noirq_start and noirq_end, where it can safely disable interrupts. *//*M:006 The Guests do not use the sysenter (fast system call) instruction, * because it's hardcoded to enter privilege level 0 and so can't go direct. * It's about twice as fast as the older "int 0x80" system call, so it might * still be worthwhile to handle it in the Switcher and lcall down to the * Guest.  The sysenter semantics are hairy tho: search for that keyword in * entry.S :*//*H:260 When we make traps go directly into the Guest, we need to make sure * the kernel stack is valid (ie. mapped in the page tables).  Otherwise, the * CPU trying to deliver the trap will fault while trying to push the interrupt * words on the stack: this is called a double fault, and it forces us to kill * the Guest. * * Which is deeply unfair, because (literally!) it wasn't the Guests' fault. */void pin_stack_pages(struct lguest *lg){	unsigned int i;	/* Depending on the CONFIG_4KSTACKS option, the Guest can have one or	 * two pages of stack space. */	for (i = 0; i < lg->stack_pages; i++)		/* The stack grows *upwards*, so the address we're given is the		 * start of the page after the kernel stack.  Subtract one to		 * get back onto the first stack page, and keep subtracting to		 * get to the rest of the stack pages. */		pin_page(lg, lg->esp1 - 1 - i * PAGE_SIZE);}/* Direct traps also mean that we need to know whenever the Guest wants to use * a different kernel stack, so we can change the IDT entries to use that * stack.  The IDT entries expect a virtual address, so unlike most addresses * the Guest gives us, the "esp" (stack pointer) value here is virtual, not * physical. * * In Linux each process has its own kernel stack, so this happens a lot: we * change stacks on each context switch. */void guest_set_stack(struct lguest *lg, u32 seg, u32 esp, unsigned int pages){	/* You are not allowed have a stack segment with privilege level 0: bad	 * Guest! */	if ((seg & 0x3) != GUEST_PL)		kill_guest(lg, "bad stack segment %i", seg);	/* We only expect one or two stack pages. */	if (pages > 2)		kill_guest(lg, "bad stack pages %u", pages);	/* Save where the stack is, and how many pages */	lg->ss1 = seg;	lg->esp1 = esp;	lg->stack_pages = pages;	/* Make sure the new stack pages are mapped */	pin_stack_pages(lg);}/* All this reference to mapping stacks leads us neatly into the other complex * part of the Host: page table handling. *//*H:235 This is the routine which actually checks the Guest's IDT entry and * transfers it into the entry in "struct lguest": */static void set_trap(struct lguest *lg, struct desc_struct *trap,		     unsigned int num, u32 lo, u32 hi){	u8 type = idt_type(lo, hi);	/* We zero-out a not-present entry */	if (!idt_present(lo, hi)) {		trap->a = trap->b = 0;		return;	}	/* We only support interrupt and trap gates. */	if (type != 0xE && type != 0xF)		kill_guest(lg, "bad IDT type %i", type);	/* We only copy the handler address, present bit, privilege level and	 * type.  The privilege level controls where the trap can be triggered	 * manually with an "int" instruction.  This is usually GUEST_PL,	 * except for system calls which userspace can use. */	trap->a = ((__KERNEL_CS|GUEST_PL)<<16) | (lo&0x0000FFFF);	trap->b = (hi&0xFFFFEF00);}/*H:230 While we're here, dealing with delivering traps and interrupts to the * Guest, we might as well complete the picture: how the Guest tells us where * it wants them to go.  This would be simple, except making traps fast * requires some tricks. * * We saw the Guest setting Interrupt Descriptor Table (IDT) entries with the * LHCALL_LOAD_IDT_ENTRY hypercall before: that comes here. */void load_guest_idt_entry(struct lguest *lg, unsigned int num, u32 lo, u32 hi){	/* Guest never handles: NMI, doublefault, spurious interrupt or	 * hypercall.  We ignore when it tries to set them. */	if (num == 2 || num == 8 || num == 15 || num == LGUEST_TRAP_ENTRY)		return;	/* Mark the IDT as changed: next time the Guest runs we'll know we have	 * to copy this again. */	lg->changed |= CHANGED_IDT;	/* Check that the Guest doesn't try to step outside the bounds. */	if (num >= ARRAY_SIZE(lg->arch.idt))		kill_guest(lg, "Setting idt entry %u", num);	else		set_trap(lg, &lg->arch.idt[num], num, lo, hi);}/* The default entry for each interrupt points into the Switcher routines which * simply return to the Host.  The run_guest() loop will then call * deliver_trap() to bounce it back into the Guest. */static void default_idt_entry(struct desc_struct *idt,			      int trap,			      const unsigned long handler){	/* A present interrupt gate. */	u32 flags = 0x8e00;	/* Set the privilege level on the entry for the hypercall: this allows	 * the Guest to use the "int" instruction to trigger it. */	if (trap == LGUEST_TRAP_ENTRY)		flags |= (GUEST_PL << 13);	/* Now pack it into the IDT entry in its weird format. */	idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF);	idt->b = (handler&0xFFFF0000) | flags;}/* When the Guest first starts, we put default entries into the IDT. */void setup_default_idt_entries(struct lguest_ro_state *state,			       const unsigned long *def){	unsigned int i;	for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++)		default_idt_entry(&state->guest_idt[i], i, def[i]);}/*H:240 We don't use the IDT entries in the "struct lguest" directly, instead * we copy them into the IDT which we've set up for Guests on this CPU, just * before we run the Guest.  This routine does that copy. */void copy_traps(const struct lguest *lg, struct desc_struct *idt,		const unsigned long *def){	unsigned int i;	/* We can simply copy the direct traps, otherwise we use the default	 * ones in the Switcher: they will return to the Host. */	for (i = 0; i < ARRAY_SIZE(lg->arch.idt); i++) {		/* If no Guest can ever override this trap, leave it alone. */		if (!direct_trap(i))			continue;		/* Only trap gates (type 15) can go direct to the Guest.		 * Interrupt gates (type 14) disable interrupts as they are		 * entered, which we never let the Guest do.  Not present		 * entries (type 0x0) also can't go direct, of course. */		if (idt_type(lg->arch.idt[i].a, lg->arch.idt[i].b) == 0xF)			idt[i] = lg->arch.idt[i];		else			/* Reset it to the default. */			default_idt_entry(&idt[i], i, def[i]);	}}/*H:200 * The Guest Clock. * * There are two sources of virtual interrupts.  We saw one in lguest_user.c: * the Launcher sending interrupts for virtual devices.  The other is the Guest * timer interrupt. * * The Guest uses the LHCALL_SET_CLOCKEVENT hypercall to tell us how long to * the next timer interrupt (in nanoseconds).  We use the high-resolution timer * infrastructure to set a callback at that time. * * 0 means "turn off the clock". */void guest_set_clockevent(struct lguest *lg, unsigned long delta){	ktime_t expires;	if (unlikely(delta == 0)) {		/* Clock event device is shutting down. */		hrtimer_cancel(&lg->hrt);		return;	}	/* We use wallclock time here, so the Guest might not be running for	 * all the time between now and the timer interrupt it asked for.  This	 * is almost always the right thing to do. */	expires = ktime_add_ns(ktime_get_real(), delta);	hrtimer_start(&lg->hrt, expires, HRTIMER_MODE_ABS);}/* This is the function called when the Guest's timer expires. */static enum hrtimer_restart clockdev_fn(struct hrtimer *timer){	struct lguest *lg = container_of(timer, struct lguest, hrt);	/* Remember the first interrupt is the timer interrupt. */	set_bit(0, lg->irqs_pending);	/* If the Guest is actually stopped, we need to wake it up. */	if (lg->halted)		wake_up_process(lg->tsk);	return HRTIMER_NORESTART;}/* This sets up the timer for this Guest. */void init_clockdev(struct lguest *lg){	hrtimer_init(&lg->hrt, CLOCK_REALTIME, HRTIMER_MODE_ABS);	lg->hrt.function = clockdev_fn;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?