* Re: [PATCH v5 01/12] vmlinux.lds.h: add linker section for KUnit test suites
From: Kees Cook @ 2020-06-26 21:20 UTC (permalink / raw)
To: Brendan Higgins
Cc: linux-doc, catalin.marinas, jcmvbkbc, will, paulus,
linux-kselftest, frowand.list, anton.ivanov, linux-arch, richard,
rppt, yzaikin, linux-xtensa, arnd, jdike, linux-um, linuxppc-dev,
davidgow, skhan, linux-arm-kernel, kunit-dev, chris, monstr,
sboyd, gregkh, linux-kernel, mcgrof, alan.maguire, akpm, logang
In-Reply-To: <20200626210917.358969-2-brendanhiggins@google.com>
On Fri, Jun 26, 2020 at 02:09:06PM -0700, Brendan Higgins wrote:
> Add a linker section where KUnit can put references to its test suites.
> This patch is the first step in transitioning to dispatching all KUnit
> tests from a centralized executor rather than having each as its own
> separate late_initcall.
>
> Co-developed-by: Iurii Zaikin <yzaikin@google.com>
> Signed-off-by: Iurii Zaikin <yzaikin@google.com>
> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
> Reviewed-by: Stephen Boyd <sboyd@kernel.org>
> ---
> include/asm-generic/vmlinux.lds.h | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index db600ef218d7d..4f9b036fc9616 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -881,6 +881,13 @@
> KEEP(*(.con_initcall.init)) \
> __con_initcall_end = .;
>
> +/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
Nit on naming:
> +#define KUNIT_TEST_SUITES \
I would call this KUNIT_TABLE to maintain the same names as other things
of this nature.
> + . = ALIGN(8); \
> + __kunit_suites_start = .; \
> + KEEP(*(.kunit_test_suites)) \
> + __kunit_suites_end = .;
> +
> #ifdef CONFIG_BLK_DEV_INITRD
> #define INIT_RAM_FS \
> . = ALIGN(4); \
> @@ -1056,6 +1063,7 @@
> INIT_CALLS \
> CON_INITCALL \
> INIT_RAM_FS \
> + KUNIT_TEST_SUITES \
> }
Nack: this must be in INIT_DATA, not in INIT_DATA_SECTION. Not all
architectures use the INIT_DATA_SECTION macro (e.g. arm64), but everything
uses INIT_DATA.
--
Kees Cook
^ permalink raw reply
* [PATCH v5 11/12] Documentation: Add kunit_shutdown to kernel-parameters.txt
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add kunit_shutdown, an option to specify that the kernel shutsdown after
running KUnit tests, to the kernel-parameters.txt documentation.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
Documentation/admin-guide/kernel-parameters.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index fb95fad81c79a..e7d5eb7249e7f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2183,6 +2183,14 @@
0: force disabled
1: force enabled
+ kunit_shutdown=[KERNEL UNIT TESTING FRAMEWORK] Shutdown kernel after
+ running built-in tests. Tests configured as modules will
+ not be run.
+ Default: (flag not present) don't shutdown
+ poweroff: poweroff the kernel after running tests
+ halt: halt the kernel after running tests
+ reboot: reboot the kernel after running tests
+
kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs.
Default is 0 (don't ignore, but inject #GP)
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 06/12] arch: xtensa: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section to xtensa where KUnit can put references to its
test suites. This patch is an early step in transitioning to dispatching
all KUnit tests from a centralized executor rather than having each as
its own separate late_initcall.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
arch/xtensa/kernel/vmlinux.lds.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S
index d23a6e38f0625..9aec4ef67d0b0 100644
--- a/arch/xtensa/kernel/vmlinux.lds.S
+++ b/arch/xtensa/kernel/vmlinux.lds.S
@@ -216,6 +216,10 @@ SECTIONS
INIT_RAM_FS
}
+ .kunit_test_suites : {
+ KUNIT_TEST_SUITES
+ }
+
PERCPU_SECTION(XCHAL_ICACHE_LINESIZE)
/* We need this dummy segment here */
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 07/12] kunit: test: create a single centralized executor for all tests
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
From: Alan Maguire <alan.maguire@oracle.com>
Add a centralized executor to dispatch tests rather than relying on
late_initcall to schedule each test suite separately. Centralized
execution is for built-in tests only; modules will execute tests when
loaded.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Co-developed-by: Iurii Zaikin <yzaikin@google.com>
Signed-off-by: Iurii Zaikin <yzaikin@google.com>
Co-developed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
include/kunit/test.h | 67 +++++++++++++++++++++++++++++---------------
lib/kunit/Makefile | 3 +-
lib/kunit/executor.c | 28 ++++++++++++++++++
lib/kunit/test.c | 2 +-
4 files changed, 76 insertions(+), 24 deletions(-)
create mode 100644 lib/kunit/executor.c
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 47e61e1d53370..f3e86c3953a2b 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -224,7 +224,7 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite);
unsigned int kunit_test_case_num(struct kunit_suite *suite,
struct kunit_case *test_case);
-int __kunit_test_suites_init(struct kunit_suite **suites);
+int __kunit_test_suites_init(struct kunit_suite * const * const suites);
void __kunit_test_suites_exit(struct kunit_suite **suites);
@@ -237,34 +237,57 @@ void __kunit_test_suites_exit(struct kunit_suite **suites);
* Registers @suites_list with the test framework. See &struct kunit_suite for
* more information.
*
- * When builtin, KUnit tests are all run as late_initcalls; this means
- * that they cannot test anything where tests must run at a different init
- * phase. One significant restriction resulting from this is that KUnit
- * cannot reliably test anything that is initialize in the late_init phase;
- * another is that KUnit is useless to test things that need to be run in
- * an earlier init phase.
- *
- * An alternative is to build the tests as a module. Because modules
- * do not support multiple late_initcall()s, we need to initialize an
- * array of suites for a module.
- *
- * TODO(brendanhiggins@google.com): Don't run all KUnit tests as
- * late_initcalls. I have some future work planned to dispatch all KUnit
- * tests from the same place, and at the very least to do so after
- * everything else is definitely initialized.
+ * If a test suite is built-in, module_init() gets translated into
+ * an initcall which we don't want as the idea is that for builtins
+ * the executor will manage execution. So ensure we do not define
+ * module_{init|exit} functions for the builtin case when registering
+ * suites via kunit_test_suites() below.
*/
-#define kunit_test_suites(suites_list...) \
- static struct kunit_suite *suites[] = {suites_list, NULL}; \
- static int kunit_test_suites_init(void) \
+#ifdef MODULE
+#define kunit_test_suites_for_module(__suites) \
+ static int __init kunit_test_suites_init(void) \
{ \
- return __kunit_test_suites_init(suites); \
+ return __kunit_test_suites_init(__suites); \
} \
- late_initcall(kunit_test_suites_init); \
+ module_init(kunit_test_suites_init); \
+ \
static void __exit kunit_test_suites_exit(void) \
{ \
- return __kunit_test_suites_exit(suites); \
+ return __kunit_test_suites_exit(__suites); \
} \
module_exit(kunit_test_suites_exit)
+#else
+#define kunit_test_suites_for_module(__suites)
+#endif /* MODULE */
+
+#define __kunit_test_suites(unique_array, unique_suites, ...) \
+ static struct kunit_suite *unique_array[] = { __VA_ARGS__, NULL }; \
+ kunit_test_suites_for_module(unique_array); \
+ static struct kunit_suite **unique_suites \
+ __used __section(.kunit_test_suites) = unique_array
+
+/**
+ * kunit_test_suites() - used to register one or more &struct kunit_suite
+ * with KUnit.
+ *
+ * @suites: a statically allocated list of &struct kunit_suite.
+ *
+ * Registers @suites with the test framework. See &struct kunit_suite for
+ * more information.
+ *
+ * When builtin, KUnit tests are all run via executor; this is done
+ * by placing the array of struct kunit_suite * in the .kunit_test_suites
+ * ELF section.
+ *
+ * An alternative is to build the tests as a module. Because modules do not
+ * support multiple initcall()s, we need to initialize an array of suites for a
+ * module.
+ *
+ */
+#define kunit_test_suites(...) \
+ __kunit_test_suites(__UNIQUE_ID(array), \
+ __UNIQUE_ID(suites), \
+ __VA_ARGS__)
#define kunit_test_suite(suite) kunit_test_suites(&suite)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 724b94311ca36..c49f4ffb6273a 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -3,7 +3,8 @@ obj-$(CONFIG_KUNIT) += kunit.o
kunit-objs += test.o \
string-stream.o \
assert.o \
- try-catch.o
+ try-catch.o \
+ executor.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
new file mode 100644
index 0000000000000..7015e7328dce7
--- /dev/null
+++ b/lib/kunit/executor.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <kunit/test.h>
+
+/*
+ * These symbols point to the .kunit_test_suites section and are defined in
+ * include/asm-generic/vmlinux.lds.h, and consequently must be extern.
+ */
+extern struct kunit_suite * const * const __kunit_suites_start[];
+extern struct kunit_suite * const * const __kunit_suites_end[];
+
+#if IS_BUILTIN(CONFIG_KUNIT)
+
+static int kunit_run_all_tests(void)
+{
+ struct kunit_suite * const * const *suites;
+
+ for (suites = __kunit_suites_start;
+ suites < __kunit_suites_end;
+ suites++)
+ __kunit_test_suites_init(*suites);
+
+ return 0;
+}
+
+late_initcall(kunit_run_all_tests);
+
+#endif /* IS_BUILTIN(CONFIG_KUNIT) */
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index ccb2ffad8dcfa..918dff400a9d7 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -380,7 +380,7 @@ static void kunit_init_suite(struct kunit_suite *suite)
kunit_debugfs_create_suite(suite);
}
-int __kunit_test_suites_init(struct kunit_suite **suites)
+int __kunit_test_suites_init(struct kunit_suite * const * const suites)
{
unsigned int i;
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 10/12] kunit: Add 'kunit_shutdown' option
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
From: David Gow <davidgow@google.com>
Add a new kernel command-line option, 'kunit_shutdown', which allows the
user to specify that the kernel poweroff, halt, or reboot after
completing all KUnit tests; this is very handy for running KUnit tests
on UML or a VM so that the UML/VM process exits cleanly immediately
after running all tests without needing a special initramfs.
Signed-off-by: David Gow <davidgow@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
lib/kunit/executor.c | 20 ++++++++++++++++++++
tools/testing/kunit/kunit_kernel.py | 2 +-
tools/testing/kunit/kunit_parser.py | 2 +-
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
index a95742a4ece73..38061d456afb2 100644
--- a/lib/kunit/executor.c
+++ b/lib/kunit/executor.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/reboot.h>
#include <kunit/test.h>
/*
@@ -11,6 +12,23 @@ extern struct kunit_suite * const * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT)
+static char *kunit_shutdown;
+core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
+
+static void kunit_handle_shutdown(void)
+{
+ if (!kunit_shutdown)
+ return;
+
+ if (!strcmp(kunit_shutdown, "poweroff"))
+ kernel_power_off();
+ else if (!strcmp(kunit_shutdown, "halt"))
+ kernel_halt();
+ else if (!strcmp(kunit_shutdown, "reboot"))
+ kernel_restart(NULL);
+
+}
+
static void kunit_print_tap_header(void)
{
struct kunit_suite * const * const *suites, * const *subsuite;
@@ -37,6 +55,8 @@ int kunit_run_all_tests(void)
suites++)
__kunit_test_suites_init(*suites);
+ kunit_handle_shutdown();
+
return 0;
}
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
index 63dbda2d029f6..d6a575f92317c 100644
--- a/tools/testing/kunit/kunit_kernel.py
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -172,7 +172,7 @@ class LinuxSourceTree(object):
return self.validate_config(build_dir)
def run_kernel(self, args=[], build_dir='', timeout=None):
- args.extend(['mem=1G'])
+ args.extend(['mem=1G', 'kunit_shutdown=halt'])
outfile = 'test.log'
self._ops.linux_bin(args, timeout, build_dir, outfile)
subprocess.call(['stty', 'sane'])
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index 6d6d94a0ee7db..a8998a5effaad 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -49,7 +49,7 @@ class TestStatus(Enum):
kunit_start_re = re.compile(r'TAP version [0-9]+$')
kunit_end_re = re.compile('(List of all partitions:|'
- 'Kernel panic - not syncing: VFS:)')
+ 'Kernel panic - not syncing: VFS:|reboot: System halted)')
def isolate_kunit_output(kernel_output):
started = False
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 09/12] kunit: test: add test plan to KUnit TAP format
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
TAP 14 allows an optional test plan to be emitted before the start of
the start of testing[1]; this is valuable because it makes it possible
for a test harness to detect whether the number of tests run matches the
number of tests expected to be run, ensuring that no tests silently
failed.
Link[1]: https://github.com/isaacs/testanything.github.io/blob/tap14/tap-version-14-specification.md#the-plan
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
lib/kunit/executor.c | 17 ++++
lib/kunit/test.c | 11 ---
tools/testing/kunit/kunit_parser.py | 76 +++++++++++++++---
.../test_is_test_passed-all_passed.log | Bin 1562 -> 1567 bytes
.../test_data/test_is_test_passed-crash.log | Bin 3016 -> 3021 bytes
.../test_data/test_is_test_passed-failure.log | Bin 1700 -> 1705 bytes
6 files changed, 80 insertions(+), 24 deletions(-)
diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
index 4aab7f70a88c3..a95742a4ece73 100644
--- a/lib/kunit/executor.c
+++ b/lib/kunit/executor.c
@@ -11,10 +11,27 @@ extern struct kunit_suite * const * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT)
+static void kunit_print_tap_header(void)
+{
+ struct kunit_suite * const * const *suites, * const *subsuite;
+ int num_of_suites = 0;
+
+ for (suites = __kunit_suites_start;
+ suites < __kunit_suites_end;
+ suites++)
+ for (subsuite = *suites; *subsuite != NULL; subsuite++)
+ num_of_suites++;
+
+ pr_info("TAP version 14\n");
+ pr_info("1..%d\n", num_of_suites);
+}
+
int kunit_run_all_tests(void)
{
struct kunit_suite * const * const *suites;
+ kunit_print_tap_header();
+
for (suites = __kunit_suites_start;
suites < __kunit_suites_end;
suites++)
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 918dff400a9d7..b1835ccb3fce2 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -19,16 +19,6 @@ static void kunit_set_failure(struct kunit *test)
WRITE_ONCE(test->success, false);
}
-static void kunit_print_tap_version(void)
-{
- static bool kunit_has_printed_tap_version;
-
- if (!kunit_has_printed_tap_version) {
- pr_info("TAP version 14\n");
- kunit_has_printed_tap_version = true;
- }
-}
-
/*
* Append formatted message to log, size of which is limited to
* KUNIT_LOG_SIZE bytes (including null terminating byte).
@@ -68,7 +58,6 @@ EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
static void kunit_print_subtest_start(struct kunit_suite *suite)
{
- kunit_print_tap_version();
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "# Subtest: %s",
suite->name);
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "1..%zd",
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index 64aac9dcd4314..6d6d94a0ee7db 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -45,10 +45,11 @@ class TestStatus(Enum):
FAILURE = auto()
TEST_CRASHED = auto()
NO_TESTS = auto()
+ FAILURE_TO_PARSE_TESTS = auto()
kunit_start_re = re.compile(r'TAP version [0-9]+$')
kunit_end_re = re.compile('(List of all partitions:|'
- 'Kernel panic - not syncing: VFS:|reboot: System halted)')
+ 'Kernel panic - not syncing: VFS:)')
def isolate_kunit_output(kernel_output):
started = False
@@ -109,7 +110,7 @@ OkNotOkResult = namedtuple('OkNotOkResult', ['is_ok','description', 'text'])
OK_NOT_OK_SUBTEST = re.compile(r'^[\s]+(ok|not ok) [0-9]+ - (.*)$')
-OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) [0-9]+ - (.*)$')
+OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) ([0-9]+) - (.*)$')
def parse_ok_not_ok_test_case(lines: List[str], test_case: TestCase) -> bool:
save_non_diagnositic(lines, test_case)
@@ -197,7 +198,9 @@ def max_status(left: TestStatus, right: TestStatus) -> TestStatus:
else:
return TestStatus.SUCCESS
-def parse_ok_not_ok_test_suite(lines: List[str], test_suite: TestSuite) -> bool:
+def parse_ok_not_ok_test_suite(lines: List[str],
+ test_suite: TestSuite,
+ expected_suite_index: int) -> bool:
consume_non_diagnositic(lines)
if not lines:
test_suite.status = TestStatus.TEST_CRASHED
@@ -210,6 +213,12 @@ def parse_ok_not_ok_test_suite(lines: List[str], test_suite: TestSuite) -> bool:
test_suite.status = TestStatus.SUCCESS
else:
test_suite.status = TestStatus.FAILURE
+ suite_index = int(match.group(2))
+ if suite_index != expected_suite_index:
+ print_with_timestamp(
+ red('[ERROR] ') + 'expected_suite_index ' +
+ str(expected_suite_index) + ', but got ' +
+ str(suite_index))
return True
else:
return False
@@ -222,7 +231,7 @@ def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
return max_status(max_test_case_status, test_suite.status)
-def parse_test_suite(lines: List[str]) -> TestSuite:
+def parse_test_suite(lines: List[str], expected_suite_index: int) -> TestSuite:
if not lines:
return None
consume_non_diagnositic(lines)
@@ -241,7 +250,7 @@ def parse_test_suite(lines: List[str]) -> TestSuite:
break
test_suite.cases.append(test_case)
expected_test_case_num -= 1
- if parse_ok_not_ok_test_suite(lines, test_suite):
+ if parse_ok_not_ok_test_suite(lines, test_suite, expected_suite_index):
test_suite.status = bubble_up_test_case_errors(test_suite)
return test_suite
elif not lines:
@@ -261,6 +270,17 @@ def parse_tap_header(lines: List[str]) -> bool:
else:
return False
+TEST_PLAN = re.compile(r'[0-9]+\.\.([0-9]+)')
+
+def parse_test_plan(lines: List[str]) -> int:
+ consume_non_diagnositic(lines)
+ match = TEST_PLAN.match(lines[0])
+ if match:
+ lines.pop(0)
+ return int(match.group(1))
+ else:
+ return None
+
def bubble_up_suite_errors(test_suite_list: List[TestSuite]) -> TestStatus:
return bubble_up_errors(lambda x: x.status, test_suite_list)
@@ -269,19 +289,34 @@ def parse_test_result(lines: List[str]) -> TestResult:
return TestResult(TestStatus.NO_TESTS, [], lines)
consume_non_diagnositic(lines)
if not parse_tap_header(lines):
- return None
+ return TestResult(TestStatus.NO_TESTS, [], lines)
+ expected_test_suite_num = parse_test_plan(lines)
+ if not expected_test_suite_num:
+ return TestResult(TestStatus.FAILURE_TO_PARSE_TESTS, [], lines)
test_suites = []
- test_suite = parse_test_suite(lines)
- while test_suite:
- test_suites.append(test_suite)
- test_suite = parse_test_suite(lines)
- return TestResult(bubble_up_suite_errors(test_suites), test_suites, lines)
+ for i in range(1, expected_test_suite_num + 1):
+ test_suite = parse_test_suite(lines, i)
+ if test_suite:
+ test_suites.append(test_suite)
+ else:
+ print_with_timestamp(
+ red('[ERROR] ') + ' expected ' +
+ str(expected_test_suite_num) +
+ ' test suites, but got ' + str(i - 2))
+ break
+ test_suite = parse_test_suite(lines, -1)
+ if test_suite:
+ print_with_timestamp(red('[ERROR] ') +
+ 'got unexpected test suite: ' + test_suite.name)
+ if test_suites:
+ return TestResult(bubble_up_suite_errors(test_suites), test_suites, lines)
+ else:
+ return TestResult(TestStatus.NO_TESTS, [], lines)
-def parse_run_tests(kernel_output) -> TestResult:
+def print_and_count_results(test_result: TestResult) -> None:
total_tests = 0
failed_tests = 0
crashed_tests = 0
- test_result = parse_test_result(list(isolate_kunit_output(kernel_output)))
for test_suite in test_result.suites:
if test_suite.status == TestStatus.SUCCESS:
print_suite_divider(green('[PASSED] ') + test_suite.name)
@@ -303,6 +338,21 @@ def parse_run_tests(kernel_output) -> TestResult:
print_with_timestamp(red('[FAILED] ') + test_case.name)
print_log(map(yellow, test_case.log))
print_with_timestamp('')
+ return total_tests, failed_tests, crashed_tests
+
+def parse_run_tests(kernel_output) -> TestResult:
+ total_tests = 0
+ failed_tests = 0
+ crashed_tests = 0
+ test_result = parse_test_result(list(isolate_kunit_output(kernel_output)))
+ if test_result.status == TestStatus.NO_TESTS:
+ print(red('[ERROR] ') + yellow('no tests run!'))
+ elif test_result.status == TestStatus.FAILURE_TO_PARSE_TESTS:
+ print(red('[ERROR] ') + yellow('could not parse test results!'))
+ else:
+ (total_tests,
+ failed_tests,
+ crashed_tests) = print_and_count_results(test_result)
print_with_timestamp(DIVIDER)
fmt = green if test_result.status == TestStatus.SUCCESS else red
print_with_timestamp(
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-all_passed.log b/tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
index 62ebc0288355c4b122ccc18ae2505f971efa57bc..bc0dc8fe35b760b1feb74ec419818dbfae1adb5c 100644
GIT binary patch
delta 28
jcmbQmGoME|#4$jjEVZaOGe1wk(1goSPtRy09}gP<dC~`u
delta 23
ecmbQwGmD2W#4$jjEVZaOGe1wk&}5@94;uhhkp{*9
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-crash.log b/tools/testing/kunit/test_data/test_is_test_passed-crash.log
index 0b249870c8be417a5865bd40a24c8597bb7f5ab1..4d97f6708c4a5ad5bb2ac879e12afca6e816d83d 100644
GIT binary patch
delta 15
WcmX>hepY;fFN>j`p3z318g2k9Uj*m?
delta 10
RcmX>renNbL@5Z2NZU7lr1S$Xk
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure.log b/tools/testing/kunit/test_data/test_is_test_passed-failure.log
index 9e89d32d5667a59d137f8adacf3a88fdb7f88baf..7a416497e3bec044eefc1535f7d84ee85703ba97 100644
GIT binary patch
delta 28
jcmZ3&yOLKp#4$jjEVZaOGe1wk(1goSPtRy0-!wJ=eKrU$
delta 23
ecmZ3<yM&i7#4$jjEVZaOGe1wk&}5_VG&TTPhX-Z=
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* Re: [PATCH v4 00/11] kunit: create a centralized executor to dispatch all KUnit tests
From: Brendan Higgins @ 2020-06-26 21:10 UTC (permalink / raw)
To: David Gow
Cc: open list:DOCUMENTATION, catalin.marinas, jcmvbkbc, will, paulus,
open list:KERNEL SELFTEST FRAMEWORK, Frank Rowand, Anton Ivanov,
linux-arch, Richard Weinberger, rppt, Iurii Zaikin, linux-xtensa,
Kees Cook, Arnd Bergmann, Jeff Dike, linux-um, linuxppc-dev,
Shuah Khan, Linux ARM, KUnit Development, chris, monstr,
Stephen Boyd, Greg Kroah-Hartman, Linux Kernel Mailing List,
Luis Chamberlain, Alan Maguire, Andrew Morton, Logan Gunthorpe
In-Reply-To: <CABVgOSnkYfXZ1YELsXAjA0GzCQT9YWO8x+Tssw_+avkDdBB4aw@mail.gmail.com>
On Wed, Jun 24, 2020 at 6:47 PM David Gow <davidgow@google.com> wrote:
>
> Glad this is back out there: a couple of minor nitpicks below:
>
> On Thu, Jun 25, 2020 at 4:58 AM Brendan Higgins
> <brendanhiggins@google.com> wrote:
> >
> > ## TL;DR
> >
> > This patchset adds a centralized executor to dispatch tests rather than
> > relying on late_initcall to schedule each test suite separately along
> > with a couple of new features that depend on it.
> >
> > Also, sorry for the extreme delay in getting this out. Part of the delay
> > came from finding that there were actually several architectures that
> > the previous revision of this patchset didn't work on, so I went through
> > and attempted to test this patchset on every architecture - more on that
> > later.
> >
> > ## What am I trying to do?
> >
> > Conceptually, I am trying to provide a mechanism by which test suites
> > can be grouped together so that they can be reasoned about collectively.
> > The last two of three patches in this series add features which depend
> > on this:
> >
> > PATCH 8/11 Prints out a test plan[1] right before KUnit tests are run;
> > this is valuable because it makes it possible for a test
> > harness to detect whether the number of tests run matches the
> > number of tests expected to be run, ensuring that no tests
> > silently failed. The test plan includes a count of tests that
> > will run. With the centralized executor, the tests are
> > located in a single data structure and thus can be counted.
> >
>
> This appears to actually be patch 9/11.
>
> > PATCH 9/11 Add a new kernel command-line option which allows the user to
> > specify that the kernel poweroff, halt, or reboot after
> > completing all KUnit tests; this is very handy for running
> > KUnit tests on UML or a VM so that the UML/VM process exits
> > cleanly immediately after running all tests without needing a
> > special initramfs. The centralized executor provides a
> > definitive point when all tests have completed and the
> > poweroff, halt, or reboot could occur.
>
> This seems to have been merged into the above patch (9/11).
Whoops, good catch.
Fixed in v5!
> > In addition, by dispatching tests from a single location, we can
> > guarantee that all KUnit tests run after late_init is complete, which
> > was a concern during the initial KUnit patchset review (this has not
> > been a problem in practice, but resolving with certainty is nevertheless
> > desirable).
> >
> > Other use cases for this exist, but the above features should provide an
> > idea of the value that this could provide.
> >
> > ## Changes since last revision:
> > - On the last revision I got some messages from 0day that showed that
> > this patchset didn't work on several architectures, one issue that
> > this patchset addresses is that we were aligning both memory segments
> > as well as structures in the segments to specific byte boundaries
> > which was incorrect.
> > - The issue mentioned above also caused me to test on additional
> > architectures which revealed that some architectures other than UML
> > do not use the default init linker section macro that most
> > architectures use. There are now several new patches (2, 3, 4, and
> > 6).
> > - Fixed a formatting consistency issue in the kernel params
> > documentation patch (9/9).
> > - Add a brief blurb on how and when the kunit_test_suite macro works.
> >
> > ## Remaining work to be done:
> >
> > The only architecture for which I was able to get a compiler, but was
> > apparently unable to get KUnit into a section that the executor to see
> > was m68k - not sure why.
> >
> > Alan Maguire (1):
> > kunit: test: create a single centralized executor for all tests
> >
> > Brendan Higgins (10):
> > vmlinux.lds.h: add linker section for KUnit test suites
> > arch: arm64: add linker section for KUnit test suites
> > arch: microblaze: add linker section for KUnit test suites
> > arch: powerpc: add linker section for KUnit test suites
> > arch: um: add linker section for KUnit test suites
> > arch: xtensa: add linker section for KUnit test suites
> > init: main: add KUnit to kernel init
> > kunit: test: add test plan to KUnit TAP format
> > Documentation: Add kunit_shutdown to kernel-parameters.txt
> > Documentation: kunit: add a brief blurb about kunit_test_suite
> >
> > .../admin-guide/kernel-parameters.txt | 8 ++
> > Documentation/dev-tools/kunit/usage.rst | 5 ++
> > arch/arm64/kernel/vmlinux.lds.S | 3 +
> > arch/microblaze/kernel/vmlinux.lds.S | 4 +
> > arch/powerpc/kernel/vmlinux.lds.S | 4 +
> > arch/um/include/asm/common.lds.S | 4 +
> > arch/xtensa/kernel/vmlinux.lds.S | 4 +
> > include/asm-generic/vmlinux.lds.h | 8 ++
> > include/kunit/test.h | 73 ++++++++++++-----
> > init/main.c | 4 +
> > lib/kunit/Makefile | 3 +-
> > lib/kunit/executor.c | 63 +++++++++++++++
> > lib/kunit/test.c | 13 +--
> > tools/testing/kunit/kunit_kernel.py | 2 +-
> > tools/testing/kunit/kunit_parser.py | 74 +++++++++++++++---
> > .../test_is_test_passed-all_passed.log | Bin 1562 -> 1567 bytes
> > .../test_data/test_is_test_passed-crash.log | Bin 3016 -> 3021 bytes
> > .../test_data/test_is_test_passed-failure.log | Bin 1700 -> 1705 bytes
> > 18 files changed, 226 insertions(+), 46 deletions(-)
> > create mode 100644 lib/kunit/executor.c
> >
> >
> > base-commit: 4333a9b0b67bb4e8bcd91bdd80da80b0ec151162
> > prerequisite-patch-id: 2d4b5aa9fa8ada9ae04c8584b47c299a822b9455
> > prerequisite-patch-id: 582b6d9d28ce4b71628890ec832df6522ca68de0
> >
> > These patches are available for download with dependencies here:
> >
> > https://kunit-review.googlesource.com/c/linux/+/3829
> >
> > [1] https://github.com/isaacs/testanything.github.io/blob/tap14/tap-version-14-specification.md#the-plan
> > [2] https://patchwork.kernel.org/patch/11383635/
> >
> > --
> > 2.27.0.212.ge8ba1cc988-goog
> >
^ permalink raw reply
* [PATCH v5 12/12] Documentation: kunit: add a brief blurb about kunit_test_suite
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a brief blurb saying how and when the kunit_test_suite() macro
works to the usage documentation.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
Documentation/dev-tools/kunit/usage.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 3c3fe8b5feccf..961d3ea3ca19a 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -211,6 +211,11 @@ KUnit test framework.
.. note::
A test case will only be run if it is associated with a test suite.
+``kunit_test_suite(...)`` is a macro which tells the linker to put the specified
+test suite in a special linker section so that it can be run by KUnit either
+after late_init, or when the test module is loaded (depending on whether the
+test was built in or not).
+
For more information on these types of things see the :doc:`api/test`.
Isolating Behavior
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 08/12] init: main: add KUnit to kernel init
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Remove KUnit from init calls entirely, instead call directly from
kernel_init().
Co-developed-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
include/kunit/test.h | 9 +++++++++
init/main.c | 4 ++++
lib/kunit/executor.c | 4 +---
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h
index f3e86c3953a2b..795039d9f1917 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -228,6 +228,15 @@ int __kunit_test_suites_init(struct kunit_suite * const * const suites);
void __kunit_test_suites_exit(struct kunit_suite **suites);
+#if IS_BUILTIN(CONFIG_KUNIT)
+int kunit_run_all_tests(void);
+#else
+static inline int kunit_run_all_tests(void)
+{
+ return 0;
+}
+#endif /* IS_BUILTIN(CONFIG_KUNIT) */
+
/**
* kunit_test_suites() - used to register one or more &struct kunit_suite
* with KUnit.
diff --git a/init/main.c b/init/main.c
index 0ead83e86b5aa..d3101d8874dea 100644
--- a/init/main.c
+++ b/init/main.c
@@ -106,6 +106,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/initcall.h>
+#include <kunit/test.h>
+
static int kernel_init(void *);
extern void init_IRQ(void);
@@ -1504,6 +1506,8 @@ static noinline void __init kernel_init_freeable(void)
do_basic_setup();
+ kunit_run_all_tests();
+
console_on_rootfs();
/*
diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
index 7015e7328dce7..4aab7f70a88c3 100644
--- a/lib/kunit/executor.c
+++ b/lib/kunit/executor.c
@@ -11,7 +11,7 @@ extern struct kunit_suite * const * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT)
-static int kunit_run_all_tests(void)
+int kunit_run_all_tests(void)
{
struct kunit_suite * const * const *suites;
@@ -23,6 +23,4 @@ static int kunit_run_all_tests(void)
return 0;
}
-late_initcall(kunit_run_all_tests);
-
#endif /* IS_BUILTIN(CONFIG_KUNIT) */
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 05/12] arch: um: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section to UML where KUnit can put references to its test
suites. This patch is an early step in transitioning to dispatching all
KUnit tests from a centralized executor rather than having each as its
own separate late_initcall.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
arch/um/include/asm/common.lds.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S
index eca6c452a41bd..9a9c97f45694c 100644
--- a/arch/um/include/asm/common.lds.S
+++ b/arch/um/include/asm/common.lds.S
@@ -52,6 +52,10 @@
CON_INITCALL
}
+ .kunit_test_suites : {
+ KUNIT_TEST_SUITES
+ }
+
.exitcall : {
__exitcall_begin = .;
*(.exitcall.exit)
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 04/12] arch: powerpc: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section to powerpc where KUnit can put references to its test
suites. This patch is an early step in transitioning to dispatching all
KUnit tests from a centralized executor rather than having each as its
own separate late_initcall.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
arch/powerpc/kernel/vmlinux.lds.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 326e113d2e456..0cc97dbfde0ad 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -202,6 +202,10 @@ SECTIONS
CON_INITCALL
}
+ .kunit_test_suites : {
+ KUNIT_TEST_SUITES
+ }
+
. = ALIGN(8);
__ftr_fixup : AT(ADDR(__ftr_fixup) - LOAD_OFFSET) {
__start___ftr_fixup = .;
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 03/12] arch: microblaze: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section to microblaze where KUnit can put references to its
test suites. This patch is an early step in transitioning to dispatching
all KUnit tests from a centralized executor rather than having each as
its own separate late_initcall.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
arch/microblaze/kernel/vmlinux.lds.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S
index df07b3d06cd6b..4fc32f8979a60 100644
--- a/arch/microblaze/kernel/vmlinux.lds.S
+++ b/arch/microblaze/kernel/vmlinux.lds.S
@@ -128,6 +128,10 @@ SECTIONS {
__init_end = .;
+ .kunit_test_suites : {
+ KUNIT_TEST_SUITES
+ }
+
.bss ALIGN (PAGE_SIZE) : AT(ADDR(.bss) - LOAD_OFFSET) {
/* page aligned when MMU used */
__bss_start = . ;
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 02/12] arch: arm64: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section to arm64 where KUnit can put references to its test
suites. This patch is an early step in transitioning to dispatching all
KUnit tests from a centralized executor rather than having each as its
own separate late_initcall.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
arch/arm64/kernel/vmlinux.lds.S | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 6827da7f3aa54..a1cae9cc655d7 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -181,6 +181,9 @@ SECTIONS
INIT_RAM_FS
*(.init.rodata.* .init.bss) /* from the EFI stub */
}
+ .kunit_test_suites : {
+ KUNIT_TEST_SUITES
+ }
.exit.data : {
EXIT_DATA
}
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 01/12] vmlinux.lds.h: add linker section for KUnit test suites
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
In-Reply-To: <20200626210917.358969-1-brendanhiggins@google.com>
Add a linker section where KUnit can put references to its test suites.
This patch is the first step in transitioning to dispatching all KUnit
tests from a centralized executor rather than having each as its own
separate late_initcall.
Co-developed-by: Iurii Zaikin <yzaikin@google.com>
Signed-off-by: Iurii Zaikin <yzaikin@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
---
include/asm-generic/vmlinux.lds.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index db600ef218d7d..4f9b036fc9616 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -881,6 +881,13 @@
KEEP(*(.con_initcall.init)) \
__con_initcall_end = .;
+/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
+#define KUNIT_TEST_SUITES \
+ . = ALIGN(8); \
+ __kunit_suites_start = .; \
+ KEEP(*(.kunit_test_suites)) \
+ __kunit_suites_end = .;
+
#ifdef CONFIG_BLK_DEV_INITRD
#define INIT_RAM_FS \
. = ALIGN(4); \
@@ -1056,6 +1063,7 @@
INIT_CALLS \
CON_INITCALL \
INIT_RAM_FS \
+ KUNIT_TEST_SUITES \
}
#define BSS_SECTION(sbss_align, bss_align, stop_align) \
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply related
* [PATCH v5 00/12] kunit: create a centralized executor to dispatch all KUnit tests
From: Brendan Higgins @ 2020-06-26 21:09 UTC (permalink / raw)
To: jdike, richard, anton.ivanov, arnd, keescook, skhan, alan.maguire,
yzaikin, davidgow, akpm, rppt, frowand.list, catalin.marinas,
will, monstr, mpe, benh, paulus, chris, jcmvbkbc
Cc: linux-arch, linux-xtensa, linux-doc, sboyd, gregkh, linuxppc-dev,
linux-um, linux-kernel, Brendan Higgins, mcgrof, linux-kselftest,
logang, linux-arm-kernel, kunit-dev
## TL;DR
This patchset adds a centralized executor to dispatch tests rather than
relying on late_initcall to schedule each test suite separately along
with a couple of new features that depend on it.
## What am I trying to do?
Conceptually, I am trying to provide a mechanism by which test suites
can be grouped together so that they can be reasoned about collectively.
The last two of three patches in this series add features which depend
on this:
PATCH 09/12 Prints out a test plan[1] right before KUnit tests are run;
this is valuable because it makes it possible for a test
harness to detect whether the number of tests run matches
the number of tests expected to be run, ensuring that no
tests silently failed. The test plan includes a count of
tests that will run. With the centralized executor, the
tests are located in a single data structure and thus can be
counted.
PATCH 10/12 Add a new kernel command-line option which allows the user
to specify that the kernel poweroff, halt, or reboot after
completing all KUnit tests; this is very handy for running
KUnit tests on UML or a VM so that the UML/VM process exits
cleanly immediately after running all tests without needing
a special initramfs. The centralized executor provides a
definitive point when all tests have completed and the
poweroff, halt, or reboot could occur.
In addition, by dispatching tests from a single location, we can
guarantee that all KUnit tests run after late_init is complete, which
was a concern during the initial KUnit patchset review (this has not
been a problem in practice, but resolving with certainty is nevertheless
desirable).
Other use cases for this exist, but the above features should provide an
idea of the value that this could provide.
## Changes since last revision:
- Fixed a compilation error in the centralized executor patch (07/12).
I had forgotten to test the patches when building as modules. I
verified that works now.
- I accidentally merged patches 09/12 and 10/12 in the previous
revision (v4), and made them separate patches again.
## Changes since v3:
- On the last revision I got some messages from 0day that showed that
this patchset didn't work on several architectures, one issue that
this patchset addresses is that we were aligning both memory segments
as well as structures in the segments to specific byte boundaries
which was incorrect.
- The issue mentioned above also caused me to test on additional
architectures which revealed that some architectures other than UML
do not use the default init linker section macro that most
architectures use. There are now several new patches (2, 3, 4, and
6).
- Fixed a formatting consistency issue in the kernel params
documentation patch (11/12).
- Add a brief blurb on how and when the kunit_test_suite macro works.
## Remaining work to be done:
The only architecture for which I was able to get a compiler, but was
apparently unable to get KUnit into a section that the executor to see
was m68k - not sure why.
Alan Maguire (1):
kunit: test: create a single centralized executor for all tests
Brendan Higgins (10):
vmlinux.lds.h: add linker section for KUnit test suites
arch: arm64: add linker section for KUnit test suites
arch: microblaze: add linker section for KUnit test suites
arch: powerpc: add linker section for KUnit test suites
arch: um: add linker section for KUnit test suites
arch: xtensa: add linker section for KUnit test suites
init: main: add KUnit to kernel init
kunit: test: add test plan to KUnit TAP format
Documentation: Add kunit_shutdown to kernel-parameters.txt
Documentation: kunit: add a brief blurb about kunit_test_suite
David Gow (1):
kunit: Add 'kunit_shutdown' option
.../admin-guide/kernel-parameters.txt | 8 ++
Documentation/dev-tools/kunit/usage.rst | 5 ++
arch/arm64/kernel/vmlinux.lds.S | 3 +
arch/microblaze/kernel/vmlinux.lds.S | 4 +
arch/powerpc/kernel/vmlinux.lds.S | 4 +
arch/um/include/asm/common.lds.S | 4 +
arch/xtensa/kernel/vmlinux.lds.S | 4 +
include/asm-generic/vmlinux.lds.h | 8 ++
include/kunit/test.h | 76 +++++++++++++-----
init/main.c | 4 +
lib/kunit/Makefile | 3 +-
lib/kunit/executor.c | 63 +++++++++++++++
lib/kunit/test.c | 13 +--
tools/testing/kunit/kunit_kernel.py | 2 +-
tools/testing/kunit/kunit_parser.py | 74 ++++++++++++++---
.../test_is_test_passed-all_passed.log | Bin 1562 -> 1567 bytes
.../test_data/test_is_test_passed-crash.log | Bin 3016 -> 3021 bytes
.../test_data/test_is_test_passed-failure.log | Bin 1700 -> 1705 bytes
18 files changed, 227 insertions(+), 48 deletions(-)
create mode 100644 lib/kunit/executor.c
These patches are available for download with dependencies here:
https://kunit-review.googlesource.com/c/linux/+/3829
[1] https://github.com/isaacs/testanything.github.io/blob/tap14/tap-version-14-specification.md#the-plan
[2] https://patchwork.kernel.org/patch/11383635/
base-commit: 4333a9b0b67bb4e8bcd91bdd80da80b0ec151162
prerequisite-patch-id: 2d4b5aa9fa8ada9ae04c8584b47c299a822b9455
prerequisite-patch-id: 582b6d9d28ce4b71628890ec832df6522ca68de0
--
2.27.0.212.ge8ba1cc988-goog
^ permalink raw reply
* Re: [PATCH v2 1/3] powerpc/mm: Enable radix GTSE only if supported.
From: Murilo Opsfelder Araújo @ 2020-06-26 20:55 UTC (permalink / raw)
To: Bharata B Rao; +Cc: aneesh.kumar, linuxppc-dev, npiggin
In-Reply-To: <20200626131000.5207-2-bharata@linux.ibm.com>
Hi, Bharata.
On Fri, Jun 26, 2020 at 06:39:58PM +0530, Bharata B Rao wrote:
> Make GTSE an MMU feature and enable it by default for radix.
> However for guest, conditionally enable it if hypervisor supports
> it via OV5 vector. Let prom_init ask for radix GTSE only if the
> support exists.
>
> Having GTSE as an MMU feature will make it easy to enable radix
> without GTSE. Currently radix assumes GTSE is enabled by default.
>
> Signed-off-by: Bharata B Rao <bharata@linux.ibm.com>
> ---
> arch/powerpc/include/asm/mmu.h | 4 ++++
> arch/powerpc/kernel/dt_cpu_ftrs.c | 1 +
> arch/powerpc/kernel/prom_init.c | 13 ++++++++-----
> arch/powerpc/mm/init_64.c | 5 ++++-
> 4 files changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
> index f4ac25d4df05..884d51995934 100644
> --- a/arch/powerpc/include/asm/mmu.h
> +++ b/arch/powerpc/include/asm/mmu.h
> @@ -28,6 +28,9 @@
> * Individual features below.
> */
>
> +/* Guest Translation Shootdown Enable */
> +#define MMU_FTR_GTSE ASM_CONST(0x00001000)
> +
> /*
> * Support for 68 bit VA space. We added that from ISA 2.05
> */
> @@ -173,6 +176,7 @@ enum {
> #endif
> #ifdef CONFIG_PPC_RADIX_MMU
> MMU_FTR_TYPE_RADIX |
> + MMU_FTR_GTSE |
> #ifdef CONFIG_PPC_KUAP
> MMU_FTR_RADIX_KUAP |
> #endif /* CONFIG_PPC_KUAP */
> diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c
> index a0edeb391e3e..ac650c233cd9 100644
> --- a/arch/powerpc/kernel/dt_cpu_ftrs.c
> +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c
> @@ -336,6 +336,7 @@ static int __init feat_enable_mmu_radix(struct dt_cpu_feature *f)
> #ifdef CONFIG_PPC_RADIX_MMU
> cur_cpu_spec->mmu_features |= MMU_FTR_TYPE_RADIX;
> cur_cpu_spec->mmu_features |= MMU_FTRS_HASH_BASE;
> + cur_cpu_spec->mmu_features |= MMU_FTR_GTSE;
> cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_MMU;
>
> return 1;
> diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
> index 90c604d00b7d..cbc605cfdec0 100644
> --- a/arch/powerpc/kernel/prom_init.c
> +++ b/arch/powerpc/kernel/prom_init.c
> @@ -1336,12 +1336,15 @@ static void __init prom_check_platform_support(void)
> }
> }
>
> - if (supported.radix_mmu && supported.radix_gtse &&
> - IS_ENABLED(CONFIG_PPC_RADIX_MMU)) {
> - /* Radix preferred - but we require GTSE for now */
> - prom_debug("Asking for radix with GTSE\n");
> + if (supported.radix_mmu && IS_ENABLED(CONFIG_PPC_RADIX_MMU)) {
> + /* Radix preferred - Check if GTSE is also supported */
> + prom_debug("Asking for radix\n");
> ibm_architecture_vec.vec5.mmu = OV5_FEAT(OV5_MMU_RADIX);
> - ibm_architecture_vec.vec5.radix_ext = OV5_FEAT(OV5_RADIX_GTSE);
> + if (supported.radix_gtse)
> + ibm_architecture_vec.vec5.radix_ext =
> + OV5_FEAT(OV5_RADIX_GTSE);
> + else
> + prom_debug("Radix GTSE isn't supported\n");
> } else if (supported.hash_mmu) {
> /* Default to hash mmu (if we can) */
> prom_debug("Asking for hash\n");
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index bc73abf0bc25..152aa0200cef 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -407,12 +407,15 @@ static void __init early_check_vec5(void)
> if (!(vec5[OV5_INDX(OV5_RADIX_GTSE)] &
> OV5_FEAT(OV5_RADIX_GTSE))) {
> pr_warn("WARNING: Hypervisor doesn't support RADIX with GTSE\n");
> - }
> + cur_cpu_spec->mmu_features &= ~MMU_FTR_GTSE;
> + } else
> + cur_cpu_spec->mmu_features |= MMU_FTR_GTSE;
> /* Do radix anyway - the hypervisor said we had to */
> cur_cpu_spec->mmu_features |= MMU_FTR_TYPE_RADIX;
> } else if (mmu_supported == OV5_FEAT(OV5_MMU_HASH)) {
> /* Hypervisor only supports hash - disable radix */
> cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
> + cur_cpu_spec->mmu_features &= ~MMU_FTR_GTSE;
> }
> }
Is this a part of the code where mmu_clear_feature() cannot be used?
I'm just curious to understand the difference of clearing
cur_cpu_spec->mmu_features bits like above versus using
mmu_clear_feature() function.
--
Murilo
^ permalink raw reply
* [PATCH 11/11] ppc64/kexec_file: add appropriate regions for memory reserve map
From: Hari Bathini @ 2020-06-26 19:06 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
While initrd, elfcorehdr and backup regions are already added to the
reserve map, there are a few missing regions that need to be added to
the memory reserve map. Add them here. And now that all the changes
to load panic kernel are in place, claim likewise.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/kexec/file_load_64.c | 61 ++++++++++++++++++++++++++++++++++---
1 file changed, 56 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index 58fc2d8..813453d 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -185,6 +185,38 @@ static int get_crash_memory_ranges(struct crash_mem **mem_ranges)
}
/**
+ * get_reserved_memory_ranges - Get reserve memory ranges. This list includes
+ * memory regions that should be added to the
+ * memory reserve map to ensure the region is
+ * protected from any mischeif.
+ * @mem_ranges: Range list to add the memory ranges to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int get_reserved_memory_ranges(struct crash_mem **mem_ranges)
+{
+ int ret;
+
+ ret = add_rtas_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_opal_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_tce_mem_ranges(mem_ranges);
+ if (ret)
+ goto out;
+
+ ret = add_reserved_ranges(mem_ranges);
+out:
+ if (ret)
+ pr_err("Failed to setup reserved memory ranges\n");
+ return ret;
+}
+
+/**
* __locate_mem_hole_ppc64 - Tests if the memory hole between buf_min & buf_max
* is large enough for the buffer. If true, sets
* kbuf->mem to the buffer.
@@ -1182,8 +1214,8 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
unsigned long initrd_load_addr,
unsigned long initrd_len, const char *cmdline)
{
- struct crash_mem *umem = NULL;
- int chosen_node, ret;
+ struct crash_mem *umem = NULL, *rmem = NULL;
+ int i, chosen_node, ret;
/* Remove memory reservation for the current device tree. */
ret = delete_fdt_mem_rsv(fdt, __pa(initial_boot_params),
@@ -1229,6 +1261,24 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
}
}
+ /* Update memory reserve map */
+ ret = get_reserved_memory_ranges(&rmem);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < rmem->nr_ranges; i++) {
+ u64 base, size;
+
+ base = rmem->ranges[i].start;
+ size = rmem->ranges[i].end - base + 1;
+ ret = fdt_add_mem_rsv(fdt, base, size);
+ if (ret) {
+ pr_err("Error updating memory reserve map: %s\n",
+ fdt_strerror(ret));
+ goto out;
+ }
+ }
+
ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len,
cmdline, &chosen_node);
if (ret)
@@ -1239,6 +1289,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
pr_err("Failed to update device-tree with linux,booted-from-kexec\n");
out:
kfree(umem);
+ kfree(rmem);
return ret;
}
@@ -1378,10 +1429,10 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
/* Get exclude memory ranges needed for setting up kdump segments */
ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges));
- if (ret)
+ if (ret) {
pr_err("Failed to setup exclude memory ranges for buffer lookup\n");
- /* Return this until all changes for panic kernel are in */
- return -EOPNOTSUPP;
+ return ret;
+ }
}
return kexec_image_probe_default(image, buf, buf_len);
^ permalink raw reply related
* [PATCH 10/11] ppc64/kexec_file: prepare elfcore header for crashing kernel
From: Hari Bathini @ 2020-06-26 19:06 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
Prepare elf headers for the crashing kernel's core file using
crash_prepare_elf64_headers() and pass on this info to kdump
kernel by updating its command line with elfcorehdr parameter.
Also, add elfcorehdr location to reserve map to avoid it from
being stomped on while booting.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/kexec.h | 6 +
arch/powerpc/kexec/elf_64.c | 12 +++
arch/powerpc/kexec/file_load.c | 49 ++++++++++
arch/powerpc/kexec/file_load_64.c | 174 +++++++++++++++++++++++++++++++++++++
4 files changed, 241 insertions(+)
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index 037cf2b..8b0a6d6 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -112,12 +112,18 @@ struct kimage_arch {
unsigned long backup_start;
void *backup_buf;
+ unsigned long elfcorehdr_addr;
+ unsigned long elf_headers_sz;
+ void *elf_headers;
+
#ifdef CONFIG_IMA_KEXEC
phys_addr_t ima_buffer_addr;
size_t ima_buffer_size;
#endif
};
+char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
+ unsigned long cmdline_len);
int setup_purgatory(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr);
diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c
index 4838b42..40a028c 100644
--- a/arch/powerpc/kexec/elf_64.c
+++ b/arch/powerpc/kexec/elf_64.c
@@ -36,6 +36,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
void *fdt;
const void *slave_code;
struct elfhdr ehdr;
+ char *modified_cmdline = NULL;
struct kexec_elf_info elf_info;
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size };
@@ -74,6 +75,16 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_err("Failed to load kdump kernel segments\n");
goto out;
}
+
+ /* Setup cmdline for kdump kernel case */
+ modified_cmdline = setup_kdump_cmdline(image, cmdline,
+ cmdline_len);
+ if (!modified_cmdline) {
+ pr_err("Setting up cmdline for kdump kernel failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ cmdline = modified_cmdline;
}
if (initrd != NULL) {
@@ -130,6 +141,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_err("Error setting up the purgatory.\n");
out:
+ kfree(modified_cmdline);
kexec_free_elf_info(&elf_info);
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
diff --git a/arch/powerpc/kexec/file_load.c b/arch/powerpc/kexec/file_load.c
index 99a2c4d..2e74992 100644
--- a/arch/powerpc/kexec/file_load.c
+++ b/arch/powerpc/kexec/file_load.c
@@ -17,11 +17,46 @@
#include <linux/slab.h>
#include <linux/kexec.h>
#include <linux/libfdt.h>
+#include <asm/setup.h>
#include <asm/ima.h>
#define SLAVE_CODE_SIZE 256 /* First 0x100 bytes */
/**
+ * setup_kdump_cmdline - Prepend "elfcorehdr=<addr> " to command line
+ * of kdump kernel for exporting the core.
+ * @image: Kexec image
+ * @cmdline: Command line parameters to update.
+ * @cmdline_len: Length of the cmdline parameters.
+ *
+ * kdump segment must be setup before calling this function.
+ *
+ * Returns new cmdline buffer for kdump kernel on success, NULL otherwise.
+ */
+char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
+ unsigned long cmdline_len)
+{
+ int elfcorehdr_strlen;
+ char *cmdline_ptr;
+
+ cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
+ if (!cmdline_ptr)
+ return NULL;
+
+ elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ",
+ image->arch.elfcorehdr_addr);
+
+ if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) {
+ pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
+ kfree(cmdline_ptr);
+ return NULL;
+ }
+
+ memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len);
+ return cmdline_ptr;
+}
+
+/**
* setup_purgatory - initialize the purgatory's global variables
* @image: kexec image.
* @slave_code: Slave code for the purgatory.
@@ -215,6 +250,20 @@ int setup_new_fdt(const struct kimage *image, void *fdt,
}
}
+ if (image->type == KEXEC_TYPE_CRASH) {
+ /*
+ * Avoid elfcorehdr from being stomped on in kdump kernel by
+ * setting up memory reserve map.
+ */
+ ret = fdt_add_mem_rsv(fdt, image->arch.elfcorehdr_addr,
+ image->arch.elf_headers_sz);
+ if (ret) {
+ pr_err("Error reserving elfcorehdr memory: %s\n",
+ fdt_strerror(ret));
+ goto err;
+ }
+ }
+
ret = setup_ima_buffer(image, fdt, chosen_node);
if (ret) {
pr_err("Error setting up the new device tree.\n");
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index d7d3841..58fc2d8 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -18,6 +18,7 @@
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <linux/of_device.h>
+#include <linux/memblock.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/types.h>
@@ -121,6 +122,69 @@ static int get_usable_memory_ranges(struct crash_mem **mem_ranges)
}
/**
+ * get_crash_memory_ranges - Get crash memory ranges. This list includes
+ * first/crashing kernel's memory regions that
+ * would be exported via an elfcore.
+ * @mem_ranges: Range list to add the memory ranges to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int get_crash_memory_ranges(struct crash_mem **mem_ranges)
+{
+ struct memblock_region *reg;
+ struct crash_mem *tmem;
+ int ret;
+
+ /* create a separate program header for the backup region */
+ ret = add_mem_range(mem_ranges, BACKUP_SRC_START, BACKUP_SRC_SIZE);
+ if (ret)
+ goto out;
+
+ for_each_memblock(memory, reg) {
+ u64 base, size;
+
+ base = (u64)reg->base;
+ size = (u64)reg->size;
+
+ /* Skip the memory chunk that is already added */
+ if (base == BACKUP_SRC_START) {
+ if (size > BACKUP_SRC_SIZE) {
+ base = BACKUP_SRC_END + 1;
+ size -= BACKUP_SRC_SIZE;
+ } else
+ continue;
+ }
+
+ ret = add_mem_range(mem_ranges, base, size);
+ if (ret)
+ goto out;
+ }
+
+ /* Reallocate memory ranges size if there is no space to split ranges */
+ tmem = *mem_ranges;
+ if (tmem->nr_ranges == tmem->max_nr_ranges) {
+ tmem = realloc_mem_ranges(mem_ranges);
+ if (!tmem)
+ goto out;
+ }
+
+ /* Exclude crashkernel region */
+ ret = crash_exclude_mem_range(tmem, crashk_res.start, crashk_res.end);
+ if (ret)
+ goto out;
+
+ ret = add_rtas_mem_range(mem_ranges, true);
+ if (ret)
+ goto out;
+
+ ret = add_opal_mem_range(mem_ranges, true);
+out:
+ if (ret)
+ pr_err("Failed to setup crash memory ranges\n");
+ return ret;
+}
+
+/**
* __locate_mem_hole_ppc64 - Tests if the memory hole between buf_min & buf_max
* is large enough for the buffer. If true, sets
* kbuf->mem to the buffer.
@@ -903,6 +967,103 @@ static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf)
}
/**
+ * update_backup_region_phdr - Update backup region's offset for the core to
+ * export the region appropriately.
+ * @image: Kexec image.
+ * @ehdr: ELF core header.
+ *
+ * Assumes an exclusive program header is setup for the backup region
+ * in the ELF headers
+ *
+ * Returns nothing.
+ */
+static void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr)
+{
+ Elf64_Phdr *phdr;
+ unsigned int i;
+
+ phdr = (Elf64_Phdr *)(ehdr + 1);
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr->p_paddr == BACKUP_SRC_START) {
+ phdr->p_offset = image->arch.backup_start;
+ pr_debug("Backup region offset updated to 0x%lx\n",
+ image->arch.backup_start);
+ return;
+ }
+ }
+}
+
+/**
+ * prepare_elf_headers - Prepare headers for the elfcore to be exported as
+ * /proc/vmcore by the kdump kernel.
+ * @image: Kexec image.
+ * @cmem: Crash memory ranges to be exported via elfcore.
+ * @addr: Vmalloc'd memory allocated by crash_prepare_elf64_headers
+ * to prepare the elf headers.
+ * @sz: Size of the vmalloc'd memory allocated.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int prepare_elf_headers(struct kimage *image, struct crash_mem *cmem,
+ void **addr, unsigned long *sz)
+{
+ int ret;
+
+ ret = crash_prepare_elf64_headers(cmem, false, addr, sz);
+
+ /* Fix the offset for backup region in the ELF header */
+ if (!ret)
+ update_backup_region_phdr(image, *addr);
+
+ return ret;
+}
+
+/**
+ * load_elfcorehdr_segment - Setup crash memory ranges and initialize elfcorehdr
+ * segment needed to load kdump kernel.
+ * @image: Kexec image.
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf)
+{
+ struct crash_mem *cmem = NULL;
+ unsigned long headers_sz;
+ void *headers = NULL;
+ int ret;
+
+ ret = get_crash_memory_ranges(&cmem);
+ if (ret)
+ goto out;
+
+ /* Setup elfcorehdr segment */
+ ret = prepare_elf_headers(image, cmem, &headers, &headers_sz);
+ if (ret) {
+ pr_err("Failed to prepare elf headers for the core\n");
+ goto out;
+ }
+
+ kbuf->buffer = headers;
+ kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+ kbuf->bufsz = kbuf->memsz = headers_sz;
+ kbuf->top_down = false;
+
+ ret = kexec_add_buffer(kbuf);
+ if (ret) {
+ vfree(headers);
+ goto out;
+ }
+
+ image->arch.elfcorehdr_addr = kbuf->mem;
+ image->arch.elf_headers_sz = headers_sz;
+ image->arch.elf_headers = headers;
+out:
+ kfree(cmem);
+ return ret;
+}
+
+/**
* load_crashdump_segments_ppc64 - Initialize the additional segements needed
* to load kdump kernel.
* @image: Kexec image.
@@ -924,6 +1085,15 @@ int load_crashdump_segments_ppc64(struct kimage *image,
pr_debug("Setup backup region of size %ld bytes at %lx\n",
kbuf->memsz, kbuf->mem);
+ /* Load elfcorehdr segment - to export crashing kernel's vmcore */
+ ret = load_elfcorehdr_segment(image, kbuf);
+ if (ret) {
+ pr_err("Failed to load elfcorehdr segment\n");
+ return ret;
+ }
+ pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
+ image->arch.elfcorehdr_addr, kbuf->bufsz, kbuf->memsz);
+
return 0;
}
@@ -1232,5 +1402,9 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
vfree(image->arch.backup_buf);
image->arch.backup_buf = NULL;
+ vfree(image->arch.elf_headers);
+ image->arch.elf_headers = NULL;
+ image->arch.elf_headers_sz = 0;
+
return kexec_image_post_load_cleanup_default(image);
}
^ permalink raw reply related
* [PATCH 09/11] ppc64/kexec_file: setup backup region for kdump kernel
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
Though kdump kernel boots from loaded address, the first 64K bytes
of it is copied down to real 0. So, setup a backup region to copy
the first 64K bytes of crashed kernel, in purgatory, before booting
into kdump kernel. Also, update reserve map with backup region and
crashed kernel's memory to avoid kdump kernel from accidentially
using that memory.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/crashdump-ppc64.h | 5 +
arch/powerpc/include/asm/kexec.h | 7 ++
arch/powerpc/kexec/elf_64.c | 9 +++
arch/powerpc/kexec/file_load_64.c | 96 ++++++++++++++++++++++++++++
arch/powerpc/purgatory/Makefile | 28 ++++++++
arch/powerpc/purgatory/purgatory_64.c | 35 ++++++++++
arch/powerpc/purgatory/trampoline_64.S | 23 +++++--
7 files changed, 195 insertions(+), 8 deletions(-)
create mode 100644 arch/powerpc/purgatory/purgatory_64.c
diff --git a/arch/powerpc/include/asm/crashdump-ppc64.h b/arch/powerpc/include/asm/crashdump-ppc64.h
index 3596c25..504a579 100644
--- a/arch/powerpc/include/asm/crashdump-ppc64.h
+++ b/arch/powerpc/include/asm/crashdump-ppc64.h
@@ -2,6 +2,11 @@
#ifndef _ARCH_POWERPC_KEXEC_CRASHDUMP_PPC64_H
#define _ARCH_POWERPC_KEXEC_CRASHDUMP_PPC64_H
+/* Backup region - first 64K bytes of System RAM. */
+#define BACKUP_SRC_START 0
+#define BACKUP_SRC_END 0xffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
/* min & max addresses for kdump load segments */
#define KDUMP_BUF_MIN (crashk_res.start)
#define KDUMP_BUF_MAX ((crashk_res.end < ppc64_rma_size) ? \
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index e78cd0a..037cf2b 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -109,6 +109,9 @@ extern const struct kexec_file_ops kexec_elf64_ops;
struct kimage_arch {
struct crash_mem *exclude_ranges;
+ unsigned long backup_start;
+ void *backup_buf;
+
#ifdef CONFIG_IMA_KEXEC
phys_addr_t ima_buffer_addr;
size_t ima_buffer_size;
@@ -124,6 +127,10 @@ int setup_new_fdt(const struct kimage *image, void *fdt,
int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size);
#ifdef CONFIG_PPC64
+struct kexec_buf;
+
+int load_crashdump_segments_ppc64(struct kimage *image,
+ struct kexec_buf *kbuf);
int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr);
diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c
index c695f94..4838b42 100644
--- a/arch/powerpc/kexec/elf_64.c
+++ b/arch/powerpc/kexec/elf_64.c
@@ -67,6 +67,15 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
+ /* Setup additional segments needed for panic kernel */
+ if (image->type == KEXEC_TYPE_CRASH) {
+ ret = load_crashdump_segments_ppc64(image, &kbuf);
+ if (ret) {
+ pr_err("Failed to load kdump kernel segments\n");
+ goto out;
+ }
+ }
+
if (initrd != NULL) {
kbuf.buffer = initrd;
kbuf.bufsz = kbuf.memsz = initrd_len;
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index 8e66c28..d7d3841 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -19,6 +19,7 @@
#include <linux/libfdt.h>
#include <linux/of_device.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <asm/types.h>
#include <asm/drmem.h>
#include <asm/kexec_ranges.h>
@@ -863,6 +864,70 @@ static int kexec_do_relocs_ppc64(unsigned long my_r2, const Elf_Sym *sym,
}
/**
+ * load_backup_segment - Initialize backup segment of crashing kernel.
+ * @image: Kexec image.
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf)
+{
+ void *buf;
+ int ret;
+
+ /* Setup a segment for backup region */
+ buf = vzalloc(BACKUP_SRC_SIZE);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * A source buffer has no meaning for backup region as data will
+ * be copied from backup source, after crash, in the purgatory.
+ * But as load segment code doesn't recognize such segments,
+ * setup a dummy source buffer to keep it happy for now.
+ */
+ kbuf->buffer = buf;
+ kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+ kbuf->bufsz = kbuf->memsz = BACKUP_SRC_SIZE;
+ kbuf->top_down = false;
+
+ ret = kexec_add_buffer(kbuf);
+ if (ret) {
+ vfree(buf);
+ return ret;
+ }
+
+ image->arch.backup_buf = buf;
+ image->arch.backup_start = kbuf->mem;
+ return 0;
+}
+
+/**
+ * load_crashdump_segments_ppc64 - Initialize the additional segements needed
+ * to load kdump kernel.
+ * @image: Kexec image.
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int load_crashdump_segments_ppc64(struct kimage *image,
+ struct kexec_buf *kbuf)
+{
+ int ret;
+
+ /* Load backup segment - first 64K bytes of the crashing kernel */
+ ret = load_backup_segment(image, kbuf);
+ if (ret) {
+ pr_err("Failed to load backup segment\n");
+ return ret;
+ }
+ pr_debug("Setup backup region of size %ld bytes at %lx\n",
+ kbuf->memsz, kbuf->mem);
+
+ return 0;
+}
+
+/**
* setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global
* variables and call setup_purgatory() to initialize
* common global variable.
@@ -902,6 +967,14 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
goto out;
}
+ /* Tell purgatory where to look for backup region */
+ ret = kexec_purgatory_get_set_symbol(image, "backup_start",
+ &image->arch.backup_start,
+ sizeof(image->arch.backup_start),
+ false);
+ if (ret)
+ goto out;
+
/* Setup the stack top */
stack_buf = kexec_purgatory_get_symbol_addr(image, "stack_buf");
if (!stack_buf)
@@ -954,7 +1027,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
/*
* Restrict memory usage for kdump kernel by setting up
- * usable memory ranges.
+ * usable memory ranges and memory reserve map.
*/
if (image->type == KEXEC_TYPE_CRASH) {
ret = get_usable_memory_ranges(&umem);
@@ -966,6 +1039,24 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
pr_err("Error setting up usable-memory property for kdump kernel\n");
goto out;
}
+
+ ret = fdt_add_mem_rsv(fdt, BACKUP_SRC_START + BACKUP_SRC_SIZE,
+ crashk_res.start - BACKUP_SRC_SIZE);
+ if (ret) {
+ pr_err("Error reserving crash memory: %s\n",
+ fdt_strerror(ret));
+ goto out;
+ }
+ }
+
+ if (image->arch.backup_start) {
+ ret = fdt_add_mem_rsv(fdt, image->arch.backup_start,
+ BACKUP_SRC_SIZE);
+ if (ret) {
+ pr_err("Error reserving memory for backup: %s\n",
+ fdt_strerror(ret));
+ goto out;
+ }
}
ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len,
@@ -1138,5 +1229,8 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
kfree(image->arch.exclude_ranges);
image->arch.exclude_ranges = NULL;
+ vfree(image->arch.backup_buf);
+ image->arch.backup_buf = NULL;
+
return kexec_image_post_load_cleanup_default(image);
}
diff --git a/arch/powerpc/purgatory/Makefile b/arch/powerpc/purgatory/Makefile
index 348f5958..a494413 100644
--- a/arch/powerpc/purgatory/Makefile
+++ b/arch/powerpc/purgatory/Makefile
@@ -2,13 +2,37 @@
KASAN_SANITIZE := n
-targets += trampoline_$(BITS).o purgatory.ro kexec-purgatory.c
+purgatory-y := purgatory_$(BITS).o trampoline_$(BITS).o
+
+targets += $(purgatory-y)
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined
+targets += purgatory.ro
+
+PURGATORY_CFLAGS_REMOVE :=
+
+# Default KBUILD_CFLAGS can have -pg option set when FUNCTION_TRACE is
+# enabled leaving some undefined symbols like _mcount in purgatory.
+ifdef CONFIG_FUNCTION_TRACER
+PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_FTRACE)
+endif
+
+ifdef CONFIG_STACKPROTECTOR
+PURGATORY_CFLAGS_REMOVE += -fstack-protector
+endif
-$(obj)/purgatory.ro: $(obj)/trampoline_$(BITS).o FORCE
+ifdef CONFIG_STACKPROTECTOR_STRONG
+PURGATORY_CFLAGS_REMOVE += -fstack-protector-strong
+endif
+
+CFLAGS_REMOVE_purgatory_$(BITS).o += $(PURGATORY_CFLAGS_REMOVE)
+
+$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
$(call if_changed,ld)
+targets += kexec-purgatory.c
+
quiet_cmd_bin2c = BIN2C $@
cmd_bin2c = $(objtree)/scripts/bin2c kexec_purgatory < $< > $@
diff --git a/arch/powerpc/purgatory/purgatory_64.c b/arch/powerpc/purgatory/purgatory_64.c
new file mode 100644
index 0000000..bdb3108
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory_64.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * purgatory: Runs between two kernels
+ *
+ * Copyright 2020, Hari Bathini, IBM Corporation.
+ */
+
+#include <asm/crashdump-ppc64.h>
+
+extern unsigned long backup_start;
+
+static void *memcpy(void *dest, const void *src, unsigned long n)
+{
+ unsigned long i;
+ unsigned char *d;
+ const unsigned char *s;
+
+ d = dest;
+ s = src;
+ for (i = 0; i < n; i++)
+ d[i] = s[i];
+
+ return dest;
+}
+
+void purgatory(void)
+{
+ void *dest, *src;
+
+ src = (void *)BACKUP_SRC_START;
+ if (backup_start) {
+ dest = (void *)backup_start;
+ memcpy(dest, src, BACKUP_SRC_SIZE);
+ }
+}
diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S
index 80615b4..c534a1e 100644
--- a/arch/powerpc/purgatory/trampoline_64.S
+++ b/arch/powerpc/purgatory/trampoline_64.S
@@ -44,11 +44,6 @@ master:
mr %r17,%r3 /* save cpu id to r17 */
mr %r15,%r4 /* save physical address in reg15 */
- or %r3,%r3,%r3 /* ok now to high priority, lets boot */
- lis %r6,0x1
- mtctr %r6 /* delay a bit for slaves to catch up */
- bdnz . /* before we overwrite 0-100 again */
-
bl 0f /* Work out where we're running */
0: mflr %r18
@@ -56,6 +51,19 @@ master:
ld %r1,(stack - 0b)(%r18) /* setup stack */
+ subi %r1,%r1,112
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+ bl purgatory
+#else
+ bl .purgatory
+#endif
+ nop
+
+ or %r3,%r3,%r3 /* ok now to high priority, lets boot */
+ lis %r6,0x1
+ mtctr %r6 /* delay a bit for slaves to catch up */
+ bdnz . /* before we overwrite 0-100 again */
+
/* load device-tree address */
ld %r3, (dt_offset - 0b)(%r18)
mr %r16,%r3 /* save dt address in reg16 */
@@ -107,6 +115,11 @@ dt_offset:
.8byte 0x0
.size dt_offset, . - dt_offset
+ .balign 8
+ .globl backup_start
+backup_start:
+ .8byte 0x0
+ .size backup_start, . - backup_start
.balign 8
.globl my_toc
^ permalink raw reply related
* [PATCH 08/11] ppc64/kexec_file: setup the stack for purgatory
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
To avoid any weird errors, the purgatory should run with its own
stack. Set one up by adding the stack buffer to .data section of
the purgatory.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/kexec.h | 4 ++++
arch/powerpc/kexec/file_load_64.c | 14 +++++++++++++-
arch/powerpc/purgatory/trampoline_64.S | 15 +++++++++++++++
3 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index bf47a01..e78cd0a 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -45,6 +45,10 @@
#define KEXEC_ARCH KEXEC_ARCH_PPC
#endif
+#ifdef CONFIG_KEXEC_FILE
+#define KEXEC_PURGATORY_STACK_SIZE 16384 /* 16KB stack size */
+#endif
+
#define KEXEC_STATE_NONE 0
#define KEXEC_STATE_IRQS_OFF 1
#define KEXEC_STATE_REAL_MODE 2
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index 4430336..8e66c28 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -878,7 +878,8 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr)
{
- uint64_t toc_ptr;
+ uint64_t toc_ptr, stack_top;
+ void *stack_buf;
int ret;
ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
@@ -901,6 +902,17 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
goto out;
}
+ /* Setup the stack top */
+ stack_buf = kexec_purgatory_get_symbol_addr(image, "stack_buf");
+ if (!stack_buf)
+ goto out;
+
+ stack_top = (u64)stack_buf + KEXEC_PURGATORY_STACK_SIZE;
+ ret = kexec_purgatory_get_set_symbol(image, "stack", &stack_top,
+ sizeof(stack_top), false);
+ if (ret)
+ goto out;
+
/* Setup the TOC pointer */
toc_ptr = get_toc_ptr(image->purgatory_info.ehdr);
ret = kexec_purgatory_get_set_symbol(image, "my_toc", &toc_ptr,
diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S
index 7b4a5f7..80615b4 100644
--- a/arch/powerpc/purgatory/trampoline_64.S
+++ b/arch/powerpc/purgatory/trampoline_64.S
@@ -9,6 +9,7 @@
* Copyright (C) 2013, Anton Blanchard, IBM Corporation
*/
+#include <asm/kexec.h>
#include <asm/asm-compat.h>
.machine ppc64
@@ -53,6 +54,8 @@ master:
ld %r2,(my_toc - 0b)(%r18) /* setup toc */
+ ld %r1,(stack - 0b)(%r18) /* setup stack */
+
/* load device-tree address */
ld %r3, (dt_offset - 0b)(%r18)
mr %r16,%r3 /* save dt address in reg16 */
@@ -111,6 +114,12 @@ my_toc:
.8byte 0x0
.size my_toc, . - my_toc
+ .balign 8
+ .globl stack
+stack:
+ .8byte 0x0
+ .size stack, . - stack
+
.data
.balign 8
.globl purgatory_sha256_digest
@@ -123,3 +132,9 @@ purgatory_sha256_digest:
purgatory_sha_regions:
.skip 8 * 2 * 16
.size purgatory_sha_regions, . - purgatory_sha_regions
+
+ .balign 8
+.globl stack_buf
+stack_buf:
+ .skip KEXEC_PURGATORY_STACK_SIZE
+ .size stack_buf, . - stack_buf
^ permalink raw reply related
* [PATCH 07/11] ppc64/kexec_file: add support to relocate purgatory
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
Right now purgatory implementation is only minimal. But if purgatory
code is to be enhanced to copy memory to the backup region and verify
sha256 digest, relocations may have to be applied to the purgatory.
So, add support to relocate purgatory in kexec_file_load system call
by setting up TOC pointer and applying RELA relocations as needed.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/kexec/file_load_64.c | 338 ++++++++++++++++++++++++++++++++
arch/powerpc/purgatory/trampoline_64.S | 8 +
2 files changed, 346 insertions(+)
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index d85cba4d..4430336 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -19,6 +19,7 @@
#include <linux/libfdt.h>
#include <linux/of_device.h>
#include <linux/slab.h>
+#include <asm/types.h>
#include <asm/drmem.h>
#include <asm/kexec_ranges.h>
#include <asm/crashdump-ppc64.h>
@@ -626,6 +627,242 @@ static int update_usable_mem_fdt(void *fdt, struct crash_mem *usable_mem)
}
/**
+ * get_toc_section - Look for ".toc" symbol and return the corresponding section
+ * @ehdr: ELF header.
+ *
+ * Returns TOC section on success, NULL otherwise.
+ */
+static const Elf_Shdr *get_toc_section(const Elf_Ehdr *ehdr)
+{
+ const Elf_Shdr *sechdrs;
+ const char *secstrings;
+ int i;
+
+ if (!ehdr) {
+ pr_err("Purgatory elf load info missing?\n");
+ return NULL;
+ }
+
+ sechdrs = (void *)ehdr + ehdr->e_shoff;
+ secstrings = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
+
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ if ((sechdrs[i].sh_size != 0) &&
+ (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)) {
+ /* Return the ".toc" section */
+ pr_debug("TOC section number is %d\n", i);
+ return &sechdrs[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * get_toc_ptr - r2 is the TOC pointer: it points 0x8000 into the TOC
+ * @ehdr: ELF header.
+ *
+ * Returns r2 on success, 0 otherwise.
+ */
+static unsigned long get_toc_ptr(const Elf_Ehdr *ehdr)
+{
+ const Elf_Shdr *sechdr;
+
+ sechdr = get_toc_section(ehdr);
+ if (!sechdr) {
+ pr_err("Could not get the TOC section!\n");
+ return 0;
+ }
+
+ return sechdr->sh_addr + 0x8000;
+}
+
+/* Helper functions to apply relocations */
+static int do_relative_toc(unsigned long val, uint16_t *loc,
+ unsigned long mask, int complain_signed)
+{
+ if (complain_signed && (val + 0x8000 > 0xffff)) {
+ pr_err("TOC16 relocation overflows (%lu)\n", val);
+ return -ENOEXEC;
+ }
+
+ if ((~mask & 0xffff) & val) {
+ pr_err("Bad TOC16 relocation (%lu)\n", val);
+ return -ENOEXEC;
+ }
+
+ *loc = (*loc & ~mask) | (val & mask);
+ return 0;
+}
+#ifdef PPC64_ELF_ABI_v2
+/* PowerPC64 specific values for the Elf64_Sym st_other field. */
+#define STO_PPC64_LOCAL_BIT 5
+#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
+#define PPC64_LOCAL_ENTRY_OFFSET(other) \
+ (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) \
+ >> 2) << 2)
+
+static unsigned int local_entry_offset(const Elf64_Sym *sym)
+{
+ /* If this symbol has a local entry point, use it. */
+ return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
+}
+#else
+static unsigned int local_entry_offset(struct mem_sym *UNUSED(sym))
+{
+ return 0;
+}
+#endif
+
+/**
+ * kexec_do_relocs_ppc64 - Apply relocations based on relocation type.
+ * @my_r2: TOC pointer.
+ * @sym: Symbol to relocate.
+ * @r_type: Relocation type.
+ * @loc: Location to modify.
+ * @val: Relocated symbol value.
+ * @addr: Final location after relocation.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int kexec_do_relocs_ppc64(unsigned long my_r2, const Elf_Sym *sym,
+ int r_type, void *loc, unsigned long val,
+ unsigned long addr)
+{
+ int ret = 0;
+
+ switch (r_type) {
+ case R_PPC64_ADDR32:
+ /* Simply set it */
+ *(uint32_t *)loc = val;
+ break;
+
+ case R_PPC64_ADDR64:
+ /* Simply set it */
+ *(uint64_t *)loc = val;
+ break;
+
+ case R_PPC64_REL64:
+ *(uint64_t *)loc = val - (uint64_t)loc;
+ break;
+
+ case R_PPC64_REL32:
+ /* Convert value to relative */
+ val -= (unsigned long)loc;
+ if (val + 0x80000000 > 0xffffffff) {
+ pr_err("REL32 %li out of range!\n", val);
+ return -ENOEXEC;
+ }
+
+ *(uint32_t *)loc = val;
+ break;
+
+ case R_PPC64_TOC:
+ *(uint64_t *)loc = my_r2;
+ break;
+
+ case R_PPC64_TOC16:
+ ret = do_relative_toc(val - my_r2, loc, 0xffff, 1);
+ break;
+
+ case R_PPC64_TOC16_DS:
+ ret = do_relative_toc(val - my_r2, loc, 0xfffc, 1);
+ break;
+
+ case R_PPC64_TOC16_LO:
+ ret = do_relative_toc(val - my_r2, loc, 0xffff, 0);
+ break;
+
+ case R_PPC64_TOC16_LO_DS:
+ ret = do_relative_toc(val - my_r2, loc, 0xfffc, 0);
+ break;
+
+ case R_PPC64_TOC16_HI:
+ ret = do_relative_toc((val - my_r2) >> 16, loc,
+ 0xffff, 0);
+ break;
+
+ case R_PPC64_TOC16_HA:
+ ret = do_relative_toc((val - my_r2 + 0x8000) >> 16, loc,
+ 0xffff, 0);
+ break;
+
+ case R_PPC64_REL24:
+ val += local_entry_offset(sym);
+ /* Convert value to relative */
+ val -= addr;
+ if (val + 0x2000000 > 0x3ffffff || (val & 3) != 0) {
+ pr_err("REL24 %li out of range!\n", val);
+ return -ENOEXEC;
+ }
+
+ /* Only replace bits 2 through 26 */
+ *(uint32_t *)loc = ((*(uint32_t *)loc & ~0x03fffffc) |
+ (val & 0x03fffffc));
+ break;
+
+ case R_PPC64_ADDR16_LO:
+ *(uint16_t *)loc = val & 0xffff;
+ break;
+
+ case R_PPC64_ADDR16_HI:
+ *(uint16_t *)loc = (val >> 16) & 0xffff;
+ break;
+
+ case R_PPC64_ADDR16_HA:
+ *(uint16_t *)loc = (((val + 0x8000) >> 16) & 0xffff);
+ break;
+
+ case R_PPC64_ADDR16_HIGHER:
+ *(uint16_t *)loc = (((uint64_t)val >> 32) & 0xffff);
+ break;
+
+ case R_PPC64_ADDR16_HIGHEST:
+ *(uint16_t *)loc = (((uint64_t)val >> 48) & 0xffff);
+ break;
+
+ /* R_PPC64_REL16_HA and R_PPC64_REL16_LO are handled to support
+ * ABIv2 r2 assignment based on r12 for PIC executable.
+ * Here address is known, so replace
+ * 0: addis 2,12,.TOC.-0b@ha
+ * addi 2,2,.TOC.-0b@l
+ * by
+ * lis 2,.TOC.@ha
+ * addi 2,2,.TOC.@l
+ */
+ case R_PPC64_REL16_HA:
+ /* check that we are dealing with the addis 2,12 instruction */
+ if (((*(uint32_t *)loc) & 0xffff0000) != 0x3c4c0000) {
+ pr_err("Unexpected instruction for R_PPC64_REL16_HA");
+ return -ENOEXEC;
+ }
+
+ val += my_r2;
+ /* replacing by lis 2 */
+ *(uint32_t *)loc = 0x3c400000 + ((val >> 16) & 0xffff);
+ break;
+
+ case R_PPC64_REL16_LO:
+ /* check that we are dealing with the addi 2,2 instruction */
+ if (((*(uint32_t *)loc) & 0xffff0000) != 0x38420000) {
+ pr_err("Unexpected instruction for R_PPC64_REL16_LO");
+ return -ENOEXEC;
+ }
+
+ val += my_r2 - 4;
+ *(uint16_t *)loc = val & 0xffff;
+ break;
+
+ default:
+ pr_err("Unknown rela relocation: %d\n", r_type);
+ ret = -ENOEXEC;
+ break;
+ }
+
+ return ret;
+}
+
+/**
* setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global
* variables and call setup_purgatory() to initialize
* common global variable.
@@ -641,6 +878,7 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr)
{
+ uint64_t toc_ptr;
int ret;
ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
@@ -663,6 +901,10 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
goto out;
}
+ /* Setup the TOC pointer */
+ toc_ptr = get_toc_ptr(image->purgatory_info.ehdr);
+ ret = kexec_purgatory_get_set_symbol(image, "my_toc", &toc_ptr,
+ sizeof(toc_ptr), false);
out:
if (ret)
pr_err("Failed to setup purgatory symbols");
@@ -751,6 +993,102 @@ int arch_kexec_add_buffer(struct kexec_buf *kbuf)
}
/**
+ * arch_kexec_apply_relocations_add - Apply relocations of type RELA
+ * @pi: Purgatory Info.
+ * @section: Section relocations applying to.
+ * @relsec: Section containing RELAs.
+ * @symtab: Corresponding symtab.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
+ Elf_Shdr *section,
+ const Elf_Shdr *relsec,
+ const Elf_Shdr *symtab)
+{
+ const char *strtab, *name, *shstrtab;
+ const Elf_Shdr *sechdrs;
+ int i, r_type, ret = 0;
+ const Elf_Ehdr *ehdr;
+ unsigned long my_r2;
+ Elf_Rela *relas;
+
+ ehdr = pi->ehdr;
+
+ /* String & section header string table */
+ sechdrs = (void *)ehdr + ehdr->e_shoff;
+ strtab = (char *)ehdr + sechdrs[symtab->sh_link].sh_offset;
+ shstrtab = (char *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
+
+ relas = (void *)ehdr + relsec->sh_offset;
+
+ pr_debug("Applying relocate section %s to %u\n",
+ shstrtab + relsec->sh_name, relsec->sh_info);
+
+ /* Get the TOC pointer (r2) */
+ my_r2 = get_toc_ptr(ehdr);
+ if (!my_r2)
+ return -ENOEXEC;
+
+ for (i = 0; i < relsec->sh_size / sizeof(*relas); i++) {
+ const Elf_Sym *sym; /* symbol to relocate */
+ unsigned long addr; /* final location after relocation */
+ unsigned long val; /* relocated symbol value */
+ void *loc; /* tmp location to modify */
+
+ sym = (void *)ehdr + symtab->sh_offset;
+ sym += ELF64_R_SYM(relas[i].r_info);
+
+ if (sym->st_name)
+ name = strtab + sym->st_name;
+ else
+ name = shstrtab + sechdrs[sym->st_shndx].sh_name;
+
+ pr_debug("Symbol: %s info: %x shndx: %x value=%llx size: %llx\n",
+ name, sym->st_info, sym->st_shndx, sym->st_value,
+ sym->st_size);
+
+ if ((sym->st_shndx == SHN_UNDEF) &&
+ (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE)) {
+ pr_err("Undefined symbol: %s\n", name);
+ return -ENOEXEC;
+ }
+
+ if (sym->st_shndx == SHN_COMMON) {
+ pr_err("symbol '%s' in common section\n", name);
+ return -ENOEXEC;
+ }
+
+ if ((sym->st_shndx >= ehdr->e_shnum) &&
+ (sym->st_shndx != SHN_ABS)) {
+ pr_err("Invalid section %d for symbol %s\n",
+ sym->st_shndx, name);
+ return -ENOEXEC;
+ }
+
+ loc = pi->purgatory_buf;
+ loc += section->sh_offset;
+ loc += relas[i].r_offset;
+
+ val = sym->st_value;
+ if (sym->st_shndx != SHN_ABS)
+ val += pi->sechdrs[sym->st_shndx].sh_addr;
+ val += relas[i].r_addend;
+
+ addr = section->sh_addr + relas[i].r_offset;
+
+ pr_debug("Symbol: %s value=%lx address=%lx\n", name, val, addr);
+
+ r_type = ELF64_R_TYPE(relas[i].r_info);
+ ret = kexec_do_relocs_ppc64(my_r2, sym, r_type, loc, val, addr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
* arch_kexec_kernel_image_probe - Does additional handling needed to setup
* kexec segments.
* @image: kexec image being loaded.
diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S
index a5a83c3..7b4a5f7 100644
--- a/arch/powerpc/purgatory/trampoline_64.S
+++ b/arch/powerpc/purgatory/trampoline_64.S
@@ -51,6 +51,8 @@ master:
bl 0f /* Work out where we're running */
0: mflr %r18
+ ld %r2,(my_toc - 0b)(%r18) /* setup toc */
+
/* load device-tree address */
ld %r3, (dt_offset - 0b)(%r18)
mr %r16,%r3 /* save dt address in reg16 */
@@ -103,6 +105,12 @@ dt_offset:
.size dt_offset, . - dt_offset
+ .balign 8
+ .globl my_toc
+my_toc:
+ .8byte 0x0
+ .size my_toc, . - my_toc
+
.data
.balign 8
.globl purgatory_sha256_digest
^ permalink raw reply related
* [PATCH 06/11] ppc64/kexec_file: restrict memory usage of kdump kernel
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
Kdump kernel, used for capturing the kernel core image, is supposed
to use only specific memory regions to avoid corrupting the image to
be captured. The regions are crashkernel range - the memory reserved
explicitly for kdump kernel, memory used for the tce-table, the OPAL
region and RTAS region as applicable. Restrict kdump kernel memory
to use only these regions by setting up usable-memory DT property.
Also, tell the kdump kernel to run at the loaded address by setting
the magic word at 0x5c.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/kexec/file_load_64.c | 400 +++++++++++++++++++++++++++++++++++++
1 file changed, 398 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index f1d7160..d85cba4d 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -17,9 +17,21 @@
#include <linux/kexec.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <asm/drmem.h>
#include <asm/kexec_ranges.h>
#include <asm/crashdump-ppc64.h>
+struct umem_info {
+ uint64_t *buf; /* data buffer for usable-memory property */
+ uint32_t idx; /* current index */
+ uint32_t size; /* size allocated for the data buffer */
+
+ /* usable memory ranges to look up */
+ const struct crash_mem *umrngs;
+};
+
const struct kexec_file_ops * const kexec_file_loaders[] = {
&kexec_elf64_ops,
NULL
@@ -75,6 +87,38 @@ static int get_exclude_memory_ranges(struct crash_mem **mem_ranges)
}
/**
+ * get_usable_memory_ranges - Get usable memory ranges. This list includes
+ * regions like crashkernel, opal/rtas & tce-table,
+ * that kdump kernel could use.
+ * @mem_ranges: Range list to add the memory ranges to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int get_usable_memory_ranges(struct crash_mem **mem_ranges)
+{
+ int ret;
+
+ /* First memory block & crashkernel region */
+ ret = add_mem_range(mem_ranges, 0, crashk_res.end + 1);
+ if (ret)
+ goto out;
+
+ ret = add_rtas_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_opal_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_tce_mem_ranges(mem_ranges);
+out:
+ if (ret)
+ pr_err("Failed to setup usable memory ranges\n");
+ return ret;
+}
+
+/**
* __locate_mem_hole_ppc64 - Tests if the memory hole between buf_min & buf_max
* is large enough for the buffer. If true, sets
* kbuf->mem to the buffer.
@@ -267,6 +311,321 @@ static int kexec_locate_mem_hole_ppc64(struct kexec_buf *kbuf)
}
/**
+ * check_realloc_usable_mem - Reallocate buffer if it can't accommodate entries
+ * @um_info: Usable memory buffer and ranges info.
+ * @cnt: No. of entries to accommodate.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static uint64_t *check_realloc_usable_mem(struct umem_info *um_info, int cnt)
+{
+ void *tbuf;
+
+ if (um_info->size >=
+ ((um_info->idx + cnt) * sizeof(*(um_info->buf))))
+ return um_info->buf;
+
+ um_info->size += MEM_RANGE_CHUNK_SZ;
+ tbuf = krealloc(um_info->buf, um_info->size, GFP_KERNEL);
+ if (!tbuf) {
+ um_info->size -= MEM_RANGE_CHUNK_SZ;
+ return NULL;
+ }
+
+ memset(tbuf + um_info->idx, 0, MEM_RANGE_CHUNK_SZ);
+ return tbuf;
+}
+
+/**
+ * add_usable_mem - Add the usable memory ranges within the given memory range
+ * to the buffer
+ * @um_info: Usable memory buffer and ranges info.
+ * @base: Base address of memory range to look for.
+ * @end: End address of memory range to look for.
+ * @cnt: No. of usable memory ranges added to buffer.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int add_usable_mem(struct umem_info *um_info, uint64_t base,
+ uint64_t end, int *cnt)
+{
+ uint64_t loc_base, loc_end, *buf;
+ const struct crash_mem *umrngs;
+ int i, add;
+
+ *cnt = 0;
+ umrngs = um_info->umrngs;
+ for (i = 0; i < umrngs->nr_ranges; i++) {
+ add = 0;
+ loc_base = umrngs->ranges[i].start;
+ loc_end = umrngs->ranges[i].end;
+ if (loc_base >= base && loc_end <= end)
+ add = 1;
+ else if (base < loc_end && end > loc_base) {
+ if (loc_base < base)
+ loc_base = base;
+ if (loc_end > end)
+ loc_end = end;
+ add = 1;
+ }
+
+ if (add) {
+ buf = check_realloc_usable_mem(um_info, 2);
+ if (!buf)
+ return -ENOMEM;
+
+ um_info->buf = buf;
+ buf[um_info->idx++] = cpu_to_be64(loc_base);
+ buf[um_info->idx++] = cpu_to_be64(loc_end - loc_base);
+ (*cnt)++;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * kdump_setup_usable_lmb - This is a callback function that gets called by
+ * walk_drmem_lmbs for every LMB to set its
+ * usable memory ranges.
+ * @lmb: LMB info.
+ * @usm: linux,drconf-usable-memory property value.
+ * @data: Pointer to usable memory buffer and ranges info.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int kdump_setup_usable_lmb(struct drmem_lmb *lmb, const __be32 **usm,
+ void *data)
+{
+ struct umem_info *um_info;
+ uint64_t base, end, *buf;
+ int cnt, tmp_idx, ret;
+
+ /*
+ * kdump load isn't supported on kernels already booted with
+ * linux,drconf-usable-memory property.
+ */
+ if (*usm) {
+ pr_err("Trying kdump load from a kdump kernel?\n");
+ return -EINVAL;
+ }
+
+ um_info = data;
+ tmp_idx = um_info->idx;
+ buf = check_realloc_usable_mem(um_info, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ um_info->idx++;
+ um_info->buf = buf;
+ base = lmb->base_addr;
+ end = base + drmem_lmb_size();
+ ret = add_usable_mem(um_info, base, end, &cnt);
+ if (!ret)
+ um_info->buf[tmp_idx] = cpu_to_be64(cnt);
+
+ return ret;
+}
+
+/**
+ * get_node_path - Get the full path of the given node.
+ * @dn: Node.
+ * @path: Updated with the full path of the node.
+ *
+ * Returns nothing.
+ */
+static void get_node_path(struct device_node *dn, char *path)
+{
+ if (!dn)
+ return;
+
+ get_node_path(dn->parent, path);
+ sprintf(path, "/%s", dn->full_name);
+}
+
+/**
+ * get_node_pathlen - Get the full path length of the given node.
+ * @dn: Node.
+ *
+ * Returns the length of the full path of the node.
+ */
+static int get_node_pathlen(struct device_node *dn)
+{
+ int len = 0;
+
+ while (dn) {
+ len += strlen(dn->full_name) + 1;
+ dn = dn->parent;
+ }
+ len++;
+
+ return len;
+}
+
+/**
+ * add_usable_mem_property - Add usable memory property for the given
+ * memory node.
+ * @fdt: Flattened device tree for the kdump kernel.
+ * @dn: Memory node.
+ * @um_info: Usable memory buffer and ranges info.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int add_usable_mem_property(void *fdt, struct device_node *dn,
+ struct umem_info *um_info)
+{
+ int n_mem_addr_cells, n_mem_size_cells, node;
+ int i, len, ranges, cnt, ret;
+ uint64_t base, end, *buf;
+ const __be32 *prop;
+ char *pathname;
+
+ /* Allocate memory for node path */
+ pathname = kzalloc(ALIGN(get_node_pathlen(dn), 8), GFP_KERNEL);
+ if (!pathname)
+ return -ENOMEM;
+
+ /* Get the full path of the memory node */
+ get_node_path(dn, pathname);
+ pr_debug("Memory node path: %s\n", pathname);
+
+ /* Now that we know the path, find its offset in kdump kernel's fdt */
+ node = fdt_path_offset(fdt, pathname);
+ if (node < 0) {
+ pr_err("Malformed device tree: error reading %s\n",
+ pathname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Get the address & size cells */
+ n_mem_addr_cells = of_n_addr_cells(dn);
+ n_mem_size_cells = of_n_size_cells(dn);
+ pr_debug("address cells: %d, size cells: %d\n", n_mem_addr_cells,
+ n_mem_size_cells);
+
+ um_info->idx = 0;
+ buf = check_realloc_usable_mem(um_info, 2);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ um_info->buf = buf;
+
+ prop = of_get_property(dn, "reg", &len);
+ if (!prop || len <= 0) {
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * "reg" property represents sequence of (addr,size) duples
+ * each representing a memory range.
+ */
+ ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells);
+
+ for (i = 0; i < ranges; i++) {
+ base = of_read_number(prop, n_mem_addr_cells);
+ prop += n_mem_addr_cells;
+ end = base + of_read_number(prop, n_mem_size_cells);
+
+ ret = add_usable_mem(um_info, base, end, &cnt);
+ if (ret) {
+ ret = ret;
+ goto out;
+ }
+ }
+
+ /*
+ * No kdump kernel usable memory found in this memory node.
+ * Write (0,0) duple in linux,usable-memory property for
+ * this region to be ignored.
+ */
+ if (um_info->idx == 0) {
+ um_info->buf[0] = 0;
+ um_info->buf[1] = 0;
+ um_info->idx = 2;
+ }
+
+ ret = fdt_setprop(fdt, node, "linux,usable-memory", um_info->buf,
+ (um_info->idx * sizeof(*(um_info->buf))));
+
+out:
+ kfree(pathname);
+ return ret;
+}
+
+
+/**
+ * update_usable_mem_fdt - Updates kdump kernel's fdt with linux,usable-memory
+ * and linux,drconf-usable-memory DT properties as
+ * appropriate to restrict its memory usage.
+ * @fdt: Flattened device tree for the kdump kernel.
+ * @usable_mem: Usable memory ranges for kdump kernel.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int update_usable_mem_fdt(void *fdt, struct crash_mem *usable_mem)
+{
+ struct umem_info um_info;
+ struct device_node *dn;
+ int node, ret = 0;
+
+ if (!usable_mem) {
+ pr_err("Usable memory ranges for kdump kernel not found\n");
+ return -ENOENT;
+ }
+
+ node = fdt_path_offset(fdt, "/ibm,dynamic-reconfiguration-memory");
+ if (node == -FDT_ERR_NOTFOUND)
+ pr_debug("No dynamic reconfiguration memory found\n");
+ else if (node < 0) {
+ pr_err("Malformed device tree: error reading /ibm,dynamic-reconfiguration-memory.\n");
+ return -EINVAL;
+ }
+
+ um_info.size = 0;
+ um_info.idx = 0;
+ um_info.buf = NULL;
+ um_info.umrngs = usable_mem;
+
+ dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
+ if (dn) {
+ ret = walk_drmem_lmbs(dn, &um_info, kdump_setup_usable_lmb);
+ of_node_put(dn);
+
+ if (ret)
+ goto out;
+
+ ret = fdt_setprop(fdt, node, "linux,drconf-usable-memory",
+ um_info.buf,
+ (um_info.idx * sizeof(*(um_info.buf))));
+ if (ret) {
+ pr_err("Failed to set linux,drconf-usable-memory property");
+ goto out;
+ }
+ }
+
+ /*
+ * Walk through each memory node and set linux,usable-memory property
+ * for the corresponding node in kdump kernel's fdt.
+ */
+ for_each_node_by_type(dn, "memory") {
+ ret = add_usable_mem_property(fdt, dn, &um_info);
+ if (ret) {
+ pr_err("Failed to set linux,usable-memory property for %s node",
+ dn->full_name);
+ goto out;
+ }
+ }
+
+out:
+ kfree(um_info.buf);
+ return ret;
+}
+
+/**
* setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global
* variables and call setup_purgatory() to initialize
* common global variable.
@@ -287,6 +646,25 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code,
ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
fdt_load_addr);
if (ret)
+ goto out;
+
+ if (image->type == KEXEC_TYPE_CRASH) {
+ uint32_t my_run_at_load = 1;
+
+ /*
+ * Tell relocatable kernel to run at load address
+ * via the word meant for that at 0x5c.
+ */
+ ret = kexec_purgatory_get_set_symbol(image, "run_at_load",
+ &my_run_at_load,
+ sizeof(my_run_at_load),
+ false);
+ if (ret)
+ goto out;
+ }
+
+out:
+ if (ret)
pr_err("Failed to setup purgatory symbols");
return ret;
}
@@ -307,6 +685,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
unsigned long initrd_load_addr,
unsigned long initrd_len, const char *cmdline)
{
+ struct crash_mem *umem = NULL;
int chosen_node, ret;
/* Remove memory reservation for the current device tree. */
@@ -319,15 +698,32 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
return ret;
}
+ /*
+ * Restrict memory usage for kdump kernel by setting up
+ * usable memory ranges.
+ */
+ if (image->type == KEXEC_TYPE_CRASH) {
+ ret = get_usable_memory_ranges(&umem);
+ if (ret)
+ goto out;
+
+ ret = update_usable_mem_fdt(fdt, umem);
+ if (ret) {
+ pr_err("Error setting up usable-memory property for kdump kernel\n");
+ goto out;
+ }
+ }
+
ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len,
cmdline, &chosen_node);
if (ret)
- return ret;
+ goto out;
ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
if (ret)
pr_err("Failed to update device-tree with linux,booted-from-kexec\n");
-
+out:
+ kfree(umem);
return ret;
}
^ permalink raw reply related
* [PATCH 05/11] powerpc/drmem: make lmb walk a bit more flexible
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
Currently, numa & prom are the users of drmem lmb walk code. Loading
kdump with kexec_file also needs to walk the drmem LMBs to setup the
usable memory ranges for kdump kernel. But there are couple of issues
in using the code as is. One, walk_drmem_lmb() code is built into the
.init section currently, while kexec_file needs it later. Two, there
is no scope to pass data to the callback function for processing and/
or erroring out on certain conditions.
Fix that by, moving drmem LMB walk code out of .init section, adding
scope to pass data to the callback function and bailing out when
an error is encountered in the callback function.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/drmem.h | 9 ++--
arch/powerpc/kernel/prom.c | 13 +++---
arch/powerpc/mm/drmem.c | 87 +++++++++++++++++++++++++-------------
arch/powerpc/mm/numa.c | 13 +++---
4 files changed, 78 insertions(+), 44 deletions(-)
diff --git a/arch/powerpc/include/asm/drmem.h b/arch/powerpc/include/asm/drmem.h
index 414d209..17ccc64 100644
--- a/arch/powerpc/include/asm/drmem.h
+++ b/arch/powerpc/include/asm/drmem.h
@@ -90,13 +90,14 @@ static inline bool drmem_lmb_reserved(struct drmem_lmb *lmb)
}
u64 drmem_lmb_memory_max(void);
-void __init walk_drmem_lmbs(struct device_node *dn,
- void (*func)(struct drmem_lmb *, const __be32 **));
+int walk_drmem_lmbs(struct device_node *dn, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *));
int drmem_update_dt(void);
#ifdef CONFIG_PPC_PSERIES
-void __init walk_drmem_lmbs_early(unsigned long node,
- void (*func)(struct drmem_lmb *, const __be32 **));
+int __init
+walk_drmem_lmbs_early(unsigned long node, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *));
#endif
static inline void invalidate_lmb_associativity_index(struct drmem_lmb *lmb)
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 9cc49f2..7df78de 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -468,8 +468,9 @@ static bool validate_mem_limit(u64 base, u64 *size)
* This contains a list of memory blocks along with NUMA affinity
* information.
*/
-static void __init early_init_drmem_lmb(struct drmem_lmb *lmb,
- const __be32 **usm)
+static int __init early_init_drmem_lmb(struct drmem_lmb *lmb,
+ const __be32 **usm,
+ void *data)
{
u64 base, size;
int is_kexec_kdump = 0, rngs;
@@ -484,7 +485,7 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb,
*/
if ((lmb->flags & DRCONF_MEM_RESERVED) ||
!(lmb->flags & DRCONF_MEM_ASSIGNED))
- return;
+ return 0;
if (*usm)
is_kexec_kdump = 1;
@@ -499,7 +500,7 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb,
*/
rngs = dt_mem_next_cell(dt_root_size_cells, usm);
if (!rngs) /* there are no (base, size) duple */
- return;
+ return 0;
}
do {
@@ -524,6 +525,8 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb,
if (lmb->flags & DRCONF_MEM_HOTREMOVABLE)
memblock_mark_hotplug(base, size);
} while (--rngs);
+
+ return 0;
}
#endif /* CONFIG_PPC_PSERIES */
@@ -534,7 +537,7 @@ static int __init early_init_dt_scan_memory_ppc(unsigned long node,
#ifdef CONFIG_PPC_PSERIES
if (depth == 1 &&
strcmp(uname, "ibm,dynamic-reconfiguration-memory") == 0) {
- walk_drmem_lmbs_early(node, early_init_drmem_lmb);
+ walk_drmem_lmbs_early(node, NULL, early_init_drmem_lmb);
return 0;
}
#endif
diff --git a/arch/powerpc/mm/drmem.c b/arch/powerpc/mm/drmem.c
index 59327ce..b2eeea3 100644
--- a/arch/powerpc/mm/drmem.c
+++ b/arch/powerpc/mm/drmem.c
@@ -14,6 +14,8 @@
#include <asm/prom.h>
#include <asm/drmem.h>
+static int n_root_addr_cells, n_root_size_cells;
+
static struct drmem_lmb_info __drmem_info;
struct drmem_lmb_info *drmem_info = &__drmem_info;
@@ -189,12 +191,13 @@ int drmem_update_dt(void)
return rc;
}
-static void __init read_drconf_v1_cell(struct drmem_lmb *lmb,
+static void read_drconf_v1_cell(struct drmem_lmb *lmb,
const __be32 **prop)
{
const __be32 *p = *prop;
- lmb->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p);
+ lmb->base_addr = of_read_number(p, n_root_addr_cells);
+ p += n_root_addr_cells;
lmb->drc_index = of_read_number(p++, 1);
p++; /* skip reserved field */
@@ -205,29 +208,33 @@ static void __init read_drconf_v1_cell(struct drmem_lmb *lmb,
*prop = p;
}
-static void __init __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm,
- void (*func)(struct drmem_lmb *, const __be32 **))
+static int
+__walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *))
{
struct drmem_lmb lmb;
u32 i, n_lmbs;
+ int ret = 0;
n_lmbs = of_read_number(prop++, 1);
- if (n_lmbs == 0)
- return;
-
for (i = 0; i < n_lmbs; i++) {
read_drconf_v1_cell(&lmb, &prop);
- func(&lmb, &usm);
+ ret = func(&lmb, &usm, data);
+ if (ret)
+ break;
}
+
+ return ret;
}
-static void __init read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
+static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
const __be32 **prop)
{
const __be32 *p = *prop;
dr_cell->seq_lmbs = of_read_number(p++, 1);
- dr_cell->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p);
+ dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
+ p += n_root_addr_cells;
dr_cell->drc_index = of_read_number(p++, 1);
dr_cell->aa_index = of_read_number(p++, 1);
dr_cell->flags = of_read_number(p++, 1);
@@ -235,17 +242,16 @@ static void __init read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
*prop = p;
}
-static void __init __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm,
- void (*func)(struct drmem_lmb *, const __be32 **))
+static int
+__walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *))
{
struct of_drconf_cell_v2 dr_cell;
struct drmem_lmb lmb;
u32 i, j, lmb_sets;
+ int ret = 0;
lmb_sets = of_read_number(prop++, 1);
- if (lmb_sets == 0)
- return;
-
for (i = 0; i < lmb_sets; i++) {
read_drconf_v2_cell(&dr_cell, &prop);
@@ -259,21 +265,29 @@ static void __init __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm,
lmb.aa_index = dr_cell.aa_index;
lmb.flags = dr_cell.flags;
- func(&lmb, &usm);
+ ret = func(&lmb, &usm, data);
+ if (ret)
+ break;
}
}
+
+ return ret;
}
#ifdef CONFIG_PPC_PSERIES
-void __init walk_drmem_lmbs_early(unsigned long node,
- void (*func)(struct drmem_lmb *, const __be32 **))
+int __init walk_drmem_lmbs_early(unsigned long node, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *))
{
const __be32 *prop, *usm;
- int len;
+ int len, ret = -ENODEV;
prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
if (!prop || len < dt_root_size_cells * sizeof(__be32))
- return;
+ return ret;
+
+ /* Get the address & size cells */
+ n_root_addr_cells = dt_root_addr_cells;
+ n_root_size_cells = dt_root_size_cells;
drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
@@ -281,20 +295,21 @@ void __init walk_drmem_lmbs_early(unsigned long node,
prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
if (prop) {
- __walk_drmem_v1_lmbs(prop, usm, func);
+ ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
} else {
prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
&len);
if (prop)
- __walk_drmem_v2_lmbs(prop, usm, func);
+ ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
}
memblock_dump_all();
+ return ret;
}
#endif
-static int __init init_drmem_lmb_size(struct device_node *dn)
+static int init_drmem_lmb_size(struct device_node *dn)
{
const __be32 *prop;
int len;
@@ -303,12 +318,12 @@ static int __init init_drmem_lmb_size(struct device_node *dn)
return 0;
prop = of_get_property(dn, "ibm,lmb-size", &len);
- if (!prop || len < dt_root_size_cells * sizeof(__be32)) {
+ if (!prop || len < n_root_size_cells * sizeof(__be32)) {
pr_info("Could not determine LMB size\n");
return -1;
}
- drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
+ drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
return 0;
}
@@ -329,24 +344,36 @@ static const __be32 *of_get_usable_memory(struct device_node *dn)
return prop;
}
-void __init walk_drmem_lmbs(struct device_node *dn,
- void (*func)(struct drmem_lmb *, const __be32 **))
+int walk_drmem_lmbs(struct device_node *dn, void *data,
+ int (*func)(struct drmem_lmb *, const __be32 **, void *))
{
const __be32 *prop, *usm;
+ int ret = -ENODEV;
+
+ if (!of_root)
+ return ret;
+
+ /* Get the address & size cells */
+ of_node_get(of_root);
+ n_root_addr_cells = of_n_addr_cells(of_root);
+ n_root_size_cells = of_n_size_cells(of_root);
+ of_node_put(of_root);
if (init_drmem_lmb_size(dn))
- return;
+ return ret;
usm = of_get_usable_memory(dn);
prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
if (prop) {
- __walk_drmem_v1_lmbs(prop, usm, func);
+ ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
} else {
prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
if (prop)
- __walk_drmem_v2_lmbs(prop, usm, func);
+ ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
}
+
+ return ret;
}
static void __init init_drmem_v1_lmbs(const __be32 *prop)
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 9fcf2d1..88eb689 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -644,8 +644,9 @@ static inline int __init read_usm_ranges(const __be32 **usm)
* Extract NUMA information from the ibm,dynamic-reconfiguration-memory
* node. This assumes n_mem_{addr,size}_cells have been set.
*/
-static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb,
- const __be32 **usm)
+static int __init numa_setup_drmem_lmb(struct drmem_lmb *lmb,
+ const __be32 **usm,
+ void *data)
{
unsigned int ranges, is_kexec_kdump = 0;
unsigned long base, size, sz;
@@ -657,7 +658,7 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb,
*/
if ((lmb->flags & DRCONF_MEM_RESERVED)
|| !(lmb->flags & DRCONF_MEM_ASSIGNED))
- return;
+ return 0;
if (*usm)
is_kexec_kdump = 1;
@@ -669,7 +670,7 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb,
if (is_kexec_kdump) {
ranges = read_usm_ranges(usm);
if (!ranges) /* there are no (base, size) duple */
- return;
+ return 0;
}
do {
@@ -686,6 +687,8 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb,
if (sz)
memblock_set_node(base, sz, &memblock.memory, nid);
} while (--ranges);
+
+ return 0;
}
static int __init parse_numa_properties(void)
@@ -787,7 +790,7 @@ static int __init parse_numa_properties(void)
*/
memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
if (memory) {
- walk_drmem_lmbs(memory, numa_setup_drmem_lmb);
+ walk_drmem_lmbs(memory, NULL, numa_setup_drmem_lmb);
of_node_put(memory);
}
^ permalink raw reply related
* [PATCH 04/11] ppc64/kexec_file: avoid stomping memory used by special regions
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
crashkernel region could have an overlap with special memory regions
like opal, rtas, tce-table & such. These regions are referred to as
exclude memory ranges. Setup this ranges during image probe in order
to avoid them while finding the buffer for different kdump segments.
Implement kexec_locate_mem_hole_ppc64() that locates a memory hole
accounting for these ranges. Also, override arch_kexec_add_buffer()
to locate a memory hole & later call __kexec_add_buffer() function
with kbuf->mem set to skip the generic locate memory hole lookup.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/crashdump-ppc64.h | 10 +
arch/powerpc/include/asm/kexec.h | 7 -
arch/powerpc/kexec/elf_64.c | 7 +
arch/powerpc/kexec/file_load_64.c | 292 ++++++++++++++++++++++++++++
4 files changed, 312 insertions(+), 4 deletions(-)
create mode 100644 arch/powerpc/include/asm/crashdump-ppc64.h
diff --git a/arch/powerpc/include/asm/crashdump-ppc64.h b/arch/powerpc/include/asm/crashdump-ppc64.h
new file mode 100644
index 0000000..3596c25
--- /dev/null
+++ b/arch/powerpc/include/asm/crashdump-ppc64.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _ARCH_POWERPC_KEXEC_CRASHDUMP_PPC64_H
+#define _ARCH_POWERPC_KEXEC_CRASHDUMP_PPC64_H
+
+/* min & max addresses for kdump load segments */
+#define KDUMP_BUF_MIN (crashk_res.start)
+#define KDUMP_BUF_MAX ((crashk_res.end < ppc64_rma_size) ? \
+ crashk_res.end : (ppc64_rma_size - 1))
+
+#endif /* __ARCH_POWERPC_KEXEC_CRASHDUMP_PPC64_H */
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index 7008ea1..bf47a01 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -100,14 +100,16 @@ void relocate_new_kernel(unsigned long indirection_page, unsigned long reboot_co
#ifdef CONFIG_KEXEC_FILE
extern const struct kexec_file_ops kexec_elf64_ops;
-#ifdef CONFIG_IMA_KEXEC
#define ARCH_HAS_KIMAGE_ARCH
struct kimage_arch {
+ struct crash_mem *exclude_ranges;
+
+#ifdef CONFIG_IMA_KEXEC
phys_addr_t ima_buffer_addr;
size_t ima_buffer_size;
-};
#endif
+};
int setup_purgatory(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
@@ -125,6 +127,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
unsigned long initrd_load_addr,
unsigned long initrd_len, const char *cmdline);
#endif /* CONFIG_PPC64 */
+
#endif /* CONFIG_KEXEC_FILE */
#else /* !CONFIG_KEXEC_CORE */
diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c
index 23ad04c..c695f94 100644
--- a/arch/powerpc/kexec/elf_64.c
+++ b/arch/powerpc/kexec/elf_64.c
@@ -22,6 +22,7 @@
#include <linux/of_fdt.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <asm/crashdump-ppc64.h>
static void *elf64_load(struct kimage *image, char *kernel_buf,
unsigned long kernel_len, char *initrd,
@@ -46,6 +47,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
if (ret)
goto out;
+ if (image->type == KEXEC_TYPE_CRASH) {
+ /* min & max buffer values for kdump case */
+ kbuf.buf_min = pbuf.buf_min = KDUMP_BUF_MIN;
+ kbuf.buf_max = pbuf.buf_max = KDUMP_BUF_MAX;
+ }
+
ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
if (ret)
goto out;
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index e6bff960..f1d7160 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -17,6 +17,8 @@
#include <linux/kexec.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
+#include <asm/kexec_ranges.h>
+#include <asm/crashdump-ppc64.h>
const struct kexec_file_ops * const kexec_file_loaders[] = {
&kexec_elf64_ops,
@@ -24,6 +26,247 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
};
/**
+ * get_exclude_memory_ranges - Get exclude memory ranges. This list includes
+ * regions like opal/rtas, tce-table, initrd,
+ * kernel, htab which should be avoided while
+ * setting up kexec load segments.
+ * @mem_ranges: Range list to add the memory ranges to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int get_exclude_memory_ranges(struct crash_mem **mem_ranges)
+{
+ int ret;
+
+ ret = add_tce_mem_ranges(mem_ranges);
+ if (ret)
+ goto out;
+
+ ret = add_initrd_mem_range(mem_ranges);
+ if (ret)
+ goto out;
+
+ ret = add_htab_mem_range(mem_ranges);
+ if (ret)
+ goto out;
+
+ ret = add_kernel_mem_range(mem_ranges);
+ if (ret)
+ goto out;
+
+ ret = add_rtas_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_opal_mem_range(mem_ranges, false);
+ if (ret)
+ goto out;
+
+ ret = add_reserved_ranges(mem_ranges);
+ if (ret)
+ goto out;
+
+ /* exclude memory ranges should be sorted for easy lookup */
+ sort_memory_ranges(*mem_ranges);
+out:
+ if (ret)
+ pr_err("Failed to setup exclude memory ranges\n");
+ return ret;
+}
+
+/**
+ * __locate_mem_hole_ppc64 - Tests if the memory hole between buf_min & buf_max
+ * is large enough for the buffer. If true, sets
+ * kbuf->mem to the buffer.
+ * @kbuf: Buffer contents and memory parameters.
+ * @buf_min: Minimum address for the buffer.
+ * @buf_max: Maximum address for the buffer.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int __locate_mem_hole_ppc64(struct kexec_buf *kbuf,
+ u64 buf_min, u64 buf_max)
+{
+ int ret = -EADDRNOTAVAIL;
+
+ buf_min = ALIGN(buf_min, kbuf->buf_align);
+
+ if (buf_min < buf_max &&
+ (buf_max - buf_min + 1) >= kbuf->memsz) {
+ /*
+ * Suitable memory range found. Set kbuf->mem here to skip
+ * locate memory hole routine in __kexec_add_buffer() call.
+ */
+ ret = 0;
+ if (kbuf->top_down)
+ kbuf->mem = ALIGN_DOWN(buf_max - kbuf->memsz + 1,
+ kbuf->buf_align);
+ else
+ kbuf->mem = buf_min;
+ }
+
+ return ret;
+}
+
+/**
+ * locate_mem_hole_top_down_ppc64 - Skip special memory regions to find a
+ * suitable buffer with top down approach.
+ * @kbuf: Buffer contents and memory parameters.
+ * @buf_min: Minimum address for the buffer.
+ * @buf_max: Maximum address for the buffer.
+ * @emem: Exclude memory ranges.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int locate_mem_hole_top_down_ppc64(struct kexec_buf *kbuf,
+ u64 buf_min, u64 buf_max,
+ const struct crash_mem *emem)
+{
+ int i, ret = 0, err = -EADDRNOTAVAIL;
+ u64 start, end, tmin, tmax;
+
+ tmax = buf_max;
+ for (i = (emem->nr_ranges - 1); i >= 0; i--) {
+ start = emem->ranges[i].start;
+ end = emem->ranges[i].end;
+
+ if (start > tmax)
+ continue;
+
+ if (end < tmax) {
+ tmin = (end < buf_min ? buf_min : end + 1);
+ ret = __locate_mem_hole_ppc64(kbuf, tmin, tmax);
+ if (!ret)
+ return 0;
+ }
+
+ tmax = start - 1;
+
+ if (tmax < buf_min) {
+ ret = err;
+ break;
+ }
+ ret = 0;
+ }
+
+ if (!ret) {
+ tmin = buf_min;
+ ret = __locate_mem_hole_ppc64(kbuf, tmin, tmax);
+ }
+ return ret;
+}
+
+/**
+ * locate_mem_hole_bottom_up_ppc64 - Skip special memory regions to find a
+ * suitable buffer with bottom up approach.
+ * @kbuf: Buffer contents and memory parameters.
+ * @buf_min: Minimum address for the buffer.
+ * @buf_max: Maximum address for the buffer.
+ * @emem: Exclude memory ranges.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf,
+ u64 buf_min, u64 buf_max,
+ const struct crash_mem *emem)
+{
+ int i, ret = 0, err = -EADDRNOTAVAIL;
+ u64 start, end, tmin, tmax;
+
+ tmin = buf_min;
+ for (i = 0; i < emem->nr_ranges; i++) {
+ start = emem->ranges[i].start;
+ end = emem->ranges[i].end;
+
+ if (end < tmin)
+ continue;
+
+ if (start > tmin) {
+ tmax = (start > buf_max ? buf_max : start - 1);
+ ret = __locate_mem_hole_ppc64(kbuf, tmin, tmax);
+ if (!ret)
+ return 0;
+ }
+
+ tmin = end + 1;
+
+ if (tmin > buf_max) {
+ ret = err;
+ break;
+ }
+ ret = 0;
+ }
+
+ if (!ret) {
+ tmax = buf_max;
+ ret = __locate_mem_hole_ppc64(kbuf, tmin, tmax);
+ }
+ return ret;
+}
+
+/**
+ * kexec_locate_mem_hole_ppc64 - Skip special memory regions like rtas,
+ * tce-table, opal, reserved-ranges & such
+ * (exclude memory ranges) as they can't be
+ * used for kexec segment buffer. Use buf_min
+ * & buf_max fields in kexec_buf structure to
+ * skip regions. Sets kbuf->mem when a
+ * suitable memory hole is found.
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int kexec_locate_mem_hole_ppc64(struct kexec_buf *kbuf)
+{
+ struct crash_mem **emem;
+ u64 buf_min, buf_max;
+ int ret;
+
+ /*
+ * Use the locate_mem_hole logic in kexec_add_buffer() for regular
+ * kexec_file_load syscall
+ */
+ if (kbuf->image->type != KEXEC_TYPE_CRASH)
+ return 0;
+
+ /* Look up the exclude ranges list while locating the memory hole */
+ emem = &(kbuf->image->arch.exclude_ranges);
+ if (!(*emem) || ((*emem)->nr_ranges == 0)) {
+ pr_warn("No exclude range list. Using the default locate mem hole method\n");
+ return 0;
+ }
+
+ /* Ensure minimum alignment needed for segments. */
+ kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE);
+ kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
+
+ /* Segments for kdump kernel should be within crashkernel region */
+ buf_min = (kbuf->buf_min < crashk_res.start ?
+ crashk_res.start : kbuf->buf_min);
+ buf_max = (kbuf->buf_max > crashk_res.end ?
+ crashk_res.end : kbuf->buf_max);
+
+ if (buf_min > buf_max) {
+ pr_err("Invalid buffer min and/or max values\n");
+ return -EINVAL;
+ }
+
+ if (kbuf->top_down)
+ ret = locate_mem_hole_top_down_ppc64(kbuf, buf_min, buf_max,
+ *emem);
+ else
+ ret = locate_mem_hole_bottom_up_ppc64(kbuf, buf_min, buf_max,
+ *emem);
+
+ /* Add the buffer allocated to the exclude list for the next lookup */
+ if (!ret) {
+ add_mem_range(emem, kbuf->mem, kbuf->memsz);
+ sort_memory_ranges(*emem);
+ }
+ return ret;
+}
+
+/**
* setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global
* variables and call setup_purgatory() to initialize
* common global variable.
@@ -89,6 +332,29 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
}
/**
+ * arch_kexec_add_buffer - Locate memory hole before calling kexec_add_buffer().
+ * All kexec_add_buffer() callers should use this
+ * function instead.
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int arch_kexec_add_buffer(struct kexec_buf *kbuf)
+{
+ int ret;
+
+ ret = kexec_locate_mem_hole_ppc64(kbuf);
+ if (ret)
+ goto out;
+
+ ret = __kexec_add_buffer(kbuf);
+out:
+ if (ret)
+ pr_err("Failed to add buffer of size %lu\n", kbuf->memsz);
+ return ret;
+}
+
+/**
* arch_kexec_kernel_image_probe - Does additional handling needed to setup
* kexec segments.
* @image: kexec image being loaded.
@@ -100,9 +366,31 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
unsigned long buf_len)
{
- /* We don't support crash kernels yet. */
- if (image->type == KEXEC_TYPE_CRASH)
+ if (image->type == KEXEC_TYPE_CRASH) {
+ int ret;
+
+ /* Get exclude memory ranges needed for setting up kdump segments */
+ ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges));
+ if (ret)
+ pr_err("Failed to setup exclude memory ranges for buffer lookup\n");
+ /* Return this until all changes for panic kernel are in */
return -EOPNOTSUPP;
+ }
return kexec_image_probe_default(image, buf, buf_len);
}
+
+/**
+ * arch_kimage_file_post_load_cleanup - Frees up all the allocations done
+ * while loading the image.
+ * @image: kexec image being loaded.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int arch_kimage_file_post_load_cleanup(struct kimage *image)
+{
+ kfree(image->arch.exclude_ranges);
+ image->arch.exclude_ranges = NULL;
+
+ return kexec_image_post_load_cleanup_default(image);
+}
^ permalink raw reply related
* [PATCH 03/11] powerpc/kexec_file: add helper functions for getting memory ranges
From: Hari Bathini @ 2020-06-26 19:05 UTC (permalink / raw)
To: Michael Ellerman, Andrew Morton
Cc: Pingfan Liu, Kexec-ml, Petr Tesarik, Mahesh J Salgaonkar,
Sourabh Jain, lkml, linuxppc-dev, Mimi Zohar, Vivek Goyal,
Dave Young, Thiago Jung Bauermann, Eric Biederman
In-Reply-To: <159319825403.16351.7253978047621755765.stgit@hbathini.in.ibm.com>
In kexec case, the kernel to be loaded uses the same memory layout as
the running kernel. So, passing on the DT of the running kernel would
be good enough.
But in case of kdump, different memory ranges are needed to manage
loading the kdump kernel, booting into it and exporting the elfcore
of the crashing kernel. The ranges are exlude memory ranges, usable
memory ranges, reserved memory ranges and crash memory ranges.
Exclude memory ranges specify the list of memory ranges to avoid while
loading kdump segments. Usable memory ranges list the memory ranges
that could be used for booting kdump kernel. Reserved memory ranges
list the memory regions for the loading kernel's reserve map. Crash
memory ranges list the memory ranges to be exported as the crashing
kernel's elfcore.
Add helper functions for setting up the above mentioned memory ranges.
This helpers facilitate in understanding the subsequent changes better
and make it easy to setup the different memory ranges listed above, as
and when appropriate.
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/include/asm/kexec_ranges.h | 18 +
arch/powerpc/kexec/Makefile | 2
arch/powerpc/kexec/ranges.c | 387 +++++++++++++++++++++++++++++++
3 files changed, 406 insertions(+), 1 deletion(-)
create mode 100644 arch/powerpc/include/asm/kexec_ranges.h
create mode 100644 arch/powerpc/kexec/ranges.c
diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h
new file mode 100644
index 0000000..29eaa65
--- /dev/null
+++ b/arch/powerpc/include/asm/kexec_ranges.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _ARCH_POWERPC_KEXEC_RANGES_H
+#define _ARCH_POWERPC_KEXEC_RANGES_H
+
+#define MEM_RANGE_CHUNK_SZ 2048 /* Memory ranges size chunk */
+
+struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges);
+int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size);
+int add_tce_mem_ranges(struct crash_mem **mem_ranges);
+int add_initrd_mem_range(struct crash_mem **mem_ranges);
+int add_htab_mem_range(struct crash_mem **mem_ranges);
+int add_kernel_mem_range(struct crash_mem **mem_ranges);
+int add_rtas_mem_range(struct crash_mem **mem_ranges, bool within_crashkernel);
+int add_opal_mem_range(struct crash_mem **mem_ranges, bool within_crashkernel);
+int add_reserved_ranges(struct crash_mem **mem_ranges);
+void sort_memory_ranges(struct crash_mem *mrngs);
+
+#endif /* _ARCH_POWERPC_KEXEC_RANGES_H */
diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile
index 67c3553..4aff684 100644
--- a/arch/powerpc/kexec/Makefile
+++ b/arch/powerpc/kexec/Makefile
@@ -7,7 +7,7 @@ obj-y += core.o crash.o core_$(BITS).o
obj-$(CONFIG_PPC32) += relocate_32.o
-obj-$(CONFIG_KEXEC_FILE) += file_load.o file_load_$(BITS).o elf_$(BITS).o
+obj-$(CONFIG_KEXEC_FILE) += file_load.o ranges.o file_load_$(BITS).o elf_$(BITS).o
ifdef CONFIG_HAVE_IMA_KEXEC
ifdef CONFIG_IMA
diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c
new file mode 100644
index 0000000..f21ddea
--- /dev/null
+++ b/arch/powerpc/kexec/ranges.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * powerpc code to implement the kexec_file_load syscall
+ *
+ * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
+ * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
+ * Copyright (C) 2020 IBM Corporation
+ *
+ * Based on kexec-tools' kexec-ppc64.c, fs2dt.c.
+ * Heavily modified for the kernel by
+ * Hari Bathini <hbathini@linux.ibm.com>.
+ */
+
+#include <linux/kexec.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <asm/sections.h>
+#include <asm/kexec_ranges.h>
+
+/**
+ * get_max_nr_ranges - Get the max no. of ranges crash_mem structure
+ * could hold, given the size allocated for it.
+ * @size: Allocation size of crash_mem structure.
+ *
+ * Returns the maximum no. of ranges.
+ */
+static inline unsigned int get_max_nr_ranges(size_t size)
+{
+ return ((size - sizeof(struct crash_mem)) /
+ sizeof(struct crash_mem_range));
+}
+
+/**
+ * get_mem_rngs_size - Get the allocated size of mrngs based on
+ * max_nr_ranges and chunk size.
+ * @mrngs: Memory ranges.
+ *
+ * Returns the maximum no. of ranges.
+ */
+static inline size_t get_mem_rngs_size(struct crash_mem *mrngs)
+{
+ size_t size;
+
+ if (!mrngs)
+ return 0;
+
+ size = (sizeof(struct crash_mem) +
+ (mrngs->max_nr_ranges * sizeof(struct crash_mem_range)));
+
+ /*
+ * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ.
+ * So, align to get the actual length.
+ */
+ return ALIGN(size, MEM_RANGE_CHUNK_SZ);
+}
+
+/**
+ * __add_mem_range - add a memory range to memory ranges list.
+ * @mem_ranges: Range list to add the memory range to.
+ * @base: Base address of the range to add.
+ * @size: Size of the memory range to add.
+ *
+ * (Re)allocates memory, if needed.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+static int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size)
+{
+ struct crash_mem *mrngs = *mem_ranges;
+
+ if ((mrngs == NULL) || (mrngs->nr_ranges == mrngs->max_nr_ranges)) {
+ mrngs = realloc_mem_ranges(mem_ranges);
+ if (!mrngs)
+ return -ENOMEM;
+ }
+
+ mrngs->ranges[mrngs->nr_ranges].start = base;
+ mrngs->ranges[mrngs->nr_ranges].end = base + size - 1;
+ mrngs->nr_ranges++;
+ return 0;
+}
+
+/**
+ * realloc_mem_ranges - reallocate mem_ranges with size incremented
+ * by MEM_RANGE_CHUNK_SZ. Frees up the old memory,
+ * if memory allocation fails.
+ * @mem_ranges: Memory ranges to reallocate.
+ *
+ * Returns pointer to reallocated memory on success, NULL otherwise.
+ */
+struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges)
+{
+ struct crash_mem *mrngs = *mem_ranges;
+ unsigned int nr_ranges;
+ size_t size;
+
+ size = get_mem_rngs_size(mrngs);
+ nr_ranges = mrngs ? mrngs->nr_ranges : 0;
+
+ size += MEM_RANGE_CHUNK_SZ;
+ mrngs = krealloc(*mem_ranges, size, GFP_KERNEL);
+ if (!mrngs) {
+ kfree(*mem_ranges);
+ *mem_ranges = NULL;
+ return NULL;
+ }
+
+ mrngs->nr_ranges = nr_ranges;
+ mrngs->max_nr_ranges = get_max_nr_ranges(size);
+ *mem_ranges = mrngs;
+
+ return mrngs;
+}
+
+/**
+ * add_mem_range - Updates existing memory range, if there is an overlap.
+ * Else, adds a new memory range.
+ * @mem_ranges: Range list to add the memory range to.
+ * @base: Base address of the range to add.
+ * @size: Size of the memory range to add.
+ *
+ * (Re)allocates memory, if needed.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size)
+{
+ struct crash_mem *mrngs = *mem_ranges;
+ u64 mstart, mend, end;
+ unsigned int i;
+
+ if (!size)
+ return 0;
+
+ end = base + size - 1;
+
+ if ((mrngs == NULL) || (mrngs->nr_ranges == 0))
+ return __add_mem_range(mem_ranges, base, size);
+
+ for (i = 0; i < mrngs->nr_ranges; i++) {
+ mstart = mrngs->ranges[i].start;
+ mend = mrngs->ranges[i].end;
+ if (base < mend && end > mstart) {
+ if (base < mstart)
+ mrngs->ranges[i].start = base;
+ if (end > mend)
+ mrngs->ranges[i].end = end;
+ return 0;
+ }
+ }
+
+ return __add_mem_range(mem_ranges, base, size);
+}
+
+/**
+ * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list.
+ * @mem_ranges: Range list to add the memory range(s) to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_tce_mem_ranges(struct crash_mem **mem_ranges)
+{
+ struct device_node *dn;
+ int ret;
+
+ for_each_node_by_type(dn, "pci") {
+ u64 base;
+ u32 size;
+
+ ret = of_property_read_u64(dn, "linux,tce-base", &base);
+ ret |= of_property_read_u32(dn, "linux,tce-size", &size);
+ if (!ret)
+ continue;
+
+ ret = add_mem_range(mem_ranges, base, size);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * add_initrd_mem_range - Adds initrd range to the given memory ranges list,
+ * if the initrd was retained.
+ * @mem_ranges: Range list to add the memory range to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_initrd_mem_range(struct crash_mem **mem_ranges)
+{
+ u64 base, end;
+ int ret = 0;
+ char *str;
+
+ /* This range means something only if initrd was retained */
+ str = strstr(saved_command_line, "retain_initrd");
+ if (!str)
+ return 0;
+
+ ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base);
+ ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end);
+ if (!ret)
+ ret = add_mem_range(mem_ranges, base, end - base + 1);
+ return ret;
+}
+
+/**
+ * add_htab_mem_range - Adds htab range to the given memory ranges list,
+ * if it exists
+ * @mem_ranges: Range list to add the memory range to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_htab_mem_range(struct crash_mem **mem_ranges)
+{
+#ifdef CONFIG_PPC_BOOK3S_64
+ int ret;
+
+ if (!htab_address)
+ return 0;
+
+ ret = add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes);
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * add_kernel_mem_range - Adds kernel text region to the given
+ * memory ranges list.
+ * @mem_ranges: Range list to add the memory range to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_kernel_mem_range(struct crash_mem **mem_ranges)
+{
+ int ret;
+
+ ret = add_mem_range(mem_ranges, 0, __pa(_end));
+ return ret;
+}
+
+/**
+ * add_rtas_mem_range - Adds RTAS region to the given memory ranges list.
+ * @mem_ranges: Range list to add the memory range to.
+ * @within_crashkernel: If true, adds only the part of the region within
+ * crashkernel range. Otherwise, all the region is added.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_rtas_mem_range(struct crash_mem **mem_ranges, bool within_crashkernel)
+{
+ struct device_node *dn;
+ int ret = 0;
+
+ dn = of_find_node_by_path("/rtas");
+ if (dn) {
+ u32 base, size;
+
+ ret = of_property_read_u32(dn, "linux,rtas-base", &base);
+ ret |= of_property_read_u32(dn, "rtas-size", &size);
+
+ if (ret)
+ return ret;
+
+ if (within_crashkernel) {
+ if (overlaps_crashkernel(base, size)) {
+ if (base < crashk_res.start)
+ base = crashk_res.start;
+ if (crashk_res.end < (base + size - 1))
+ size = crashk_res.end - base + 1;
+
+ ret = add_mem_range(mem_ranges, base, size);
+ }
+ } else
+ ret = add_mem_range(mem_ranges, base, size);
+ }
+ return ret;
+}
+
+/**
+ * add_opal_mem_range - Adds OPAL region to the given memory ranges list.
+ * @mem_ranges: Range list to add the memory range to.
+ * @within_crashkernel: If true, adds only the part of the region within
+ * crashkernel range. Otherwise, all the region is added.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_opal_mem_range(struct crash_mem **mem_ranges, bool within_crashkernel)
+{
+ struct device_node *dn;
+ int ret = 0;
+
+ dn = of_find_node_by_path("/ibm,opal");
+ if (dn) {
+ u64 base, size;
+
+ ret = of_property_read_u64(dn, "opal-base-address", &base);
+ ret |= of_property_read_u64(dn, "opal-runtime-size", &size);
+
+ if (ret)
+ return ret;
+
+ if (within_crashkernel) {
+ if (overlaps_crashkernel(base, size)) {
+ if (base < crashk_res.start)
+ base = crashk_res.start;
+ if (crashk_res.end < (base + size - 1))
+ size = crashk_res.end - base + 1;
+
+ ret = add_mem_range(mem_ranges, base, size);
+ }
+ } else
+ ret = add_mem_range(mem_ranges, base, size);
+ }
+ return ret;
+}
+
+/**
+ * add_reserved_ranges - Adds "/reserved-ranges" regions exported by f/w
+ * to the given memory ranges list.
+ * @mem_ranges: Range list to add the memory ranges to.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int add_reserved_ranges(struct crash_mem **mem_ranges)
+{
+ int i, len, ret = 0;
+ const __be32 *prop;
+
+ prop = of_get_property(of_root, "reserved-ranges", &len);
+ if (!prop)
+ return 0;
+
+ /*
+ * Each reserved range is an (address,size) pair, 2 cells each,
+ * totalling 4 cells per range.
+ */
+ for (i = 0; i < len / (sizeof(*prop) * 4); i++) {
+ u64 base, size;
+
+ base = of_read_number(prop + (i * 4) + 0, 2);
+ size = of_read_number(prop + (i * 4) + 2, 2);
+
+ ret = add_mem_range(mem_ranges, base, size);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * sort_memory_ranges - Sorts the given memory ranges list.
+ * @mem_ranges: Range list to sort.
+ *
+ * Returns nothing.
+ */
+void sort_memory_ranges(struct crash_mem *mrngs)
+{
+ struct crash_mem_range *rngs;
+ struct crash_mem_range rng;
+ int i, j, idx;
+
+ if (!mrngs)
+ return;
+
+ /* Sort the ranges in-place */
+ rngs = &mrngs->ranges[0];
+ for (i = 0; i < mrngs->nr_ranges; i++) {
+ idx = i;
+ for (j = (i + 1); j < mrngs->nr_ranges; j++) {
+ if (rngs[idx].start > rngs[j].start)
+ idx = j;
+ }
+ if (idx != i) {
+ rng = rngs[idx];
+ rngs[idx] = rngs[i];
+ rngs[i] = rng;
+ }
+ }
+}
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox