client/hostinfo_unix: addapt for Apple Silicon M1 / arm64 CPU #2

- The native arm64 client tries to launch a separate helper program that
  is compiled only for the x86_64 architecture. If successful, this means
  that Rosetta2 is installed and the host can run x86_64 applications, so
  x86_64-apple-darwin is added as an alternative platform. The helper
  program 'detect_rosetta_cpu' gets the feature string for the emulated
  x86_64 CPU (e.g. SSE), and writes it to a file in the BOINC data
  directory. If  this file exists, this file is read in
  hostinfo_unix_mac() and the features of the meulated CPU are
  added to that of the native CPU.
This commit is contained in:
Bernd Machenschalk 2021-05-26 17:03:21 +02:00
parent de4e0f9823
commit 365a1bbc29
4 changed files with 156 additions and 6 deletions

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2020 University of California
// Copyright (C) 2021 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -53,6 +53,7 @@ extern int compareOSVersionTo(int toMajor, int toMinor);
#include "client_types.h"
#include "client_state.h"
#include "client_msgs.h"
#include "log_flags.h"
#include "project.h"
@ -72,6 +73,90 @@ void CLIENT_STATE::add_platform(const char* platform) {
}
#if defined (__APPLE__) && defined (__arm64__)
// detect a possibly emulated x86_64 CPU and its features on a Apple Silicon M1 Mac
//
int launch_child_process_to_detect_emulated_cpu() {
int prog;
char data_dir[MAXPATHLEN];
char execpath[MAXPATHLEN];
int retval = 0;
retval = boinc_delete_file(EMULATED_CPU_INFO_FILENAME);
if (retval) {
msg_printf(0, MSG_INFO,
"Failed to delete old %s. error code %d",
EMULATED_CPU_INFO_FILENAME, retval
);
} else {
for (;;) {
if (!boinc_file_exists(EMULATED_CPU_INFO_FILENAME)) break;
boinc_sleep(0.01);
}
}
// write the EMULATED_CPU_INFO into the BOINC data dir
boinc_getcwd(data_dir);
// the execuable should be in BOINC data dir
strncpy(execpath, data_dir, sizeof(execpath));
strncat(execpath, "/" EMULATED_CPU_INFO_EXECUTABLE, sizeof(execpath) - strlen(execpath) - 1);
if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO,
"[x86_64-M1] launching child process at %s",
execpath
);
}
int argc = 1;
char* const argv[2] = {
const_cast<char *>(execpath),
NULL
};
retval = run_program(
data_dir,
execpath,
argc,
argv,
0,
prog
);
if (retval) {
if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO,
"[x86_64-M1] run_program of child process returned error %d",
retval
);
}
return retval;
}
retval = get_exit_status(prog);
if (retval) {
char buf[200];
if (WIFEXITED(retval)) {
int code = WEXITSTATUS(retval);
snprintf(buf, sizeof(buf), "process exited with status %d: %s", code, strerror(code));
} else if (WIFSIGNALED(retval)) {
int sig = WTERMSIG(retval);
snprintf(buf, sizeof(buf), "process was terminated by signal %d", sig);
} else {
snprintf(buf, sizeof(buf), "unknown status %d", retval);
}
msg_printf(0, MSG_INFO,
"Emulated CPU detection failed: %s",
buf
);
}
return retval;
}
#endif
// determine the list of supported platforms.
//
void CLIENT_STATE::detect_platforms() {
@ -106,8 +191,9 @@ void CLIENT_STATE::detect_platforms() {
}
#elif defined(__arm64__)
add_platform("arm64-apple-darwin");
//TODO: Add test for Mac OS Version when Apple Rosetta emulator is removed
add_platform("x86_64-apple-darwin");
if (!launch_child_process_to_detect_emulated_cpu()) {
add_platform("x86_64-apple-darwin");
}
#else
#error Mac client now requires a 64-bit system
#endif

View File

@ -0,0 +1,44 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2021 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// This helper program is used to detect an emulated x86_64 CPU on Apples
// ARM64 CPUs (M1). It should be compiiled _only_ for x86_64 architecture.
// It writes the feature string of the meulated CPU to a file
// EMULATED_CPU_INFO_FILENAME in the current working directory.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include "hostinfo.h" // for P_FEATURES_SIZE
#include "filesys.h" // for boinc_fopen()
#include "file_names.h" // for EMULATED_CPU_INFO_FILENAME
int main () {
size_t len;
char features[P_FEATURES_SIZE];
FILE*fp;
len = sizeof(features);
sysctlbyname("machdep.cpu.features", features, &len, NULL, 0);
if ((fp = boinc_fopen(EMULATED_CPU_INFO_FILENAME, "w"))) {
fprintf(fp," %s\n", features);
fclose(fp);
}
return 0;
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// Copyright (C) 2021 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -71,7 +71,9 @@ extern void send_log_after(const char* filename, double t, MIOFILE& mf);
#define CLIENT_OPAQUE_FILENAME "client_opaque.txt"
#define CONFIG_FILE "cc_config.xml"
#define NVC_CONFIG_FILE "nvc_config.xml"
#define COPROC_INFO_FILENAME "coproc_info.xml"
#define COPROC_INFO_FILENAME "coproc_info.txt"
#define EMULATED_CPU_INFO_EXECUTABLE "detect_rosetta_cpu"
#define EMULATED_CPU_INFO_FILENAME "emulated_cpu_info.txt"
#define CPU_BENCHMARKS_FILE_NAME "cpu_benchmarks"
#define CREATE_ACCOUNT_FILENAME "create_account.xml"
#define DAILY_XFER_HISTORY_FILENAME "daily_xfer_history.xml"

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2020 University of California
// Copyright (C) 2021 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -892,6 +892,24 @@ static void get_cpu_info_mac(HOST_INFO& host) {
sysctlbyname("hw.optional.ucnormal_mem", &feature, &len, NULL, 0);
if (feature) feature_string += " ucnormal_mem";
// read features of the emulated CPU if there is a file containing these
char fpath[MAXPATHLEN];
boinc_getcwd(fpath);
strcat(fpath,"/");
strcat(fpath,EMULATED_CPU_INFO_FILENAME);
if (boinc_file_exists(fpath)) {
FILE* fp = boinc_fopen(fpath, "r");
if (fp) {
fgets(features, sizeof(features), fp);
feature_string += features;
fclose(fp);
} else if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO, "[x86_64-M1] couldn't open file %s", fpath);
}
} else if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO, "[x86_64-M1] didn't find file %s", fpath);
}
#endif // defined(__i386__) || defined(__x86_64__)
strncpy(features,feature_string.c_str(),sizeof(features));