pkg/bootparam: add bootparam pkg

This adds the bootparam package which can marshal and unmarshal the Linux
kernel command line into boot parameters and a rest section passed to
init.

This is a very quirky format, thus there is a fuzz testing harness
against the reference implementation from the kernel included to verify
correctness.

A set of weird edge cases is rejected by Unmarshal instead of parsing
to nonsensical data as the reference implementation does to save on
complexity in the parser.

Change-Id: I6debfa67e69ae8db4e0356f34ecb127ea27d18de
Reviewed-on: https://review.monogon.dev/c/monogon/+/1125
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/metropolis/pkg/bootparam/ref/ref.go b/metropolis/pkg/bootparam/ref/ref.go
new file mode 100644
index 0000000..9842ecd
--- /dev/null
+++ b/metropolis/pkg/bootparam/ref/ref.go
@@ -0,0 +1,140 @@
+// Package ref provides the reference implementation for kernel command line
+// parsing as present in the Linux kernel. This is a separate package and
+// not part of the bootparam tests because Go does not let you use cgo in
+// tests.
+package ref
+
+// Reference implementation from the kernel
+
+/*
+#include <stdlib.h>
+#include <ctype.h>
+#include <stddef.h>
+
+#define _U	0x01
+#define _L	0x02
+#define _D	0x04
+#define _C	0x08
+#define _P	0x10
+#define _S	0x20
+#define _X	0x40
+#define _SP	0x80
+
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
+#define kisspace(c)	((__ismask(c)&(_S)) != 0)
+
+const unsigned char _ctype[] = {
+_C,_C,_C,_C,_C,_C,_C,_C,
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,
+_C,_C,_C,_C,_C,_C,_C,_C,
+_C,_C,_C,_C,_C,_C,_C,_C,
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,
+_P,_P,_P,_P,_P,_P,_P,_P,
+_D,_D,_D,_D,_D,_D,_D,_D,
+_D,_D,_P,_P,_P,_P,_P,_P,
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,
+_U,_U,_U,_U,_U,_U,_U,_U,
+_U,_U,_U,_U,_U,_U,_U,_U,
+_U,_U,_U,_P,_P,_P,_P,_P,
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,
+_L,_L,_L,_L,_L,_L,_L,_L,
+_L,_L,_L,_L,_L,_L,_L,_L,
+_L,_L,_L,_P,_P,_P,_P,_C,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};
+
+
+
+char *skip_spaces(const char *str)
+{
+	while (kisspace(*str))
+		++str;
+	return (char *)str;
+}
+
+
+// * Parse a string to get a param value pair.
+// * You can use " around spaces, but can't escape ".
+// * Hyphens and underscores equivalent in parameter names.
+ char *next_arg(char *args, char **param, char **val)
+ {
+	 unsigned int i, equals = 0;
+	 int in_quote = 0, quoted = 0;
+
+	 if (*args == '"') {
+		 args++;
+		 in_quote = 1;
+		 quoted = 1;
+	 }
+
+	 for (i = 0; args[i]; i++) {
+		 if (kisspace(args[i]) && !in_quote)
+			 break;
+		 if (equals == 0) {
+			 if (args[i] == '=')
+				 equals = i;
+		 }
+		 if (args[i] == '"')
+			 in_quote = !in_quote;
+	 }
+
+	 *param = args;
+	 if (!equals)
+		 *val = NULL;
+	 else {
+		 args[equals] = '\0';
+		 *val = args + equals + 1;
+
+		 // Don't include quotes in value.
+		 if (**val == '"') {
+			 (*val)++;
+			 if (args[i-1] == '"')
+				 args[i-1] = '\0';
+		 }
+	 }
+	 if (quoted && i > 0 && args[i-1] == '"')
+		 args[i-1] = '\0';
+
+	 if (args[i]) {
+		 args[i] = '\0';
+		 args += i + 1;
+	 } else
+		 args += i;
+
+	 // Chew up trailing spaces.
+	 return skip_spaces(args);
+ }
+*/
+import "C"
+import (
+	"unsafe"
+
+	"source.monogon.dev/metropolis/pkg/bootparam"
+)
+
+func Parse(str string) (params bootparam.Params, rest string) {
+	cs := C.CString(bootparam.TrimLeftSpace(str))
+	csAllocPtr := cs
+	var param, val *C.char
+	for *cs != 0 {
+		var p bootparam.Param
+		cs = C.next_arg(cs, &param, &val)
+		p.Param = C.GoString(param)
+		if val != nil {
+			p.Value = C.GoString(val)
+		}
+		if p.Param == "--" {
+			rest = C.GoString(cs)
+			return
+		}
+		params = append(params, p)
+	}
+	C.free(unsafe.Pointer(csAllocPtr))
+	return
+}