Skip to content

Commit 5f9f9d9

Browse files
slpptitSebm1m1k4tz
committed
Support using Box64 for x86 emulation
Add the "emu" command line argument to let users choose the emulator to be used for running x86 binaries. Add support for installing Box64 in binfmt_misc. If the argument is not present in the command line, try to use FEX first and if it can't be set up, try again with Box64. Supersedes: #140 Co-authored-by: ptitSeb <[email protected]> Co-authored-by: Alex Arnold <[email protected]> Signed-off-by: Sergio Lopez <[email protected]>
1 parent 49a0346 commit 5f9f9d9

7 files changed

Lines changed: 99 additions & 4 deletions

File tree

crates/muvm/src/bin/muvm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ fn main() -> Result<ExitCode> {
394394
gid: getgid().as_raw(),
395395
host_display: display,
396396
merged_rootfs: options.merged_rootfs,
397+
emulator: options.emulator,
397398
};
398399
let mut muvm_config_file = NamedTempFile::new()
399400
.context("Failed to create a temporary file to store the muvm guest config")?;

crates/muvm/src/cli_options.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use anyhow::{anyhow, Context};
55
use bpaf::{any, construct, long, positional, OptionParser, Parser};
66

77
use crate::types::MiB;
8+
use crate::utils::launch::Emulator;
89

