diff options
author | Sasha Levin <levinsasha928@gmail.com> | 2011-11-28 04:20:29 -0500 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2011-12-27 04:22:02 -0500 |
commit | 831bf664e9c1fc08fc6b3984d00d275cac82f5e9 (patch) | |
tree | a75b2baafbd58e9b20991dbab7bdadf2552e756d /arch/x86/kvm | |
parent | fb215366b3c7320ac25dca766a0152df16534932 (diff) |
KVM: Refactor and simplify kvm_dev_ioctl_get_supported_cpuid
This patch cleans and simplifies kvm_dev_ioctl_get_supported_cpuid by using a table
instead of duplicating code as Avi suggested.
This patch also fixes a bug where kvm_dev_ioctl_get_supported_cpuid would return
-E2BIG when amount of entries passed was just right.
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm')
-rw-r--r-- | arch/x86/kvm/cpuid.c | 113 |
1 files changed, 63 insertions, 50 deletions
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 47be763e1b60..52593e8e1ae8 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c | |||
@@ -183,9 +183,10 @@ static bool supported_xcr0_bit(unsigned bit) | |||
183 | 183 | ||
184 | #define F(x) bit(X86_FEATURE_##x) | 184 | #define F(x) bit(X86_FEATURE_##x) |
185 | 185 | ||
186 | static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | 186 | static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, |
187 | u32 index, int *nent, int maxnent) | 187 | u32 index, int *nent, int maxnent) |
188 | { | 188 | { |
189 | int r; | ||
189 | unsigned f_nx = is_efer_nx() ? F(NX) : 0; | 190 | unsigned f_nx = is_efer_nx() ? F(NX) : 0; |
190 | #ifdef CONFIG_X86_64 | 191 | #ifdef CONFIG_X86_64 |
191 | unsigned f_gbpages = (kvm_x86_ops->get_lpage_level() == PT_PDPE_LEVEL) | 192 | unsigned f_gbpages = (kvm_x86_ops->get_lpage_level() == PT_PDPE_LEVEL) |
@@ -246,6 +247,12 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
246 | 247 | ||
247 | /* all calls to cpuid_count() should be made on the same cpu */ | 248 | /* all calls to cpuid_count() should be made on the same cpu */ |
248 | get_cpu(); | 249 | get_cpu(); |
250 | |||
251 | r = -E2BIG; | ||
252 | |||
253 | if (*nent >= maxnent) | ||
254 | goto out; | ||
255 | |||
249 | do_cpuid_1_ent(entry, function, index); | 256 | do_cpuid_1_ent(entry, function, index); |
250 | ++*nent; | 257 | ++*nent; |
251 | 258 | ||
@@ -271,7 +278,10 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
271 | 278 | ||
272 | entry->flags |= KVM_CPUID_FLAG_STATEFUL_FUNC; | 279 | entry->flags |= KVM_CPUID_FLAG_STATEFUL_FUNC; |
273 | entry->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT; | 280 | entry->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT; |
274 | for (t = 1; t < times && *nent < maxnent; ++t) { | 281 | for (t = 1; t < times; ++t) { |
282 | if (*nent >= maxnent) | ||
283 | goto out; | ||
284 | |||
275 | do_cpuid_1_ent(&entry[t], function, 0); | 285 | do_cpuid_1_ent(&entry[t], function, 0); |
276 | entry[t].flags |= KVM_CPUID_FLAG_STATEFUL_FUNC; | 286 | entry[t].flags |= KVM_CPUID_FLAG_STATEFUL_FUNC; |
277 | ++*nent; | 287 | ++*nent; |
@@ -284,7 +294,10 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
284 | 294 | ||
285 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; | 295 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; |
286 | /* read more entries until cache_type is zero */ | 296 | /* read more entries until cache_type is zero */ |
287 | for (i = 1; *nent < maxnent; ++i) { | 297 | for (i = 1; ; ++i) { |
298 | if (*nent >= maxnent) | ||
299 | goto out; | ||
300 | |||
288 | cache_type = entry[i - 1].eax & 0x1f; | 301 | cache_type = entry[i - 1].eax & 0x1f; |
289 | if (!cache_type) | 302 | if (!cache_type) |
290 | break; | 303 | break; |
@@ -316,7 +329,10 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
316 | 329 | ||
317 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; | 330 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; |
318 | /* read more entries until level_type is zero */ | 331 | /* read more entries until level_type is zero */ |
319 | for (i = 1; *nent < maxnent; ++i) { | 332 | for (i = 1; ; ++i) { |
333 | if (*nent >= maxnent) | ||
334 | goto out; | ||
335 | |||
320 | level_type = entry[i - 1].ecx & 0xff00; | 336 | level_type = entry[i - 1].ecx & 0xff00; |
321 | if (!level_type) | 337 | if (!level_type) |
322 | break; | 338 | break; |
@@ -331,7 +347,10 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
331 | int idx, i; | 347 | int idx, i; |
332 | 348 | ||
333 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; | 349 | entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; |
334 | for (idx = 1, i = 1; *nent < maxnent && idx < 64; ++idx) { | 350 | for (idx = 1, i = 1; idx < 64; ++idx) { |
351 | if (*nent >= maxnent) | ||
352 | goto out; | ||
353 | |||
335 | do_cpuid_1_ent(&entry[i], function, idx); | 354 | do_cpuid_1_ent(&entry[i], function, idx); |
336 | if (entry[i].eax == 0 || !supported_xcr0_bit(idx)) | 355 | if (entry[i].eax == 0 || !supported_xcr0_bit(idx)) |
337 | continue; | 356 | continue; |
@@ -416,17 +435,41 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, | |||
416 | 435 | ||
417 | kvm_x86_ops->set_supported_cpuid(function, entry); | 436 | kvm_x86_ops->set_supported_cpuid(function, entry); |
418 | 437 | ||
438 | r = 0; | ||
439 | |||
440 | out: | ||
419 | put_cpu(); | 441 | put_cpu(); |
442 | |||
443 | return r; | ||
420 | } | 444 | } |
421 | 445 | ||
422 | #undef F | 446 | #undef F |
423 | 447 | ||
448 | struct kvm_cpuid_param { | ||
449 | u32 func; | ||
450 | u32 idx; | ||
451 | bool has_leaf_count; | ||
452 | bool (*qualifier)(struct kvm_cpuid_param *param); | ||
453 | }; | ||
454 | |||
455 | static bool is_centaur_cpu(struct kvm_cpuid_param *param) | ||
456 | { | ||
457 | return boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR; | ||
458 | } | ||
459 | |||
424 | int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, | 460 | int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, |
425 | struct kvm_cpuid_entry2 __user *entries) | 461 | struct kvm_cpuid_entry2 __user *entries) |
426 | { | 462 | { |
427 | struct kvm_cpuid_entry2 *cpuid_entries; | 463 | struct kvm_cpuid_entry2 *cpuid_entries; |
428 | int limit, nent = 0, r = -E2BIG; | 464 | int limit, nent = 0, r = -E2BIG, i; |
429 | u32 func; | 465 | u32 func; |
466 | static struct kvm_cpuid_param param[] = { | ||
467 | { .func = 0, .has_leaf_count = true }, | ||
468 | { .func = 0x80000000, .has_leaf_count = true }, | ||
469 | { .func = 0xC0000000, .qualifier = is_centaur_cpu, .has_leaf_count = true }, | ||
470 | { .func = KVM_CPUID_SIGNATURE }, | ||
471 | { .func = KVM_CPUID_FEATURES }, | ||
472 | }; | ||
430 | 473 | ||
431 | if (cpuid->nent < 1) | 474 | if (cpuid->nent < 1) |
432 | goto out; | 475 | goto out; |
@@ -437,61 +480,31 @@ int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, | |||
437 | if (!cpuid_entries) | 480 | if (!cpuid_entries) |
438 | goto out; | 481 | goto out; |
439 | 482 | ||
440 | do_cpuid_ent(&cpuid_entries[0], 0, 0, &nent, cpuid->nent); | 483 | r = 0; |
441 | limit = cpuid_entries[0].eax; | 484 | for (i = 0; i < ARRAY_SIZE(param); i++) { |
442 | for (func = 1; func <= limit && nent < cpuid->nent; ++func) | 485 | struct kvm_cpuid_param *ent = ¶m[i]; |
443 | do_cpuid_ent(&cpuid_entries[nent], func, 0, | ||
444 | &nent, cpuid->nent); | ||
445 | r = -E2BIG; | ||
446 | if (nent >= cpuid->nent) | ||
447 | goto out_free; | ||
448 | |||
449 | do_cpuid_ent(&cpuid_entries[nent], 0x80000000, 0, &nent, cpuid->nent); | ||
450 | limit = cpuid_entries[nent - 1].eax; | ||
451 | for (func = 0x80000001; func <= limit && nent < cpuid->nent; ++func) | ||
452 | do_cpuid_ent(&cpuid_entries[nent], func, 0, | ||
453 | &nent, cpuid->nent); | ||
454 | |||
455 | |||
456 | 486 | ||
457 | r = -E2BIG; | 487 | if (ent->qualifier && !ent->qualifier(ent)) |
458 | if (nent >= cpuid->nent) | 488 | continue; |
459 | goto out_free; | ||
460 | 489 | ||
461 | /* Add support for Centaur's CPUID instruction. */ | 490 | r = do_cpuid_ent(&cpuid_entries[nent], ent->func, ent->idx, |
462 | if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) { | ||
463 | do_cpuid_ent(&cpuid_entries[nent], 0xC0000000, 0, | ||
464 | &nent, cpuid->nent); | 491 | &nent, cpuid->nent); |
465 | 492 | ||
466 | r = -E2BIG; | 493 | if (r) |
467 | if (nent >= cpuid->nent) | ||
468 | goto out_free; | 494 | goto out_free; |
469 | 495 | ||
496 | if (!ent->has_leaf_count) | ||
497 | continue; | ||
498 | |||
470 | limit = cpuid_entries[nent - 1].eax; | 499 | limit = cpuid_entries[nent - 1].eax; |
471 | for (func = 0xC0000001; | 500 | for (func = ent->func + 1; func <= limit && nent < cpuid->nent && r == 0; ++func) |
472 | func <= limit && nent < cpuid->nent; ++func) | 501 | r = do_cpuid_ent(&cpuid_entries[nent], func, ent->idx, |
473 | do_cpuid_ent(&cpuid_entries[nent], func, 0, | 502 | &nent, cpuid->nent); |
474 | &nent, cpuid->nent); | ||
475 | 503 | ||
476 | r = -E2BIG; | 504 | if (r) |
477 | if (nent >= cpuid->nent) | ||
478 | goto out_free; | 505 | goto out_free; |
479 | } | 506 | } |
480 | 507 | ||
481 | do_cpuid_ent(&cpuid_entries[nent], KVM_CPUID_SIGNATURE, 0, &nent, | ||
482 | cpuid->nent); | ||
483 | |||
484 | r = -E2BIG; | ||
485 | if (nent >= cpuid->nent) | ||
486 | goto out_free; | ||
487 | |||
488 | do_cpuid_ent(&cpuid_entries[nent], KVM_CPUID_FEATURES, 0, &nent, | ||
489 | cpuid->nent); | ||
490 | |||
491 | r = -E2BIG; | ||
492 | if (nent >= cpuid->nent) | ||
493 | goto out_free; | ||
494 | |||
495 | r = -EFAULT; | 508 | r = -EFAULT; |
496 | if (copy_to_user(entries, cpuid_entries, | 509 | if (copy_to_user(entries, cpuid_entries, |
497 | nent * sizeof(struct kvm_cpuid_entry2))) | 510 | nent * sizeof(struct kvm_cpuid_entry2))) |