Add `struct string` for strings parsed from FDT
Just a small wrapper around a char array to make things simpler.
Bug: 117551352
Change-Id: I0880ebbb81258830290ce49bf3772280551d3483
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 964509e..1b81c4a 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -52,6 +52,7 @@
"manifest.c",
"panic.c",
"spci_architected_message.c",
+ "string.c",
"vm.c",
]
@@ -158,6 +159,7 @@
"mm_test.cc",
"mpool_test.cc",
"spci_test.cc",
+ "string_test.cc",
]
sources += [ "layout_fake.c" ]
cflags_cc = [
diff --git a/src/boot_flow/linux.c b/src/boot_flow/linux.c
index f4f34e0..9d4d886 100644
--- a/src/boot_flow/linux.c
+++ b/src/boot_flow/linux.c
@@ -14,34 +14,13 @@
* limitations under the License.
*/
+#include "hf/check.h"
#include "hf/cpio.h"
#include "hf/dlog.h"
#include "hf/fdt_handler.h"
#include "hf/plat/boot_flow.h"
#include "hf/std.h"
-/**
- * Looks for a file in the given cpio archive. The file, if found, is returned
- * in the "it" argument.
- */
-static bool find_file(const struct memiter *cpio, const char *name,
- struct memiter *it)
-{
- const char *fname;
- const void *fcontents;
- size_t fsize;
- struct memiter iter = *cpio;
-
- while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
- if (!strcmp(fname, name)) {
- memiter_init(it, fcontents, fsize);
- return true;
- }
- }
-
- return false;
-}
-
/* Set by arch-specific boot-time hook. */
uintreg_t plat_boot_flow_fdt_addr;
@@ -76,9 +55,10 @@
struct boot_params_update *update,
struct memiter *cpio, struct mpool *ppool)
{
+ static struct string filename = STRING_INIT("initrd.img");
struct memiter primary_initrd;
- if (!find_file(cpio, "initrd.img", &primary_initrd)) {
+ if (!cpio_get_file(cpio, &filename, &primary_initrd)) {
dlog("Unable to find initrd.img\n");
return false;
}
diff --git a/src/cpio.c b/src/cpio.c
index 61a82d6..58626ec 100644
--- a/src/cpio.c
+++ b/src/cpio.c
@@ -41,8 +41,8 @@
* advances the iterator such that another call to this function would return
* the following file.
*/
-bool cpio_next(struct memiter *iter, const char **name, const void **contents,
- size_t *size)
+static bool cpio_next(struct memiter *iter, const char **name,
+ const void **contents, size_t *size)
{
size_t len;
struct memiter lit = *iter;
@@ -80,3 +80,25 @@
return true;
}
+
+/**
+ * Looks for a file in the given cpio archive. The file, if found, is returned
+ * in the "it" argument.
+ */
+bool cpio_get_file(const struct memiter *cpio, const struct string *name,
+ struct memiter *it)
+{
+ const char *fname;
+ const void *fcontents;
+ size_t fsize;
+ struct memiter iter = *cpio;
+
+ while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
+ if (!strcmp(fname, string_data(name))) {
+ memiter_init(it, fcontents, fsize);
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/load.c b/src/load.c
index fd4bff3..7138824 100644
--- a/src/load.c
+++ b/src/load.c
@@ -61,48 +61,20 @@
return true;
}
-/**
- * Looks for a file in the given cpio archive. The filename is not
- * null-terminated, so we use a memory iterator to represent it. The file, if
- * found, is returned in the "it" argument.
- */
-static bool find_file(const struct memiter *cpio,
- const struct memiter *filename, struct memiter *it)
-{
- const char *fname;
- const void *fcontents;
- size_t fsize;
- struct memiter iter = *cpio;
-
- while (cpio_next(&iter, &fname, &fcontents, &fsize)) {
- if (memiter_iseq(filename, fname)) {
- memiter_init(it, fcontents, fsize);
- return true;
- }
- }
-
- return false;
-}
-
static bool load_kernel(struct mm_stage1_locked stage1_locked, paddr_t begin,
paddr_t end, const struct manifest_vm *manifest_vm,
const struct memiter *cpio, struct mpool *ppool)
{
- struct memiter kernel_filename;
struct memiter kernel;
- memiter_init(&kernel_filename, manifest_vm->kernel_filename,
- strnlen_s(manifest_vm->kernel_filename,
- MANIFEST_MAX_STRING_LENGTH));
-
- if (memiter_size(&kernel_filename) == 0) {
+ if (string_is_empty(&manifest_vm->kernel_filename)) {
/* This signals the kernel has been preloaded. */
return true;
}
- if (!find_file(cpio, &kernel_filename, &kernel)) {
+ if (!cpio_get_file(cpio, &manifest_vm->kernel_filename, &kernel)) {
dlog("Could not find kernel file \"%s\".\n",
- manifest_vm->kernel_filename);
+ string_data(&manifest_vm->kernel_filename));
return false;
}
diff --git a/src/manifest.c b/src/manifest.c
index 80382ee..6d8b4ba 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -53,29 +53,9 @@
return ptr;
}
-static enum manifest_return_code extract_string(const char *data, uint32_t size,
- char *out, rsize_t out_sz)
-{
- /*
- * Require that the value contains exactly one NULL character and that
- * it is the last byte.
- */
- if (memchr(data, '\0', size) != &data[size - 1]) {
- return MANIFEST_ERROR_MALFORMED_STRING;
- }
-
- /* Check that the string fits into the buffer. */
- if (size > out_sz) {
- return MANIFEST_ERROR_STRING_TOO_LONG;
- }
-
- memcpy_s(out, out_sz, data, size);
- return MANIFEST_SUCCESS;
-}
-
static enum manifest_return_code read_string(const struct fdt_node *node,
- const char *property, char *out,
- rsize_t out_sz)
+ const char *property,
+ struct string *out)
{
const char *data;
uint32_t size;
@@ -84,26 +64,27 @@
return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
}
- return extract_string(data, size, out, out_sz);
+ switch (string_init(out, data, size)) {
+ case STRING_SUCCESS:
+ return MANIFEST_SUCCESS;
+ case STRING_ERROR_INVALID_INPUT:
+ return MANIFEST_ERROR_MALFORMED_STRING;
+ case STRING_ERROR_TOO_LONG:
+ return MANIFEST_ERROR_STRING_TOO_LONG;
+ }
}
static enum manifest_return_code read_optional_string(
- const struct fdt_node *node, const char *property, char *out,
- rsize_t out_sz)
+ const struct fdt_node *node, const char *property, struct string *out)
{
- const char *data;
- uint32_t size;
+ enum manifest_return_code ret;
- if (!fdt_read_property(node, property, &data, &size)) {
- if (out_sz < 1) {
- return MANIFEST_ERROR_STRING_TOO_LONG;
- }
-
- *out = '\0';
- return MANIFEST_SUCCESS;
+ ret = read_string(node, property, out);
+ if (ret == MANIFEST_ERROR_PROPERTY_NOT_FOUND) {
+ string_init_empty(out);
+ ret = MANIFEST_SUCCESS;
}
-
- return extract_string(data, size, out, out_sz);
+ return ret;
}
static enum manifest_return_code read_uint64(const struct fdt_node *node,
@@ -225,10 +206,9 @@
struct manifest_vm *vm,
spci_vm_id_t vm_id)
{
- TRY(read_string(node, "debug_name", vm->debug_name,
- sizeof(vm->debug_name)));
- TRY(read_optional_string(node, "kernel_filename", vm->kernel_filename,
- sizeof(vm->kernel_filename)));
+ TRY(read_string(node, "debug_name", &vm->debug_name));
+ TRY(read_optional_string(node, "kernel_filename",
+ &vm->kernel_filename));
if (vm_id != HF_PRIMARY_VM_ID) {
TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index 14eef49..d7690b4 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -304,8 +304,8 @@
{
const char last_valid[] = "1234567890123456789012345678901";
const char first_invalid[] = "12345678901234567890123456789012";
- static_assert(sizeof(last_valid) == MANIFEST_MAX_STRING_LENGTH);
- static_assert(sizeof(first_invalid) == MANIFEST_MAX_STRING_LENGTH + 1);
+ static_assert(sizeof(last_valid) == STRING_MAX_SIZE);
+ static_assert(sizeof(first_invalid) == STRING_MAX_SIZE + 1);
/* clang-format off */
return ManifestDtBuilder()
@@ -433,18 +433,19 @@
ASSERT_EQ(m.vm_count, 3);
vm = &m.vm[0];
- ASSERT_STREQ(vm->debug_name, "primary_vm");
- ASSERT_STREQ(vm->kernel_filename, "primary_kernel");
+ ASSERT_STREQ(string_data(&vm->debug_name), "primary_vm");
+ ASSERT_STREQ(string_data(&vm->kernel_filename), "primary_kernel");
vm = &m.vm[1];
- ASSERT_STREQ(vm->debug_name, "first_secondary_vm");
- ASSERT_STREQ(vm->kernel_filename, "");
+ ASSERT_STREQ(string_data(&vm->debug_name), "first_secondary_vm");
+ ASSERT_STREQ(string_data(&vm->kernel_filename), "");
ASSERT_EQ(vm->secondary.vcpu_count, 42);
ASSERT_EQ(vm->secondary.mem_size, 12345);
vm = &m.vm[2];
- ASSERT_STREQ(vm->debug_name, "second_secondary_vm");
- ASSERT_STREQ(vm->kernel_filename, "second_secondary_kernel");
+ ASSERT_STREQ(string_data(&vm->debug_name), "second_secondary_vm");
+ ASSERT_STREQ(string_data(&vm->kernel_filename),
+ "second_secondary_kernel");
ASSERT_EQ(vm->secondary.vcpu_count, 43);
ASSERT_EQ(vm->secondary.mem_size, 0x12345);
}
diff --git a/src/string.c b/src/string.c
new file mode 100644
index 0000000..4876a3f
--- /dev/null
+++ b/src/string.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hf/string.h"
+
+#include "hf/static_assert.h"
+#include "hf/std.h"
+
+void string_init_empty(struct string *str)
+{
+ static_assert(sizeof(str->data) >= 1, "String buffer too small");
+ str->data[0] = '\0';
+}
+
+/**
+ * Caller must guarantee that `data` points to a NULL-terminated string.
+ * The constructor checks that it fits into the internal buffer and copies
+ * the string there.
+ */
+enum string_return_code string_init(struct string *str, const char *data,
+ size_t size)
+{
+ /*
+ * Require that the value contains exactly one NULL character and that
+ * it is the last byte.
+ */
+ if (size < 1 || memchr(data, '\0', size) != &data[size - 1]) {
+ return STRING_ERROR_INVALID_INPUT;
+ }
+
+ if (size > sizeof(str->data)) {
+ return STRING_ERROR_TOO_LONG;
+ }
+
+ memcpy_s(str->data, sizeof(str->data), data, size);
+ return STRING_SUCCESS;
+}
+
+bool string_is_empty(const struct string *str)
+{
+ return str->data[0] == '\0';
+}
+
+const char *string_data(const struct string *str)
+{
+ return str->data;
+}
diff --git a/src/string_test.cc b/src/string_test.cc
new file mode 100644
index 0000000..f814636
--- /dev/null
+++ b/src/string_test.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+extern "C" {
+#include "hf/string.h"
+}
+
+namespace
+{
+TEST(string, valid)
+{
+ struct string str;
+ constexpr const char data[] = "test";
+
+ string_init_empty(&str);
+ ASSERT_TRUE(string_is_empty(&str));
+ ASSERT_STREQ(string_data(&str), "");
+
+ ASSERT_EQ(string_init(&str, data, sizeof(data)), STRING_SUCCESS);
+ ASSERT_FALSE(string_is_empty(&str));
+ ASSERT_STRNE(string_data(&str), "");
+ ASSERT_STREQ(string_data(&str), "test");
+}
+
+TEST(string, data_zero_size)
+{
+ struct string str;
+ constexpr const char data[] = "test";
+
+ ASSERT_EQ(string_init(&str, data, 0), STRING_ERROR_INVALID_INPUT);
+}
+
+TEST(string, data_no_null_terminator)
+{
+ struct string str;
+ constexpr const char data[] = {'t', 'e', 's', 't'};
+
+ ASSERT_EQ(string_init(&str, data, sizeof(data)),
+ STRING_ERROR_INVALID_INPUT);
+}
+
+TEST(string, data_two_null_terminators)
+{
+ struct string str;
+ constexpr const char data[] = {'\0', 't', 'e', 's', 't', '\0'};
+
+ ASSERT_EQ(string_init(&str, data, sizeof(data)),
+ STRING_ERROR_INVALID_INPUT);
+}
+
+} /* namespace */