Skip to contentSkip to conversation

paravirt_ops x86_64 , take 2

PATCH New Glauber de Oliveira Costa 1 participant 1 message Jan 12, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 paravirt_ops x86_64 , take 2

Hello all,

Here's a new version of the paravirt_ops x86_64 patch. With this message, I'm sending an incremental patch. The complete patches can be found , from now on, at http://et.redhat.com/~gcosta/paravirt_ops/

The main aim of this new update, is to fix a critical bug, namely, Rusty's name. However, I took the opportunity to write some new less important pieces of code, highlighting:

* proper casts in places in which macros were replaced by functions, and the arguments happened to mismatch types.
* calling paravirt_ops functions from .S files (I lacked this last time)
* addition of the startup_paravirt function, to kick off guests (not tested)
* fixed problems with patching
* added a new field, vsyscall_page in the paravirt_ops struct, which allows the kernel to map a vsyscall_page on its own
* fixed vsyscall functions to avoid calling paravirt_ops functions. __vsyscall_0 is the page to be mapped for the host. (set and get cpu not yet tested.)
* fixed cpuid calls.
* added substitute for the swapgs instruction. (Notice that I'm not saying it works ;-) )

In my TODO list, you can find:
* putting swapgs to work
* making sure legacy mode binaries work
* merging in valuable commentaries from all you ;-)

Signature · 3 lines
Glauber de Oliveira Costa
Red Hat Inc.
"Free as in Freedom"
arch/i386/kernel/alternative.c +0 -2
diff -urp linux-2.6.19-paravirt0/arch/i386/kernel/alternative.c linux-2.6.19-paravirt1/arch/i386/kernel/alternative.c
@@ -431,9 +431,7 @@ void __init alternative_instructions(voi
 	}
 #endif
 #ifdef CONFIG_PARAVIRT
-  #ifndef CONFIG_X86_64 /* Not working properly yet */
  	apply_paravirt(__start_parainstructions, __stop_parainstructions);
-  #endif
 #endif
 	local_irq_restore(flags);
 }
arch/x86_64/ia32/syscall32.c +1 -1
diff -urp linux-2.6.19-paravirt0/arch/x86_64/ia32/syscall32.c linux-2.6.19-paravirt1/arch/x86_64/ia32/syscall32.c
@@ -104,5 +104,5 @@ void syscall32_cpu_init(void)
 	checking_wrmsrl(MSR_IA32_SYSENTER_ESP, 0ULL);
 	checking_wrmsrl(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target);
 
-	wrmsrl(MSR_CSTAR, ia32_cstar_target);
+	wrmsrl(MSR_CSTAR, (u64)ia32_cstar_target);
 }
arch/x86_64/kernel/asm-offsets.c +3 -2
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/asm-offsets.c linux-2.6.19-paravirt1/arch/x86_64/kernel/asm-offsets.c
@@ -79,9 +79,10 @@ int main(void)
 	ENTRY(paravirt_enabled);
 	ENTRY(irq_disable);
 	ENTRY(irq_enable);
-	ENTRY(irq_enable_sysexit);
+	ENTRY(sysret);
 	ENTRY(iret);
-	ENTRY(read_cr0);
+	ENTRY(read_cr2);
+	ENTRY(swapgs);
 #endif
 
 	return 0;
arch/x86_64/kernel/entry.S +52 -43

275 lines not on this page · raw message

