From c36fde33b28a3e7317d0879fe83f4e1b7d426f46 Mon Sep 17 00:00:00 2001 From: Karl Stelzner Date: Fri, 22 Jul 2022 11:41:45 +0200 Subject: [PATCH 1/3] Add -s option for attaching shared directories --- README.md | 9 ++++++ vftool/main.m | 88 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 799164b..5b17472 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The following command-line arguments are supported: -i -d -c + -s [:[:{ro, rw}]] -b -p -m @@ -50,6 +51,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 +``` +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: diff --git a/vftool/main.m b/vftool/main.m index 2e1f528..e490896 100644 --- a/vftool/main.m +++ b/vftool/main.m @@ -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*/ @@ -96,6 +103,8 @@ static int createPty(bool waitForConnection) NSString *initrd_path, struct disc_info *dinfo, unsigned int num_discs, + struct share_info *sinfo, + unsigned int num_shares, NSString *bridged_eth) { /* **************************************************************** */ @@ -210,6 +219,27 @@ 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]; + return conf; } @@ -219,17 +249,19 @@ static void usage(const char *me) fprintf(stderr, "vftool version " VERSION "\n\n" "Syntax:\n\t%s \n\n" "Options:\n" - "\t-k [REQUIRED]\n" + "\t-k [REQUIRED]\n" "\t-a \n" "\t-i \n" "\t-d \n" - "\t-c (As -d, but read-only)\n" - "\t-b (Default NAT)\n" - "\t-p (Default 1)\n" - "\t-m (Default 512MB)\n" - "\t-t (0 = stdio, 1 = pty (default))\n" + "\t-c (As -d, but read-only)\n" + "\t-s [:[:{ro, rw}]] (default tag: dir name,\n" + "\t default mode: read/write, at most %d shares) \n" + "\t-b (Default NAT)\n" + "\t-p (Default 1)\n" + "\t-m (Default 512MB)\n" + "\t-t (0 = stdio, 1 = pty (default))\n" "\n\tSpecify multiple discs with multiple -d/-c options, in order (max %d)\n", - me, MAX_DISCS); + me, MAX_SHARES, MAX_DISCS); } @@ -249,8 +281,11 @@ int main(int argc, char *argv[]) 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:h")) != -1) { switch (ch) { case 'k': kern_path = [NSString stringWithUTF8String:optarg]; @@ -272,6 +307,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; @@ -323,6 +392,7 @@ int main(int argc, char *argv[]) VZVirtualMachineConfiguration *conf = getVMConfig(mem, cpus, tty_type, cmdline, kern_path, initrd_path, dinfo, num_discs, + sinfo, num_shares, eth_if); if (!conf) { From ee514c4856fc737748fd1ee40443d8e90fe29330 Mon Sep 17 00:00:00 2001 From: James Dunne Date: Tue, 11 Apr 2023 11:03:03 -0500 Subject: [PATCH 2/3] Add ROSETTA virtiofs directory share exposed to guest linux OS --- README.md | 36 ++++++++++++++++++++++++++++++++++++ vftool/main.m | 21 +++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/README.md b/README.md index 5b17472..30793c3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,39 @@ +# Rosetta Support + +This fork adds to the Linux guest a virtiofs directory share named `ROSETTA` which contains a single 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 (Mac M1, M2, etc.). + +To actually make use of it you must explicitly register a custom binfmt handler in the Linux guest by following the +steps below: + +## PREREQUISITES +The `binfmt-support` Ubuntu package must be installed. This package contains the `update-binfmts` required tool. +```shell +% sudo apt install binfmt-support +``` + +## INSTALLATION +```shell +% mkdir /tmp/rosetta +% sudo mount -t virtiofs ROSETTA /tmp/rosetta +% ls /tmp/rosetta 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 +``` + +You may see this error on first use of `update-binfmts` but 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 +``` + +## 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. diff --git a/vftool/main.m b/vftool/main.m index e490896..a89e399 100644 --- a/vftool/main.m +++ b/vftool/main.m @@ -240,6 +240,27 @@ static int createPty(bool waitForConnection) } [conf setDirectorySharingDevices:shares]; + // expose the Rosetta directory share: + NSString *tag = @"ROSETTA"; + + NSError *validationError; + if (![VZVirtioFileSystemDeviceConfiguration validateTag: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:tag]; + fileSystemDevice.share = rosettaDirectoryShare; + + conf.directorySharingDevices = @[fileSystemDevice]; + return conf; } From b5fcb2d6eabca6e0f5252f3715da934f4dc4de80 Mon Sep 17 00:00:00 2001 From: James Dunne Date: Wed, 12 Apr 2023 08:44:14 -0500 Subject: [PATCH 3/3] Added Rosetta support via optional -g commandline option to specify the virtiofs directory share tag. Clarified README --- README.md | 44 +++++++++++++++++++++++++++++++++++--------- vftool/main.m | 46 ++++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 30793c3..ee0d6f4 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,62 @@ -# Rosetta Support +# Rosetta Support for Linux guests -This fork adds to the Linux guest a virtiofs directory share named `ROSETTA` which contains a single 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 (Mac M1, M2, etc.). +This fork adds a commandline option `-g ` 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. -To actually make use of it you must explicitly register a custom binfmt handler in the Linux guest by following the -steps below: +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. This package contains the `update-binfmts` required tool. +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 -% ls /tmp/rosetta 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 ``` -You may see this error on first use of `update-binfmts` but ignore it and repeat the command and it should succeed. +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 --- diff --git a/vftool/main.m b/vftool/main.m index a89e399..9117e65 100644 --- a/vftool/main.m +++ b/vftool/main.m @@ -105,7 +105,8 @@ static int createPty(bool waitForConnection) unsigned int num_discs, struct share_info *sinfo, unsigned int num_shares, - NSString *bridged_eth) + NSString *bridged_eth, + NSString *rosetta_tag) { /* **************************************************************** */ /* Linux bootloader setup: @@ -243,23 +244,26 @@ static int createPty(bool waitForConnection) // expose the Rosetta directory share: NSString *tag = @"ROSETTA"; - NSError *validationError; - if (![VZVirtioFileSystemDeviceConfiguration validateTag:tag error:&validationError]) { - // Handle validation error here. - NSLog(@"-- Configuration validation failure! %@\n", validationError); - return nil; - } + // 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; - } + VZLinuxRosettaDirectoryShare *rosettaDirectoryShare = [[VZLinuxRosettaDirectoryShare alloc] initWithError:&validationError]; + if (validationError) { + NSLog(@"-- Configuration validation failure! %@\n", validationError); + return nil; + } - VZVirtioFileSystemDeviceConfiguration *fileSystemDevice = [[VZVirtioFileSystemDeviceConfiguration alloc] initWithTag:tag]; - fileSystemDevice.share = rosettaDirectoryShare; + VZVirtioFileSystemDeviceConfiguration *fileSystemDevice = [[VZVirtioFileSystemDeviceConfiguration alloc] initWithTag:rosetta_tag]; + fileSystemDevice.share = rosettaDirectoryShare; - conf.directorySharingDevices = @[fileSystemDevice]; + conf.directorySharingDevices = @[fileSystemDevice]; + } return conf; } @@ -281,6 +285,7 @@ static void usage(const char *me) "\t-p (Default 1)\n" "\t-m (Default 512MB)\n" "\t-t (0 = stdio, 1 = pty (default))\n" + "\t-g \n" "\n\tSpecify multiple discs with multiple -d/-c options, in order (max %d)\n", me, MAX_SHARES, MAX_DISCS); } @@ -295,6 +300,7 @@ 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; @@ -306,7 +312,7 @@ int main(int argc, char *argv[]) unsigned int num_shares = 0; int ch; - while ((ch = getopt(argc, argv, "k:a:i:d:c:s: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]; @@ -379,6 +385,9 @@ int main(int argc, char *argv[]) return 1; } break; + case 'g': + rosetta_tag = [NSString stringWithUTF8String:optarg]; + break; case 'h': default: @@ -414,8 +423,9 @@ int main(int argc, char *argv[]) kern_path, initrd_path, dinfo, num_discs, sinfo, num_shares, - eth_if); - + eth_if, + rosetta_tag); + if (!conf) { NSLog(@"Couldn't create configuration for VM.\n"); return 1;