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 + -
显示快捷键?