On Sun, 2006-07-30 at 19:05 +0200, Andi Kleen wrote: > > (1) We can make startup_32 work for every known and future reasonable > > hypervisor as well as native, by testing if ring isn't 0 and paging is > > enabled and jumping to the paravirt entry path. > > Somehow the right Hypervisor still needs to be discovered though > so that the right paravirt ops can be installed. > > We would need a standard interface for this. Yes, that's %ebx here (0 == Xen): we call paravirts[%ebx]->init(). Of course if you do full virtualization and then later want to insert paravirt_ops, you can just use the normal boot path (Zach has indicated that VMWare will do this in the short to medium term anyway). FYI, here's the actual patch. Thanks! Rusty. First cut (compiles, untested) of generic startup_paravirt entry point. 1) Each hypervisor type creates a paravirt_ops struct and puts an agreed-on entry in the paravirts[] array. Strictly, this need only have the init function populated. 2) The hypervisor type is handed through %ebx to the startup_paravirt function at boot. Currently 0 = Xen 3.0, 1 = VMI. 3) The init function (called with all regs except for %ebx and %esp intact), with first arg pointing to the paravirt_ops structure we're using. This is responsible for overwriting the paravirt_ops: a helper called initialize_ops_struct is provided. Signed-off-by: Rusty Russell <rusty at rustcorp.com.au> =================================================================== --- a/arch/i386/kernel/asm-offsets.c +++ b/arch/i386/kernel/asm-offsets.c @@ -81,5 +81,6 @@ void foo(void) OFFSET(PARAVIRT_irq_enable_sysexit, paravirt_ops, irq_enable_sysexit); OFFSET(PARAVIRT_iret, paravirt_ops, iret); OFFSET(PARAVIRT_read_cr0, paravirt_ops, read_cr0); + OFFSET(PARAVIRT_init_offset, paravirt_ops, init); #endif } =================================================================== --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -54,6 +54,12 @@ * can. */ ENTRY(startup_32) + +#ifdef CONFIG_PARAVIRT + movl %cs, %eax + testl $0x3, %eax + jnz startup_paravirt +#endif /* * Set segments to known values. @@ -411,6 +417,17 @@ ignore_int: #endif iret +#ifdef CONFIG_PARAVIRT +ENTRY(startup_paravirt) + cld + movl $(init_thread_union+THREAD_SIZE),%esp + + /* ebx contains index into paravirt to copy. hand to init as a ptr. */ + movl paravirts(,%ebx,4), %ebx + call *PARAVIRT_init_offset(%ebx) +1: jmp 1b +#endif + /* * Real beginning of normal "text" segment */ =================================================================== --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c @@ -388,6 +388,25 @@ static unsigned nopara_patch(unsigned in return insn_len; } + +/* Array of all the paravirtualization ops available. See head.S. */ +struct paravirt_ops *paravirts[] = { +#ifdef CONFIG_VMI + [1] = &vmi_paravirt_ops, +#endif +}; + +/* Overwrite ops struct with non-NULL ops entries from this struct. */ +void initialize_ops_struct(const struct paravirt_ops *ops) +{ + unsigned int i; + void **src = (void **)ops, **dst = (void **)¶virt_ops; + + for (i = 0; i < offsetof(struct paravirt_ops, kernel_rpl) / 4; i++) { + if (src[i]) + dst[i] = src[i]; + } +} struct paravirt_ops paravirt_ops = { .kernel_rpl = 0, =================================================================== --- a/include/asm-i386/paravirt.h +++ b/include/asm-i386/paravirt.h @@ -5,6 +5,7 @@ #include <linux/config.h> #include <linux/linkage.h> #include <linux/stringify.h> +#include <asm/linkage.h> #ifndef CONFIG_PARAVIRT #include <asm/no_paravirt.h> @@ -22,8 +23,7 @@ struct Xgt_desc_struct; struct Xgt_desc_struct; struct paravirt_ops { - unsigned int kernel_rpl; - + fastcall void (*init)(struct paravirt_ops *me); unsigned (*patch)(unsigned int type, void *firstinsn, unsigned len); /* All the function pointers here are declared as "fastcall" @@ -87,9 +87,15 @@ struct paravirt_ops /* These two are jmp to, not actually called. */ void (fastcall *irq_enable_sysexit)(void); void (fastcall *iret)(void); + + /* Here and below are not copied by initialize_ops_struct */ + unsigned int kernel_rpl; }; extern struct paravirt_ops paravirt_ops; + +/* Overwrite ops struct with non-NULL ops entries from this struct. */ +void initialize_ops_struct(const struct paravirt_ops *ops); /* The paravirtualized CPUID instruction. */ static inline void __cpuid(unsigned int *eax, unsigned int *ebx, -- Help! Save Australia from the worst of the DMCA: http://linux.org.au/law