-
Notifications
You must be signed in to change notification settings - Fork 16
(FACT-1715) Add data source for CPUID instruction #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,17 @@ | ||
| #pragma once | ||
|
|
||
| #include <internal/sources/dmi_source.hpp> | ||
| #include <internal/sources/cpuid_source.hpp> | ||
|
|
||
| namespace whereami { namespace detectors { | ||
|
|
||
| /** | ||
| * VirtualBox detector function | ||
| * @param cpuid_source An instance of a CPUID data source | ||
| * @param dmi_source An instance of a DMI data source | ||
| * @return Whether this machine is a VirtualBox guest | ||
| */ | ||
| bool virtualbox(const sources::dmi_base& dmi_source); | ||
| bool virtualbox(const sources::cpuid_base& cpuid_source, | ||
| const sources::dmi_base& dmi_source); | ||
|
|
||
| }} // namespace whereami::detectors |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| namespace whereami { namespace sources { | ||
|
|
||
| /** | ||
| * Register values returned by CPUID | ||
| */ | ||
| struct cpuid_registers | ||
| { | ||
| /** | ||
| * Value from eax register | ||
| */ | ||
| unsigned int eax; | ||
| /** | ||
| * Value from ebx register | ||
| */ | ||
| unsigned int ebx; | ||
| /** | ||
| * Value from ecx register | ||
| */ | ||
| unsigned int ecx; | ||
| /** | ||
| * Value from edx register | ||
| */ | ||
| unsigned int edx; | ||
| }; | ||
|
|
||
| /** | ||
| * Base cpuid data source | ||
| */ | ||
| class cpuid_base | ||
| { | ||
| public: | ||
| /** | ||
| * Call the cpuid instruction | ||
| * @param leaf The value to pass into CPUID via eax; Determines the type of information returned | ||
| * @return Register object with results of the CPUID function | ||
| */ | ||
| virtual cpuid_registers read_cpuid(unsigned int leaf) const; | ||
| /** | ||
| * Determine whether CPUID reports that this system is running on a hypervisor (Calls CPUID with eax = 1) | ||
| * @return whether CPUID reports a hypervisor | ||
| */ | ||
| bool has_hypervisor() const; | ||
| /** | ||
| * Retrieve the vendor ID (Calls CPUID with eax = VENDOR_LEAF) | ||
| * @return the vendor ID string | ||
| */ | ||
| std::string vendor() const; | ||
| /** | ||
| * Most hypervisors store vendor information in this leaf | ||
| * When this value is passed to CPUID, ebx, ecx, and edx report a vendor ID | ||
| */ | ||
| static const unsigned int VENDOR_LEAF = 0x40000000; | ||
| /** | ||
| * When CPUID is passed a 1, bit 31 of ecx reports whether the machine is running on a hypervisor | ||
| */ | ||
| static const unsigned int HYPERVISOR_PRESENT = 1; | ||
| }; | ||
|
|
||
| /** | ||
| * Default CPUID data source; Requires no extra functionality beyond the base. | ||
| */ | ||
| using cpuid = cpuid_base; | ||
|
|
||
| }} // namespace whereami::sources | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,18 @@ | ||
| #include <internal/detectors/virtualbox_detector.hpp> | ||
| #include <leatherman/logging/logging.hpp> | ||
| #include <leatherman/execution/execution.hpp> | ||
| #include <leatherman/util/regex.hpp> | ||
|
|
||
| using namespace std; | ||
| using namespace whereami; | ||
| using namespace leatherman::execution; | ||
| using namespace leatherman::util; | ||
|
|
||
| namespace whereami { namespace detectors { | ||
|
|
||
| bool virtualbox(const sources::dmi_base& dmi_source) { | ||
| bool virtualbox(const sources::cpuid_base& cpuid_source, | ||
| const sources::dmi_base& dmi_source) { | ||
| static const boost::regex re_virtualbox{"[Vv]irtual[Bb]ox"}; | ||
|
|
||
| return re_search( | ||
| dmi_source.product_name(), | ||
| re_virtualbox); | ||
| return cpuid_source.vendor() == "VBoxVBoxVBox" || | ||
| re_search(dmi_source.product_name(), re_virtualbox); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. longer-term we're going to want to be able to track which data sources were successful, but this is good until we have a metadata object to work with 👍 |
||
| } | ||
|
|
||
| }} // namespace whereami::detectors | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #include <internal/sources/cpuid_source.hpp> | ||
|
|
||
| using namespace std; | ||
| using namespace whereami::sources; | ||
|
|
||
| namespace whereami { namespace sources { | ||
|
|
||
| #if defined(__x86_64__) || defined(__i386__) | ||
| cpuid_registers cpuid_base::read_cpuid(unsigned int leaf) const { | ||
| cpuid_registers result; | ||
| asm volatile( | ||
| "xchgl %%ebx,%1; xor %%ebx,%%ebx; cpuid; xchgl %%ebx,%1" | ||
| : "=a" (result.eax), "=b" (result.ebx), "=c" (result.ecx), "=d" (result.edx) | ||
| : "a" (leaf), "c" (0)); | ||
| return result; | ||
| } | ||
| #else | ||
| cpuid_registers cpuid_base::read_cpuid(unsigned int leaf) const { | ||
| return {}; | ||
| } | ||
| #endif | ||
|
|
||
| bool cpuid_base::has_hypervisor() const | ||
| { | ||
| auto regs = read_cpuid(HYPERVISOR_PRESENT); | ||
| return static_cast<bool>(regs.ecx & (1 << 31)); | ||
| } | ||
|
|
||
| string cpuid_base::vendor() const | ||
| { | ||
| auto regs = read_cpuid(VENDOR_LEAF); | ||
| unsigned int result[4] = {regs.ebx, regs.ecx, regs.edx, 0}; | ||
| return string{reinterpret_cast<char*>(result)}; | ||
| } | ||
|
|
||
| }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #include "./cpuid_fixtures.hpp" | ||
|
|
||
| using namespace whereami::testing; | ||
| using namespace whereami::sources; | ||
| using namespace std; | ||
|
|
||
| namespace whereami { namespace testing { namespace cpuid { | ||
|
|
||
| cpuid_registers cpuid_fixture_empty::read_cpuid(unsigned int leaf) const | ||
| { | ||
| return {}; | ||
| } | ||
|
|
||
| cpuid_fixture_values::cpuid_fixture_values(std::unordered_map<unsigned int, sources::cpuid_registers> values) | ||
| : register_values_(std::move(values)) | ||
| { | ||
| } | ||
|
|
||
| cpuid_registers cpuid_fixture_values::read_cpuid(unsigned int leaf) const | ||
| { | ||
| auto it = register_values_.find(leaf); | ||
| if (it == register_values_.end()) { | ||
| return {}; | ||
| } | ||
| return it->second; | ||
| } | ||
|
|
||
| }}} // namespace whereami::testing::cpuid |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #include "../fixtures.hpp" | ||
| #include <internal/sources/cpuid_source.hpp> | ||
| #include <unordered_map> | ||
|
|
||
| namespace whereami { namespace testing { namespace cpuid { | ||
|
|
||
| /** | ||
| * CPUID data source returning no usable information | ||
| */ | ||
| class cpuid_fixture_empty : public sources::cpuid_base { | ||
| public: | ||
| sources::cpuid_registers read_cpuid(unsigned int leaf) const override; | ||
| }; | ||
|
|
||
| /** | ||
| * CPUID data source with predefined register results | ||
| */ | ||
| class cpuid_fixture_values : public sources::cpuid_base { | ||
| public: | ||
| cpuid_fixture_values(std::unordered_map<unsigned int, sources::cpuid_registers> values); | ||
| sources::cpuid_registers read_cpuid(unsigned int leaf) const override; | ||
|
|
||
| protected: | ||
| std::unordered_map<unsigned int, sources::cpuid_registers> register_values_; | ||
| }; | ||
|
|
||
| /** | ||
| * Expected raw register values | ||
| */ | ||
| namespace register_fixtures { | ||
| static const sources::cpuid_registers HYPERVISOR_ABSENT{0, 0, 0, 0}; | ||
| static const sources::cpuid_registers HYPERVISOR_PRESENT{0, 0, 3736609283, 0}; | ||
| static const sources::cpuid_registers VENDOR_NONE{0, 0, 0, 0}; | ||
| static const sources::cpuid_registers VENDOR_KVMKVMKVM{0, 1263359563, 1447775574, 77}; | ||
| static const sources::cpuid_registers VENDOR_VBoxVBoxVBox{0, 2020557398, 2020557398, 2020557398}; | ||
| } | ||
|
|
||
| /** | ||
| * Aliased here to allow use in cpuid fixture initialization lists | ||
| */ | ||
| static const unsigned int VENDOR_LEAF = sources::cpuid_base::VENDOR_LEAF; | ||
| static const unsigned int HYPERVISOR_PRESENT = sources::cpuid_base::HYPERVISOR_PRESENT; | ||
|
|
||
| }}}; // namespace whereami::testing::cpuid |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #include <catch.hpp> | ||
| #include <iostream> | ||
| #include "../fixtures/cpuid_fixtures.hpp" | ||
|
|
||
| using namespace std; | ||
| using namespace whereami::sources; | ||
| using namespace whereami::testing::cpuid; | ||
|
|
||
| SCENARIO("No cpuid information is available") { | ||
| cpuid_fixture_empty cpuid_source; | ||
| WHEN("attempting to call cpuid") { | ||
| THEN("no information is found") { | ||
| REQUIRE(cpuid_source.vendor().empty()); | ||
| REQUIRE_FALSE(cpuid_source.has_hypervisor()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| SCENARIO("Using the cpuid data source on a virtual machine") { | ||
| WHEN("looking for a hypervisor") { | ||
| cpuid_fixture_values cpuid_source({ | ||
| {VENDOR_LEAF, register_fixtures::VENDOR_VBoxVBoxVBox}, | ||
| {HYPERVISOR_PRESENT, register_fixtures::HYPERVISOR_PRESENT}, | ||
| }); | ||
| THEN("a hypervisor is detected") { | ||
| REQUIRE(cpuid_source.has_hypervisor()); | ||
| } | ||
| THEN("a vendor ID is found") { | ||
| REQUIRE(cpuid_source.vendor() == "VBoxVBoxVBox"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| SCENARIO("Using the cpuid data source on a physical machine") { | ||
| WHEN("looking for a hypervisor") { | ||
| cpuid_fixture_values cpuid_source({ | ||
| {VENDOR_LEAF, register_fixtures::VENDOR_NONE}, | ||
| {HYPERVISOR_PRESENT, register_fixtures::HYPERVISOR_ABSENT}, | ||
| }); | ||
| THEN("no hypervisor is detected") { | ||
| REQUIRE_FALSE(cpuid_source.has_hypervisor()); | ||
| } | ||
| THEN("no vendor ID is found") { | ||
| REQUIRE(cpuid_source.vendor() == ""); | ||
| } | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say these comments are redundant, but I think travis will fail if we don't have 100% docs coverage :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct! 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they have to be here, maybe we could make them a little more descriptive? Something like "Value from eax register", etc. Still redundant, but slightly less general (I'd argue any field could accurately be annotated with " value" :P).