Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,65 @@
# Rosetta Support for Linux guests

This fork adds a commandline option `-g <rosetta directory share tag>` to specify the name of the directory share tag
used to expose the Rosetta virtiofs directory share to the arm64 Linux guest. The tag can be any name you choose so long
as it conforms to virtiofs naming conventions.

The virtiofs directory share contains a single linux/arm64 executable named `rosetta`. This executable is an amd64
emulator which uses the native Rosetta functionality on MacOS to execute amd64 Linux binaries on the arm64 host
running Apple Silicon.

To actually make use of it in your arm64 Linux guest you must explicitly register a custom binfmt handler in the arm64
Linux guest by following the installation steps below.

Example usage of `vftool` with `-g ROSETTA`:
```shell
vftool/build/vftool -k vmlinux -i initrd -g ROSETTA -d root.img -d varlibdocker.img -m 8192 -p 6 -t 0 -a "root=/dev/vda" "$@"
```

## PREREQUISITES
The `binfmt-support` Ubuntu package must be installed in the arm64 Linux guest; this package contains the required
`update-binfmts` tool.
```shell
% sudo apt install binfmt-support
```

## INSTALLATION
Run these commands in your arm64 Linux guest on every boot:
```shell
% mkdir /tmp/rosetta
% sudo mount -t virtiofs ROSETTA /tmp/rosetta
% sudo /usr/sbin/update-binfmts --install rosetta /tmp/rosetta/rosetta \
--magic "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00" \
--mask "\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" \
--credentials yes --preserve no --fix-binary yes
```

The token `ROSETTA` in the `mount` command above MUST MATCH the tag name given to vftool `-g` commandline option.

You may see this error on first use of `update-binfmts`; ignore it and repeat the command and it should succeed.
```
update-binfmts: warning: unable to close /proc/sys/fs/binfmt_misc/register: No such file or directory
update-binfmts: warning: unable to enable binary format rosetta
update-binfmts: exiting due to previous errors
```

If you intend to use `docker` in the arm64 Linux guest to run containers with the `linux/amd64` platform, it's a good
idea to set this environment variable in your MacOS host for the MacOS `docker` client to use as a default:

```shell
export DOCKER_DEFAULT_PLATFORM=linux/amd64
```

This environment variable could also be specified in the `/lib/systemd/system/docker.service` systemd unit file on the
arm64 Linux guest by adding this line to the `[Service]` stanza:

```ini
Environment=DOCKER_DEFAULT_PLATFORM=linux/amd64
```

## Original README follows

---
# Virtualization.framework tool (vftool)

Here lies a _really minimalist_ and very noddy command-line wrapper to run VMs in the macOS Big Sur Virtualization.framework.
Expand Down Expand Up @@ -38,6 +100,7 @@ The following command-line arguments are supported:
-i <initrd path>
-d <disc image path>
-c <CDROM image path>
-s <shared dir path>[:<tag>[:{ro, rw}]]
-b <bridged ethernet interface>
-p <number of processors>
-m <memory size in MB>
Expand All @@ -50,6 +113,14 @@ The `-t` option permits the console to either use stdin/stdout (option `0`), or

Multiple disc images can be attached by using several `-d` or `-c` options. The discs are attached in the order they are given on the command line, which should then influence which device they appear as. For example, `-d foo -d bar -c blah` will create three virtio-blk devices, `/dev/vda`, `/dev/vdb`, `/dev/vdc` attached to _foo_, _bar_ and _blah_ respectively. Up to 8 discs can be attached.

Up to 8 shared directories may be attached with the `-s` option. They are each labelled with a tag;
if none is specified, the directory name is used. In they guest VM, they may be mounted using:
```
mount -t virtiofs <tag> <mount point>
```
Note that this requires virtiofs support, which is available in recent distributions such as Ubuntu
22.04. Directories may be specified as read only by adding the `:ro` suffix.