arch/x86_64/kernel/head64.c +1 -1
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/head64.c linux-2.6.19-paravirt1/arch/x86_64/kernel/head64.c
@@ -62,7 +62,7 @@ void __init x86_64_start_kernel(char * r
 
 	for (i = 0; i < IDT_ENTRIES; i++)
 		set_intr_gate(i, early_idt_handler);
-	asm volatile("lidt %0" :: "m" (idt_descr));
+	load_idt((const struct desc_struct *)&idt_descr);
 
 	early_printk("Kernel alive\n");
 
arch/x86_64/kernel/head.S +59 -2

87 lines not on this page · raw message

arch/x86_64/kernel/paravirt.c +28 -9
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/paravirt.c linux-2.6.19-paravirt1/arch/x86_64/kernel/paravirt.c
@@ -1,6 +1,6 @@
 /*  Paravirtualization interfaces
     Copyright (C) 2007 Glauber de Oliveira Costa, Red Hat Inc.
-    Based on i386 work by Rusty Russel.
+    Based on i386 work by Rusty Russell.
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -59,11 +59,14 @@ void memory_setup(void)
 	asm("start_" #name ": " code "; end_" #name ":")
 DEF_NATIVE(cli, "cli");
 DEF_NATIVE(sti, "sti");
-DEF_NATIVE(popfq, "pushq %rax; popfq");
+/* We push rdi , and pop in rda. This is due to x86_64 calling conventions
+ * Recall that we are patching a function call */
+DEF_NATIVE(popfq, "pushq %rdi; popfq");
 DEF_NATIVE(pushfq, "pushfq; popq %rax");
 DEF_NATIVE(pushfq_cli, "pushfq; popq %rax; cli");
-DEF_NATIVE(iret, "iret");
-DEF_NATIVE(sti_sysretq, "sti; sysretq");
+DEF_NATIVE(iret, "iretq");
+DEF_NATIVE(sysretq, "sysretq");
+DEF_NATIVE(swapgs, "swapgs");
 
 static const struct native_insns
 {
@@ -75,7 +78,8 @@ static const struct native_insns
 	[PARAVIRT_SAVE_FLAGS] = { start_pushfq, end_pushfq },
 	[PARAVIRT_SAVE_FLAGS_IRQ_DISABLE] = { start_pushfq_cli, end_pushfq_cli },
 	[PARAVIRT_INTERRUPT_RETURN] = { start_iret, end_iret },
-	[PARAVIRT_STI_SYSRETQ] = { start_sti_sysretq, end_sti_sysretq },
+	[PARAVIRT_SYSRETQ] = { start_sysretq, end_sysretq },
+	[PARAVIRT_SWAPGS] = { start_swapgs, end_swapgs },
 };
 
 static unsigned native_patch(u8 type, u16 clobbers, void *insns, unsigned len)
@@ -88,7 +92,6 @@ static unsigned native_patch(u8 type, u1
 
 	insn_len = native_insns[type].end - native_insns[type].start;
 
-
 	/* Similarly if we can't fit replacement. */
 	if (len < insn_len)
 		return len;
@@ -243,7 +246,7 @@ static void native_wbinvd(void)
 	asm volatile("wbinvd": : :"memory");
 }
 
-static unsigned long native_read_msr(unsigned int msr, int *err)
+static u64 native_read_msr(unsigned int msr, int *err)
 {
 	unsigned long val;
 
@@ -287,6 +290,13 @@ static u64 native_read_tsc(void)
 	return val;
 }
 
+static u64 native_read_tscp(int *aux)
+{
+	u64 val;
+	asm volatile ("rdtscp" : "=A" (val), "=c" (aux));
+	return val;
+}
+
 static u64 native_read_pmc(void)
 {
 	unsigned long val;
@@ -463,7 +473,8 @@ void native_pmd_clear(pmd_t *pmd)
 
 /* These are in entry.S */
 extern void native_iret(void);
-extern void native_irq_enable_sysexit(void);
+extern void native_sysret(void);
+extern void native_swapgs(void);
 
 static int __init print_banner(void)
 {
@@ -475,12 +486,18 @@ core_initcall(print_banner);
 /* We simply declare start_kernel to be the paravirt probe of last resort. */
 paravirt_probe(start_kernel);
 
+extern unsigned long __vsyscall_0;
 struct paravirt_ops paravirt_ops = {
 	.name = "bare hardware",
 	.paravirt_enabled = 0,
 	.kernel_rpl = 0,
 	.pgd_alignment = sizeof(pgd_t) * PTRS_PER_PGD,
 
+	.swapgs = {
+		.ret = 0,
+		.fn = native_swapgs,
+	 },
+	.vsyscall_page = &__vsyscall_0,
  	.patch = native_patch,
 	.banner = default_banner,
 	.arch_setup = native_nop,
@@ -512,6 +529,7 @@ struct paravirt_ops paravirt_ops = {
 	.read_msr = native_read_msr,
 	.write_msr = native_write_msr,
 	.read_tsc = native_read_tsc,
+	.read_tscp = native_read_tscp,
 	.read_pmc = native_read_pmc,
 	.load_tr_desc = native_load_tr_desc,
 	.set_ldt = native_set_ldt,
@@ -571,7 +589,7 @@ struct paravirt_ops paravirt_ops = {
 	.make_pud = native_make_pud,
 	.make_pgd = native_make_pgd,
 
-	.irq_enable_sysexit = native_irq_enable_sysexit,
+	.sysret = native_sysret,
 	.iret = native_iret,
 
 	.dup_mmap = (void *)native_nop,
@@ -580,4 +598,5 @@ struct paravirt_ops paravirt_ops = {
 
 	.startup_ipi_hook = (void *)native_nop,
 };
+
 EXPORT_SYMBOL(paravirt_ops);
arch/x86_64/kernel/setup64.c +4 -4
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/setup64.c linux-2.6.19-paravirt1/arch/x86_64/kernel/setup64.c
@@ -123,7 +123,7 @@ void pda_init(int cpu)
 	asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0)); 
 	/* Memory clobbers used to order PDA accessed */
 	mb();
-	wrmsrl(MSR_GS_BASE, pda);
+	wrmsrl(MSR_GS_BASE, (u64)pda);
 	mb();
 
 	pda->cpunumber = cpu; 
@@ -160,7 +160,7 @@ void syscall_init(void)
 	 * but only a 32bit target. LSTAR sets the 64bit rip. 	 
 	 */ 
 	wrmsrl(MSR_STAR,  ((u64)__USER32_CS)<<48  | ((u64)__KERNEL_CS)<<32); 
-	wrmsrl(MSR_LSTAR, system_call); 
+	wrmsrl(MSR_LSTAR, (u64)system_call); 
 
 #ifdef CONFIG_IA32_EMULATION   		
 	syscall32_cpu_init ();
@@ -223,8 +223,8 @@ void __cpuinit cpu_init (void)
  		memcpy(cpu_gdt(cpu), cpu_gdt_table, GDT_SIZE);
 
 	cpu_gdt_descr[cpu].size = GDT_SIZE;
-	asm volatile("lgdt %0" :: "m" (cpu_gdt_descr[cpu]));
-	asm volatile("lidt %0" :: "m" (idt_descr));
+	load_gdt((const struct desc_struct *)&cpu_gdt_descr[cpu]);
+	load_idt((const struct desc_struct *)&idt_descr);
 
 	memset(me->thread.tls_array, 0, GDT_ENTRY_TLS_ENTRIES * 8);
 	syscall_init();
arch/x86_64/kernel/setup.c +6 -6
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/setup.c linux-2.6.19-paravirt1/arch/x86_64/kernel/setup.c
@@ -341,6 +341,12 @@ static void discover_ebda(void)
 		ebda_size = 64*1024;
 }
 
+/* Overridden in paravirt.c if CONFIG_PARAVIRT */
+void __attribute__((weak)) memory_setup(void)
+{
+       return setup_memory_region();
+}
+
 void __init setup_arch(char **cmdline_p)
 {
 	printk(KERN_INFO "Command line: %s\n", saved_command_line);
@@ -561,12 +567,6 @@ static int __cpuinit get_model_name(stru
 	return 1;
 }
 
-/* Overridden in paravirt.c if CONFIG_PARAVIRT */
-void __attribute__((weak)) memory_setup(void)
-{
-       return setup_memory_region();
-}
-
 static void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
 {
 	unsigned int n, dummy, eax, ebx, ecx, edx;
arch/x86_64/kernel/vsyscall.c +22 -6
diff -urp linux-2.6.19-paravirt0/arch/x86_64/kernel/vsyscall.c linux-2.6.19-paravirt1/arch/x86_64/kernel/vsyscall.c
@@ -73,7 +73,7 @@ static __always_inline void do_vgettimeo
 		usec = __xtime.tv_nsec / 1000;
 
 		if (__vxtime.mode != VXTIME_HPET) {
-			t = get_cycles_sync();
+			t = vget_cycles_sync();
 			if (t < __vxtime.last_tsc)
 				t = __vxtime.last_tsc;
 			usec += ((t - __vxtime.last_tsc) *
@@ -147,8 +147,8 @@ time_t __vsyscall(1) vtime(time_t *t)
 long __vsyscall(2)
 vgetcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
 {
-	unsigned int dummy, p;
-	unsigned long j = 0;
+	unsigned int p;
+	unsigned long dummy, j = 0;
 
 	/* Fast cache - only recompute value once per jiffies and avoid
 	   relatively costly rdtscp/cpuid otherwise.
@@ -162,7 +162,8 @@ vgetcpu(unsigned *cpu, unsigned *node, s
 		p = tcache->blob[1];
 	} else if (__vgetcpu_mode == VGETCPU_RDTSCP) {
 		/* Load per CPU data from RDTSCP */
-		rdtscp(dummy, dummy, p);
+		/* rdtscp() cannot be called due to the paravirt indirection */
+		asm("rdtscp" : "=A" (dummy), "=c" (p));
 	} else {
 		/* Load per CPU data from GDT */
 		asm("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));
@@ -257,7 +258,11 @@ static void __cpuinit vsyscall_set_cpu(i
 	node = cpu_to_node[cpu];
 #endif
 	if (cpu_has(&cpu_data[cpu], X86_FEATURE_RDTSCP))
-		write_rdtscp_aux((node << 12) | cpu);
+		/* This is write_rdtscp_aux. It cannot be called directly
+		 * due to the paravirt indirection */
+		asm("wrmsr"  :  /* no output */
+			     :  "d"(0),
+				"a" ((node << 12) | cpu), "c" (0xc0000103));
 
 	/* Store cpu number in limit so that it can be loaded quickly
 	   in user space in vgetcpu.
@@ -286,8 +291,12 @@ cpu_vsyscall_notifier(struct notifier_bl
 
 static void __init map_vsyscall(void)
 {
+#ifndef CONFIG_PARAVIRT
 	extern char __vsyscall_0;
 	unsigned long physaddr_page0 = __pa_symbol(&__vsyscall_0);
+#else
+	unsigned long physaddr_page0 = __pa_symbol(paravirt_ops.vsyscall_page);
+#endif
 
 	/* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */
 	__set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
@@ -300,7 +309,14 @@ static int __init vsyscall_init(void)
 	BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
 	BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
 	BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu));
-	map_vsyscall();
+#ifdef CONFIG_PARAVIRT
+	if (paravirt_ops.vsyscall_page)
+#endif
+		map_vsyscall();
+#ifdef CONFIG_PARAVIRT
+	else
+		__sysctl_vsyscall = 0;
+#endif
 #ifdef CONFIG_SYSCTL
 	register_sysctl_table(kernel_root_table2, 0);
 #endif
arch/x86_64/mm/pageattr.c +1 -1
diff -urp linux-2.6.19-paravirt0/arch/x86_64/mm/pageattr.c linux-2.6.19-paravirt1/arch/x86_64/mm/pageattr.c
@@ -81,7 +81,7 @@ static void flush_kernel_map(void *arg)
 		void *adr = page_address(pg);
 		if (cpu_has_clflush)
 			cache_flush_page(adr);
-		__flush_tlb_one(adr);
+		__flush_tlb_one((u64)adr);
 	}
 }
 
include/asm-x86_64/alternative.h +4 -1
diff -urp linux-2.6.19-paravirt0/include/asm-x86_64/alternative.h linux-2.6.19-paravirt1/include/asm-x86_64/alternative.h
@@ -134,8 +134,10 @@ static inline void alternatives_smp_swit
 #define LOCK_PREFIX ""
 #endif
 
-struct paravirt_patch;
+
+
 #ifdef CONFIG_PARAVIRT
+struct paravirt_patch;
 void apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end);
 #else
 static inline void
@@ -145,4 +147,5 @@ apply_paravirt(struct paravirt_patch *st
 #define __stop_parainstructions NULL
 #endif
 
+
 #endif /* _X86_64_ALTERNATIVE_H */
include/asm-x86_64/irqflags.h +0 -1
diff -urp linux-2.6.19-paravirt0/include/asm-x86_64/irqflags.h linux-2.6.19-paravirt1/include/asm-x86_64/irqflags.h
@@ -18,7 +18,6 @@ static inline int raw_irqs_disabled_flag
 {
 	return !(flags & (1 << 9));
 }
-
 #else
 
 /*
include/asm-x86_64/msr.h +5 -9
diff -urp linux-2.6.19-paravirt0/include/asm-x86_64/msr.h linux-2.6.19-paravirt1/include/asm-x86_64/msr.h
@@ -105,15 +105,6 @@ static inline void native_cpuid(unsigned
 
 #endif /* CONFIG_PARAVIRT */
 
-#define rdtscp(low,high,aux) \
-     asm volatile (".byte 0x0f,0x01,0xf9" : "=a" (low), "=d" (high), "=c" (aux))
-
-#define rdtscpll(val, aux) do { \
-     unsigned long __a, __d; \
-     asm volatile (".byte 0x0f,0x01,0xf9" : "=a" (__a), "=d" (__d), "=c" (aux)); \
-     (val) = (__d << 32) | __a; \
-} while (0)
-
 #define checking_wrmsrl(msr,val) wrmsr_safe(msr,(u32)(val),(u32)((val)>>32))
 #define write_tsc(val1,val2) wrmsr(0x10, val1, val2)
 
@@ -125,6 +116,7 @@ static inline void cpuid(unsigned int op
 	*eax = op;
 	__cpuid(eax, ebx, ecx, edx);
 }
+
 /* Some CPUID calls want 'count' to be placed in ecx */
 static inline void cpuid_count(int op, int count,
 			 int *eax, int *ebx, int *ecx, int *edx)
@@ -140,24 +132,28 @@ static inline void cpuid_count(int op, i
 static inline unsigned int cpuid_eax(unsigned int op)
 {
 	unsigned int eax, ebx, ecx, edx;
+	eax = op;
 	__cpuid(&eax, &ebx, &ecx, &edx);
 	return eax;
 }
 static inline unsigned int cpuid_ebx(unsigned int op)
 {
 	unsigned int eax, ebx, ecx, edx;
+	eax = op;
 	__cpuid(&eax, &ebx, &ecx, &edx);
 	return ebx;
 }
 static inline unsigned int cpuid_ecx(unsigned int op)
 {
 	unsigned int eax, ebx, ecx, edx;
+	eax = op;
 	__cpuid(&eax, &ebx, &ecx, &edx);
 	return ecx;
 }
 static inline unsigned int cpuid_edx(unsigned int op)
 {
 	unsigned int eax, ebx, ecx, edx;
+	eax = op;
 	__cpuid(&eax, &ebx, &ecx, &edx);
 	return edx;
 }
include/asm-x86_64/paravirt.h +50 -33

172 lines not on this page · raw message

include/asm-x86_64/timex.h +17 -2
diff -urp linux-2.6.19-paravirt0/include/asm-x86_64/timex.h linux-2.6.19-paravirt1/include/asm-x86_64/timex.h
@@ -31,14 +31,29 @@ static __always_inline cycles_t get_cycl
 {
 	unsigned long long ret;
 	unsigned eax;
+	unsigned int (*fn)(unsigned int) = &cpuid_eax;
 	/* Don't do an additional sync on CPUs where we know
 	   RDTSC is already synchronous. */
-	alternative_io("cpuid", ASM_NOP2, X86_FEATURE_SYNC_RDTSC,
-			  "=a" (eax), "0" (1) : "ebx","ecx","edx","memory");
+	alternative_io("call *%3", ASM_NOP2, X86_FEATURE_SYNC_RDTSC,
+			"=a" (eax) , "D" (1) , "m" (fn));
 	rdtscll(ret);
 	return ret;
 }
 
+/* Inside a vsyscall, we cannot call paravirt functions. (like rdtsc
+ * and cpuid). For the host, use this function instead */
+static __always_inline cycles_t vget_cycles_sync(void)
+{
+	unsigned long ret;
+	unsigned eax;
+	/* Don't do an additional sync on CPUs where we know
+	   RDTSC is already synchronous. */
+	alternative_io("cpuid", ASM_NOP2, X86_FEATURE_SYNC_RDTSC,
+			  "=a" (eax), "0" (1) : "ebx","ecx","edx","memory");
+
+	asm volatile("rdtsc" : "=A" (ret));
+	return ret;
+}
 extern unsigned int cpu_khz;
 
 extern int read_current_timer(unsigned long *timer_value);

_______________________________________________
Virtualization mailing list
[email protected]
https://lists.osdl.org/mailman/listinfo/virtualization