summaryrefslogtreecommitdiff
path: root/kernel_call/platform_match.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel_call/platform_match.c')
-rwxr-xr-xkernel_call/platform_match.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/kernel_call/platform_match.c b/kernel_call/platform_match.c
new file mode 100755
index 0000000..8226be5
--- /dev/null
+++ b/kernel_call/platform_match.c
@@ -0,0 +1,346 @@
+/*
+ * platform_match.c
+ * Brandon Azad
+ */
+#include "platform_match.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "log.h"
+#include "platform.h"
+
+// ---- Matching helper functions -----------------------------------------------------------------
+
+// Advance past any spaces in a string.
+static void
+skip_spaces(const char **p) {
+ const char *pch = *p;
+ while (*pch == ' ') {
+ pch++;
+ }
+ *p = pch;
+}
+
+// ---- Device matching ---------------------------------------------------------------------------
+
+// A wildcard device version number.
+#define ANY ((unsigned)(-1))
+
+// Parse the version part of a device string.
+static bool
+parse_device_version_internal(const char *device_version, unsigned *major, unsigned *minor,
+ bool allow_wildcard, const char **end) {
+ const char *p = device_version;
+ // Parse the major version, which might be a wildcard.
+ unsigned maj = 0;
+ if (allow_wildcard && *p == '*') {
+ maj = ANY;
+ p++;
+ } else {
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ maj = maj * 10 + (ch - '0');
+ p++;
+ }
+ }
+ // Make sure we got the comma.
+ if (*p != ',') {
+ return false;
+ }
+ p++;
+ // Parse the minor version, which might be a wildcard.
+ unsigned min = 0;
+ if (allow_wildcard && *p == '*') {
+ min = ANY;
+ p++;
+ } else {
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ min = min * 10 + (ch - '0');
+ p++;
+ }
+ }
+ // If end is NULL, then require that we're at the end of the string. Else, return the end
+ // of what we parsed.
+ if (end == NULL) {
+ if (*p != 0) {
+ return false;
+ }
+ } else {
+ *end = p;
+ }
+ // Return the values.
+ *major = maj;
+ *minor = min;
+ return true;
+}
+
+// Parse a device name.
+static bool
+parse_device_internal(const char *device, char *device_type, unsigned *major, unsigned *minor,
+ bool allow_wildcard, const char **end) {
+ // "iPhone11,8" -> "iPhone", 11, 8; "iPad7,*" -> "iPad", 7, ANY
+ // If this device name doesn't have a comma then we don't know how to parse it. Just set
+ // the whole thing as the device type.
+ const char *comma = strchr(device, ',');
+ if (comma == NULL) {
+unknown:
+ strcpy(device_type, device);
+ *major = 0;
+ *minor = 0;
+ return false;
+ }
+ // Walk backwards from the comma to the start of the major version.
+ if (comma == device) {
+ goto unknown;
+ }
+ const char *p = comma;
+ for (;;) {
+ char ch = *(p - 1);
+ if (!(('0' <= ch && ch <= '9') || (allow_wildcard && ch == '*'))) {
+ break;
+ }
+ p--;
+ if (p == device) {
+ goto unknown;
+ }
+ }
+ if (p == comma) {
+ goto unknown;
+ }
+ size_t device_type_length = p - device;
+ // Parse the version numbers.
+ bool ok = parse_device_version_internal(p, major, minor, allow_wildcard, end);
+ if (!ok) {
+ goto unknown;
+ }
+ // Return the device_type string. This is last in case it's shared with the device string.
+ strncpy(device_type, device, device_type_length);
+ device_type[device_type_length] = 0;
+ return true;
+}
+
+// Parse a device name.
+static bool
+parse_device(const char *device, char *device_type, unsigned *major, unsigned *minor) {
+ return parse_device_internal(device, device_type, major, minor, false, NULL);
+}
+
+// Parse a device range string.
+static bool
+parse_device_range(const char *device, char *device_type,
+ unsigned *min_major, unsigned *min_minor,
+ unsigned *max_major, unsigned *max_minor,
+ const char **end) {
+ char dev_type[32];
+ const char *next = device;
+ // First parse a full device.
+ bool ok = parse_device_internal(next, dev_type, min_major, min_minor, true, &next);
+ if (!ok) {
+unknown:
+ strcpy(device_type, device);
+ *min_major = 0;
+ *min_minor = 0;
+ *max_major = 0;
+ *max_minor = 0;
+ return false;
+ }
+ // Optionally parse a separator and more versions.
+ if (*next == 0) {
+ *max_major = *min_major;
+ *max_minor = *min_minor;
+ } else if (*next == '-') {
+ next++;
+ ok = parse_device_version_internal(next, max_major, max_minor, true, &next);
+ if (!ok) {
+ goto unknown;
+ }
+ }
+ *end = next;
+ // Return the device_type.
+ strcpy(device_type, dev_type);
+ return true;
+}
+
+// Check if the given device number is numerically within range.
+static bool
+numerical_device_match(unsigned major, unsigned minor,
+ unsigned min_major, unsigned min_minor, unsigned max_major, unsigned max_minor) {
+ if (major < min_major && min_major != ANY) {
+ return false;
+ }
+ if ((major == min_major || min_major == ANY)
+ && minor < min_minor && min_minor != ANY) {
+ return false;
+ }
+ if (major > max_major && max_major != ANY) {
+ return false;
+ }
+ if ((major == max_major || max_major == ANY)
+ && minor > max_minor && max_minor != ANY) {
+ return false;
+ }
+ return true;
+}
+
+// Match a specific device against a device match list.
+static bool
+match_device(const char *device, const char *devices) {
+ if (devices == NULL || strcmp(devices, "*") == 0) {
+ return true;
+ }
+ // Parse this device.
+ char device_type[32];
+ unsigned major, minor;
+ parse_device(device, device_type, &major, &minor);
+ // Parse the match list.
+ const char *next = devices;
+ while (*next != 0) {
+ // Parse the next device range.
+ char match_device_type[32];
+ unsigned min_major, min_minor, max_major, max_minor;
+ parse_device_range(next, match_device_type, &min_major, &min_minor,
+ &max_major, &max_minor, &next);
+ if (*next != 0) {
+ skip_spaces(&next);
+ assert(*next == '|');
+ next++;
+ skip_spaces(&next);
+ assert(*next != 0);
+ }
+ // Check if this is a match.
+ if (strcmp(device_type, match_device_type) == 0
+ && numerical_device_match(major, minor,
+ min_major, min_minor, max_major, max_minor)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// ---- Build matching ----------------------------------------------------------------------------
+
+// Parse a build version string into a uint64_t. Maintains comparison order.
+static uint64_t
+parse_build_version(const char *build, const char **end) {
+ // 16A5288q -> [2 bytes][1 byte][3 bytes][1 byte]
+ const char *p = build;
+ // Parse out the major number.
+ uint64_t major = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ major = major * 10 + (ch - '0');
+ p++;
+ }
+ // Parse out the minor.
+ uint64_t minor = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < 'A' || 'Z' < ch) {
+ break;
+ }
+ minor = (minor << 8) + ch;
+ p++;
+ }
+ // Parse out the patch.
+ uint64_t patch = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < '0' || '9' < ch) {
+ break;
+ }
+ patch = patch * 10 + (ch - '0');
+ p++;
+ }
+ // Parse out the alpha.
+ uint64_t alpha = 0;
+ for (;;) {
+ char ch = *p;
+ if (ch < 'a' || 'z' < ch) {
+ break;
+ }
+ alpha = (alpha << 8) + ch;
+ p++;
+ }
+ // Construct the full build version.
+ if (end != NULL) {
+ *end = p;
+ }
+ return ((major << (8 * 5))
+ | (minor << (8 * 4))
+ | (patch << (8 * 1))
+ | (alpha << (8 * 0)));
+}
+
+// Parse a build version range string.
+static void
+parse_build_version_range(const char *builds, uint64_t *version_min, uint64_t *version_max) {
+ const char *next = builds;
+ uint64_t min, max;
+ // Parse the lower range.
+ if (*next == '*') {
+ min = 0;
+ next++;
+ } else {
+ min = parse_build_version(next, &next);
+ }
+ // Parse the upper range (if it exists).
+ if (*next == 0) {
+ assert(min != 0);
+ max = min;
+ } else {
+ skip_spaces(&next);
+ assert(*next == '-');
+ next++;
+ skip_spaces(&next);
+ if (*next == '*') {
+ max = (uint64_t)(-1);
+ next++;
+ } else {
+ max = parse_build_version(next, &next);
+ }
+ assert(*next == 0);
+ }
+ *version_min = min;
+ *version_max = max;
+}
+
+// Check if the given build version string matches the build range.
+static bool
+match_build(const char *build, const char *builds) {
+ if (builds == NULL || strcmp(builds, "*") == 0) {
+ return true;
+ }
+ uint64_t version = parse_build_version(build, NULL);
+ uint64_t version_min, version_max;
+ parse_build_version_range(builds, &version_min, &version_max);
+ return (version_min <= version && version <= version_max);
+}
+
+// ---- Public API --------------------------------------------------------------------------------
+
+bool
+platform_matches_device(const char *device_range) {
+ return match_device(platform.machine, device_range);
+}
+
+bool
+platform_matches_build(const char *build_range) {
+ return match_build(platform.osversion, build_range);
+}
+
+bool
+platform_matches(const char *device_range, const char *build_range) {
+ return platform_matches_device(device_range)
+ && platform_matches_build(build_range);
+}