The kernel should be uncompressed. The initrd may be a gz. Disc images are raw/flat files (nothing fancy like qcow2).

When starting vftool, you will see output similar to:
Expand Down
125 changes: 113 additions & 12 deletions vftool/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@
#define VERSION "v0.3 10/12/2020"

#define MAX_DISCS 8

#define MAX_SHARES 8

struct disc_info {
NSString *path;
bool readOnly;
};

struct share_info {
NSString *path; // Path on host system
NSString *tag; // Tag on guest system
bool readOnly;
};

/* ******************************************************************** */
/* PTY management*/

Expand Down Expand Up @@ -96,7 +103,10 @@ static int createPty(bool waitForConnection)
NSString *initrd_path,
struct disc_info *dinfo,
unsigned int num_discs,
NSString *bridged_eth)
struct share_info *sinfo,
unsigned int num_shares,
NSString *bridged_eth,
NSString *rosetta_tag)
{
/* **************************************************************** */
/* Linux bootloader setup:
Expand Down Expand Up @@ -210,6 +220,51 @@ static int createPty(bool waitForConnection)

[conf setStorageDevices:discs];

// Shared directories
NSArray *shares = @[];

for (unsigned int i = 0; i < num_shares; i++) {
NSLog(@"+++ Attaching shared directory '%@' with tag '%@' with readOnly=%@ \n",
sinfo[i].path, sinfo[i].tag, sinfo[i].readOnly ? @"YES" : @"NO");
NSURL *shareURL = [NSURL fileURLWithPath:sinfo[i].path];
VZSharedDirectory *sharedDirectory = [[VZSharedDirectory alloc]
initWithURL: shareURL
readOnly: sinfo[i].readOnly];

VZSingleDirectoryShare *singleDirectoryShare = [[VZSingleDirectoryShare alloc]
initWithDirectory: sharedDirectory];

VZVirtioFileSystemDeviceConfiguration *share_conf = [[VZVirtioFileSystemDeviceConfiguration alloc]
initWithTag: sinfo[i].tag];
share_conf.share = singleDirectoryShare;
shares = [shares arrayByAddingObject:share_conf];
}
[conf setDirectorySharingDevices:shares];

// expose the Rosetta directory share:
NSString *tag = @"ROSETTA";

// expose the Rosetta directory share as value of rosetta_tag NSString:
if (rosetta_tag) {
NSError *validationError;
if (![VZVirtioFileSystemDeviceConfiguration validateTag:rosetta_tag error:&validationError]) {
// Handle validation error here.
NSLog(@"-- Configuration validation failure! %@\n", validationError);
return nil;
}

VZLinuxRosettaDirectoryShare *rosettaDirectoryShare = [[VZLinuxRosettaDirectoryShare alloc] initWithError:&validationError];
if (validationError) {
NSLog(@"-- Configuration validation failure! %@\n", validationError);
return nil;
}

VZVirtioFileSystemDeviceConfiguration *fileSystemDevice = [[VZVirtioFileSystemDeviceConfiguration alloc] initWithTag:rosetta_tag];
fileSystemDevice.share = rosettaDirectoryShare;

conf.directorySharingDevices = @[fileSystemDevice];
}

return conf;
}

Expand All @@ -219,17 +274,20 @@ static void usage(const char *me)
fprintf(stderr, "vftool version " VERSION "\n\n"
"Syntax:\n\t%s <options>\n\n"
"Options:\n"
"\t-k <kernel path> [REQUIRED]\n"
"\t-k <kernel path> [REQUIRED]\n"
"\t-a <kernel cmdline arguments>\n"
"\t-i <initrd path>\n"
"\t-d <disc image path>\n"
"\t-c <CDROM image path> (As -d, but read-only)\n"
"\t-b <bridged ethernet interface> (Default NAT)\n"
"\t-p <number of processors> (Default 1)\n"
"\t-m <memory size in MB> (Default 512MB)\n"
"\t-t <tty type> (0 = stdio, 1 = pty (default))\n"
"\t-c <CDROM image path> (As -d, but read-only)\n"
"\t-s <shared dir path>[:<tag>[:{ro, rw}]] (default tag: dir name,\n"
"\t default mode: read/write, at most %d shares) \n"
"\t-b <bridged ethernet interface> (Default NAT)\n"
"\t-p <number of processors> (Default 1)\n"
"\t-m <memory size in MB> (Default 512MB)\n"
"\t-t <tty type> (0 = stdio, 1 = pty (default))\n"
"\t-g <rosetta directory share tag>\n"
"\n\tSpecify multiple discs with multiple -d/-c options, in order (max %d)\n",
me, MAX_DISCS);
me, MAX_SHARES, MAX_DISCS);
}


Expand All @@ -242,15 +300,19 @@ int main(int argc, char *argv[])
NSString *disc_path = NULL;
NSString *cdrom_path = NULL;
NSString *eth_if = NULL;
NSString *rosetta_tag = NULL;
unsigned int cpus = 0;
unsigned int mem = 0;
unsigned int tty_type = 1;

struct disc_info dinfo[MAX_DISCS];
unsigned int num_discs = 0;

struct share_info sinfo[MAX_SHARES];
unsigned int num_shares = 0;

int ch;
while ((ch = getopt(argc, argv, "k:a:i:d:c:b:p:m:t:h")) != -1) {
while ((ch = getopt(argc, argv, "k:a:i:d:c:s:b:p:m:t:g:h")) != -1) {
switch (ch) {
case 'k':
kern_path = [NSString stringWithUTF8String:optarg];
Expand All @@ -272,6 +334,40 @@ int main(int argc, char *argv[])
dinfo[num_discs].readOnly = (ch == 'c');
num_discs++;
break;
case 's':
if (num_shares > MAX_SHARES-1) {
usage(argv[0]);
fprintf(stderr, "\nError: Too many shared directories specified (max %d)\n\n", MAX_SHARES);
return 1;
}

NSString *share_string = [NSString stringWithUTF8String:optarg];
NSArray *share_components = [share_string componentsSeparatedByString:@":"];

sinfo[num_shares].path = share_components[0];
sinfo[num_shares].tag = [share_components[0] lastPathComponent];
sinfo[num_shares].readOnly = false;

if ([share_components count] > 1){
sinfo[num_shares].tag = share_components[1];
}
if ([share_components count] > 2){
if ([share_components[2] isEqualToString:@"ro"]){
sinfo[num_shares].readOnly = true;
} else if (![share_components[2] isEqualToString:@"rw"]){
usage(argv[0]);
fprintf(stderr, "\nError: Third part of share argument must be one of 'rw' and 'ro', but '%s' was given.\n\n", [share_components[2] UTF8String]);
return 1;
}
}
if ([share_components count] > 3){
usage(argv[0]);
fprintf(stderr, "\nError: Share argument should consist of at most three components separated by ':', but %s was given.\n\n", [share_string UTF8String]);
return 1;
}
num_shares++;
break;

case 'b':
eth_if = [NSString stringWithUTF8String:optarg];
break;
Expand All @@ -289,6 +385,9 @@ int main(int argc, char *argv[])
return 1;
}
break;
case 'g':
rosetta_tag = [NSString stringWithUTF8String:optarg];
break;

case 'h':
default:
Expand Down Expand Up @@ -323,8 +422,10 @@ int main(int argc, char *argv[])
VZVirtualMachineConfiguration *conf = getVMConfig(mem, cpus, tty_type, cmdline,
kern_path, initrd_path,
dinfo, num_discs,
eth_if);

sinfo, num_shares,
eth_if,
rosetta_tag);

if (!conf) {
NSLog(@"Couldn't create configuration for VM.\n");
return 1;
Expand Down