910
#[derive(Clone, Debug)]
1011
pub struct Options {
@@ -19,6 +20,7 @@ pub struct Options {
1920
pub tty: bool,
2021
pub privileged: bool,
2122
pub publish_ports: Vec<String>,
23+
pub emulator: Option<Emulator>,
2224
pub command: PathBuf,
2325
pub command_args: Vec<String>,
2426
}
@@ -60,6 +62,15 @@ pub fn options() -> OptionParser<Options> {
6062
None => Ok((s, None)),
6163
})
6264
.many();
65+
let emulator = long("emu")
66+
.help(
67+
"Which emulator to use for running x86_64 binaries.
68+
Valid options are \"box\" and \"fex\". If this argument is not
69+
present, muvm will try to use FEX, falling back to Box if it
70+
can't be found.",
71+
)
72+
.argument::<Emulator>("EMU")
73+
.optional();
6374
let mem = long("mem")
6475
.help(
6576
"The amount of RAM, in MiB, that will be available to this microVM.
@@ -140,6 +151,7 @@ pub fn options() -> OptionParser<Options> {
140151
tty,
141152
privileged,
142153
publish_ports,
154+
emulator,
143155
// positionals
144156
command,
145157
command_args,

crates/muvm/src/guest/bin/muvm-guest.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::process::Command;
66
use std::{cmp, env, fs, thread};
77

88
use anyhow::{Context, Result};
9+
use muvm::guest::box64::setup_box;
910
use muvm::guest::fex::setup_fex;
1011
use muvm::guest::hidpipe::start_hidpipe;
1112
use muvm::guest::mount::mount_filesystems;
@@ -15,7 +16,7 @@ use muvm::guest::socket::setup_socket_proxy;
1516
use muvm::guest::user::setup_user;
1617
use muvm::guest::x11::setup_x11_forwarding;
1718
use muvm::guest::x11bridge::start_x11bridge;
18-
use muvm::utils::launch::{GuestConfiguration, PULSE_SOCKET};
19+
use muvm::utils::launch::{Emulator, GuestConfiguration, PULSE_SOCKET};
1920
use nix::unistd::{Gid, Uid};
2021
use rustix::process::{getrlimit, setrlimit, Resource};
2122

@@ -77,7 +78,20 @@ fn main() -> Result<()> {
7778

7879
Command::new("/usr/lib/systemd/systemd-udevd").spawn()?;
7980

80-
setup_fex()?;
81+
if let Some(emulator) = options.emulator {
82+
match emulator {
83+
Emulator::Box => setup_box()?,
84+
Emulator::Fex => setup_fex()?,
85+
};
86+
} else if let Err(err) = setup_fex() {
87+
eprintln!("Error setting up FEX in binfmt_misc: {err}");
88+
eprintln!("Failed to find or configure FEX, falling back to Box");
89+
90+
if let Err(err) = setup_box() {
91+
eprintln!("Error setting up Box in binfmt_misc: {err}");
92+
eprintln!("No emulators were configured, x86 emulation may not work");
93+
}
94+
}
8195

8296
configure_network()?;
8397

crates/muvm/src/guest/box64.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use std::fs::File;
2+
use std::io::Write;
3+
4+
use anyhow::{anyhow, Context, Result};
5+
6+
use crate::utils::env::find_in_path;
7+
8+
const BOX32_BINFMT_MISC_RULE: &str = ":BOX32:M:0:\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\\
9+
x00\\x00\\x00\\x00\\x00\\x02\\x00\\x03\\x00:\\xff\\xff\\\
10+
xff\\xff\\xff\\xfe\\xfe\\x00\\x00\\x00\\x00\\xff\\xff\\\
11+
xff\\xff\\xff\\xfe\\xff\\xff\\xff:${BOX64}:POCF";
12+
const BOX64_BINFMT_MISC_RULE: &str =
13+
":BOX64:M:0:\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\\
14+
x00\\x3e\\x00:\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\x00\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\\
15+
xff\\xfe\\xff\\xff\\xff:${BOX64}:POCF";
16+
17+
pub fn setup_box() -> Result<()> {
18+
let box64_path = find_in_path("box64").context("Failed to check existence of `box64`")?;
19+
let Some(box64_path) = box64_path else {
20+
return Err(anyhow!("Failed to find `box64` in PATH"));
21+
};
22+
let box64_path = box64_path
23+
.to_str()
24+
.context("Failed to process `box64` path as it contains invalid UTF-8")?;
25+
26+
let mut file = File::options()
27+
.write(true)
28+
.open("/proc/sys/fs/binfmt_misc/register")
29+
.context("Failed to open binfmt_misc/register for writing")?;
30+
31+
{
32+
let rule = BOX32_BINFMT_MISC_RULE.replace("${BOX64}", box64_path);
33+
file.write_all(rule.as_bytes())
34+
.context("Failed to register `Box32` binfmt_misc rule")?;
35+
}
36+
{
37+
let rule = BOX64_BINFMT_MISC_RULE.replace("${BOX64}", box64_path);
38+
file.write_all(rule.as_bytes())
39+
.context("Failed to register `Box64` binfmt_misc rule")?;
40+
}
41+
42+
Ok(())
43+
}

crates/muvm/src/guest/fex.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::fs::File;
22
use std::io::Write;
33

4-
use anyhow::{Context, Result};
4+
use anyhow::{anyhow, Context, Result};
55

66
use crate::utils::env::find_in_path;
77

@@ -18,7 +18,7 @@ pub fn setup_fex() -> Result<()> {
1818
let fex_interpreter_path =
1919
find_in_path("FEXInterpreter").context("Failed to check existence of `FEXInterpreter`")?;
2020
let Some(fex_interpreter_path) = fex_interpreter_path else {
21-
return Ok(());
21+
return Err(anyhow!("Failed to find `FEXInterpreter` in PATH"));
2222
};
2323
let fex_interpreter_path = fex_interpreter_path
2424
.to_str()

crates/muvm/src/guest/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod box64;
12
pub mod fex;
23
pub mod hidpipe;
34
pub mod mount;

crates/muvm/src/utils/launch.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use anyhow::anyhow;
12
use serde::{Deserialize, Serialize};
23
use std::collections::HashMap;
34
use std::path::PathBuf;
5+
use std::str::FromStr;
46

57
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
68
pub struct Launch {
@@ -12,6 +14,27 @@ pub struct Launch {
1214
pub privileged: bool,
1315
}
1416

17+
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
18+
pub enum Emulator {
19+
Box,
20+
Fex,
21+
}
22+
23+
impl FromStr for Emulator {
24+
type Err = anyhow::Error;
25+
26+
fn from_str(s: &str) -> Result<Self, Self::Err> {
27+
let v = s.to_lowercase();
28+
if v.starts_with("box") {
29+
Ok(Emulator::Box)
30+
} else if v.starts_with("fex") {
31+
Ok(Emulator::Fex)
32+
} else {
33+
Err(anyhow!("Invalid or unsupported emulator"))
34+
}
35+
}
36+
}
37+
1538
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
1639
pub struct GuestConfiguration {
1740
pub command: Launch,
@@ -20,6 +43,7 @@ pub struct GuestConfiguration {
2043
pub gid: u32,
2144
pub host_display: Option<String>,
2245
pub merged_rootfs: bool,
46+
pub emulator: Option<Emulator>,
2347
}
2448

2549
pub const PULSE_SOCKET: u32 = 3333;

0 commit comments

Comments
 (0)