| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/bin/guest/vmm/guest_config.h" |
| |
| #include <zircon/compiler.h> |
| #include <zircon/syscalls.h> |
| |
| #include "gtest/gtest.h" |
| #include "lib/fxl/arraysize.h" |
| |
| class GuestConfigParserTest : public ::testing::Test { |
| protected: |
| GuestConfig config_; |
| GuestConfigParser parser_{&config_}; |
| |
| zx_status_t ParseArg(const char* arg) { |
| const char* argv[] = {"exe_name", arg}; |
| return parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv)); |
| } |
| }; |
| |
| TEST_F(GuestConfigParserTest, DefaultValues) { |
| ASSERT_EQ(ZX_OK, parser_.ParseConfig("{}")); |
| ASSERT_EQ(Kernel::ZIRCON, config_.kernel()); |
| ASSERT_TRUE(config_.kernel_path().empty()); |
| ASSERT_TRUE(config_.ramdisk_path().empty()); |
| ASSERT_EQ(zx_system_get_num_cpus(), config_.cpus()); |
| ASSERT_TRUE(config_.block_devices().empty()); |
| ASSERT_TRUE(config_.cmdline().empty()); |
| } |
| |
| TEST_F(GuestConfigParserTest, ParseConfig) { |
| ASSERT_EQ(ZX_OK, parser_.ParseConfig( |
| R"JSON({ |
| "zircon": "zircon_path", |
| "ramdisk": "ramdisk_path", |
| "cpus": "4", |
| "block": "/pkg/data/block_path", |
| "cmdline": "kernel cmdline" |
| })JSON")); |
| ASSERT_EQ(Kernel::ZIRCON, config_.kernel()); |
| ASSERT_EQ("zircon_path", config_.kernel_path()); |
| ASSERT_EQ("ramdisk_path", config_.ramdisk_path()); |
| ASSERT_EQ(4, config_.cpus()); |
| ASSERT_EQ(1, config_.block_devices().size()); |
| ASSERT_EQ("/pkg/data/block_path", config_.block_devices()[0].path); |
| ASSERT_EQ("kernel cmdline", config_.cmdline()); |
| } |
| |
| TEST_F(GuestConfigParserTest, ParseArgs) { |
| const char* argv[] = { |
| "exe_name", "--linux=linux_path", "--ramdisk=ramdisk_path", |
| "--cpus=4", "--block=/pkg/data/block_path", "--cmdline=kernel_cmdline"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| ASSERT_EQ(Kernel::LINUX, config_.kernel()); |
| ASSERT_EQ("linux_path", config_.kernel_path()); |
| ASSERT_EQ("ramdisk_path", config_.ramdisk_path()); |
| ASSERT_EQ(4, config_.cpus()); |
| ASSERT_EQ(1, config_.block_devices().size()); |
| ASSERT_EQ("/pkg/data/block_path", config_.block_devices()[0].path); |
| ASSERT_EQ("kernel_cmdline", config_.cmdline()); |
| } |
| |
| TEST_F(GuestConfigParserTest, UnknownArgument) { |
| const char* argv[] = {"exe_name", "--invalid-arg"}; |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| } |
| |
| TEST_F(GuestConfigParserTest, BooleanFlag) { |
| const char* argv_false[] = {"exe_name", "--virtio-net=false"}; |
| ASSERT_EQ(ZX_OK, parser_.ParseArgcArgv(arraysize(argv_false), |
| const_cast<char**>(argv_false))); |
| ASSERT_FALSE(config_.virtio_net()); |
| |
| const char* argv_true[] = {"exe_name", "--virtio-net=true"}; |
| ASSERT_EQ(ZX_OK, parser_.ParseArgcArgv(arraysize(argv_true), |
| const_cast<char**>(argv_true))); |
| ASSERT_TRUE(config_.virtio_net()); |
| } |
| |
| TEST_F(GuestConfigParserTest, CommandLineAppend) { |
| const char* argv[] = {"exe_name", "--cmdline=foo bar", "--cmdline-add=baz"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| ASSERT_EQ("foo bar baz", config_.cmdline()); |
| } |
| |
| TEST_F(GuestConfigParserTest, BlockSpecArg) { |
| const char* argv[] = {"exe_name", "--block=/pkg/data/foo,ro,fdio", |
| "--block=/dev/class/block/001,rw,fdio"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| ASSERT_EQ(2, config_.block_devices().size()); |
| |
| const BlockSpec& spec0 = config_.block_devices()[0]; |
| ASSERT_EQ(fuchsia::guest::BlockMode::READ_ONLY, spec0.mode); |
| ASSERT_EQ(fuchsia::guest::BlockFormat::RAW, spec0.format); |
| ASSERT_EQ("/pkg/data/foo", spec0.path); |
| |
| const BlockSpec& spec1 = config_.block_devices()[1]; |
| ASSERT_EQ(fuchsia::guest::BlockMode::READ_WRITE, spec1.mode); |
| ASSERT_EQ(fuchsia::guest::BlockFormat::RAW, spec1.format); |
| ASSERT_EQ("/dev/class/block/001", spec1.path); |
| } |
| |
| TEST_F(GuestConfigParserTest, BlockSpecJson) { |
| ASSERT_EQ(ZX_OK, parser_.ParseConfig( |
| R"JSON({ |
| "block": [ |
| "/pkg/data/foo,ro,fdio", |
| "/dev/class/block/001,rw,fdio" |
| ] |
| })JSON")); |
| ASSERT_EQ(2, config_.block_devices().size()); |
| |
| const BlockSpec& spec0 = config_.block_devices()[0]; |
| ASSERT_EQ(fuchsia::guest::BlockMode::READ_ONLY, spec0.mode); |
| ASSERT_EQ(fuchsia::guest::BlockFormat::RAW, spec0.format); |
| ASSERT_EQ("/pkg/data/foo", spec0.path); |
| |
| const BlockSpec& spec1 = config_.block_devices()[1]; |
| ASSERT_EQ(fuchsia::guest::BlockMode::READ_WRITE, spec1.mode); |
| ASSERT_EQ(fuchsia::guest::BlockFormat::RAW, spec1.format); |
| ASSERT_EQ("/dev/class/block/001", spec1.path); |
| } |
| |
| TEST_F(GuestConfigParserTest, InterruptSpecArg) { |
| const char* argv[] = {"exe_name", "--interrupt=32,2", "--interrupt=33,4"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| ASSERT_EQ(2, config_.interrupts().size()); |
| |
| const InterruptSpec& spec0 = config_.interrupts()[0]; |
| ASSERT_EQ(32, spec0.vector); |
| ASSERT_EQ(ZX_INTERRUPT_MODE_EDGE_LOW, spec0.options); |
| |
| const InterruptSpec& spec1 = config_.interrupts()[1]; |
| ASSERT_EQ(33, spec1.vector); |
| ASSERT_EQ(ZX_INTERRUPT_MODE_EDGE_HIGH, spec1.options); |
| } |
| |
| TEST_F(GuestConfigParserTest, InterruptSpecJson) { |
| ASSERT_EQ(ZX_OK, parser_.ParseConfig( |
| R"JSON({ |
| "interrupt": [ |
| "32,2", |
| "33,4" |
| ] |
| })JSON")); |
| ASSERT_EQ(2, config_.interrupts().size()); |
| |
| const InterruptSpec& spec0 = config_.interrupts()[0]; |
| ASSERT_EQ(32, spec0.vector); |
| ASSERT_EQ(ZX_INTERRUPT_MODE_EDGE_LOW, spec0.options); |
| |
| const InterruptSpec& spec1 = config_.interrupts()[1]; |
| ASSERT_EQ(33, spec1.vector); |
| ASSERT_EQ(ZX_INTERRUPT_MODE_EDGE_HIGH, spec1.options); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_1024k) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=1024k")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0, memory[0].addr); |
| EXPECT_EQ(1ul << 20, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_2M) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=2M")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0, memory[0].addr); |
| EXPECT_EQ(2ul << 20, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_4G) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=4G")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0, memory[0].addr); |
| EXPECT_EQ(4ul << 30, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_AddressAndSize) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=ffff,4G")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0xffff, memory[0].addr); |
| EXPECT_EQ(4ul << 30, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_HostCached) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=eeee,2G,cached")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0xeeee, memory[0].addr); |
| EXPECT_EQ(2ul << 30, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::HOST_CACHED, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_HostDevice) { |
| ASSERT_EQ(ZX_OK, ParseArg("--memory=dddd,1G,device")); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(1, memory.size()); |
| EXPECT_EQ(0xdddd, memory[0].addr); |
| EXPECT_EQ(1ul << 30, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::HOST_DEVICE, memory[0].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_MultipleEntries) { |
| const char* argv[] = {"exe_name", "--memory=f0000000,1M", |
| "--memory=ffffffff,2M"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(argv), const_cast<char**>(argv))); |
| const auto& memory = config_.memory(); |
| EXPECT_EQ(2, memory.size()); |
| EXPECT_EQ(0xf0000000, memory[0].addr); |
| EXPECT_EQ(1ul << 20, memory[0].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[0].policy); |
| EXPECT_EQ(0xffffffff, memory[1].addr); |
| EXPECT_EQ(2ul << 20, memory[1].len); |
| EXPECT_EQ(MemoryPolicy::GUEST_CACHED, memory[1].policy); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_TooSmall) { |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, ParseArg("--memory=1k")); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_IllegalModifier) { |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, ParseArg("--memory=5l")); |
| } |
| |
| TEST_F(GuestConfigParserTest, Memory_NonNumber) { |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, ParseArg("--memory=abc")); |
| } |
| |
| TEST_F(GuestConfigParserTest, VirtioGpu) { |
| GuestConfig config; |
| GuestConfigParser parser(&config); |
| |
| const char* virtio_gpu_true_argv[] = {"exe_name", "--virtio-gpu=true"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(virtio_gpu_true_argv), |
| const_cast<char**>(virtio_gpu_true_argv))); |
| ASSERT_TRUE(config_.virtio_gpu()); |
| |
| const char* virtio_gpu_false_argv[] = {"exe_name", "--virtio-gpu=false"}; |
| ASSERT_EQ(ZX_OK, |
| parser_.ParseArgcArgv(arraysize(virtio_gpu_false_argv), |
| const_cast<char**>(virtio_gpu_false_argv))); |
| ASSERT_FALSE(config_.virtio_gpu()); |
| } |