From patchwork Mon Feb 29 12:47:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Jones X-Patchwork-Id: 589917 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 866CF140B0E for ; Mon, 29 Feb 2016 23:49:11 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752462AbcB2MtK (ORCPT ); Mon, 29 Feb 2016 07:49:10 -0500 Received: from mx1.redhat.com ([209.132.183.28]:45295 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752709AbcB2MtH (ORCPT ); Mon, 29 Feb 2016 07:49:07 -0500 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (Postfix) with ESMTPS id 83B7264363; Mon, 29 Feb 2016 12:49:07 +0000 (UTC) Received: from hawk.localdomain.com (ovpn-116-111.ams2.redhat.com [10.36.116.111]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1TClvMA024439; Mon, 29 Feb 2016 07:49:03 -0500 From: Andrew Jones To: kvm@vger.kernel.org, kvm-ppc@vger.kernel.org Cc: thuth@redhat.com, dgibson@redhat.com, david@gibson.dropbear.id.au, agraf@suse.de, lvivier@redhat.com, pbonzini@redhat.com, rkrcmar@redhat.com Subject: [kvm-unit-tests PATCH v7 13/18] powerpc/ppc64: adapt arm's setup Date: Mon, 29 Feb 2016 13:47:47 +0100 Message-Id: <1456750072-7524-14-git-send-email-drjones@redhat.com> In-Reply-To: <1456750072-7524-1-git-send-email-drjones@redhat.com> References: <1456750072-7524-1-git-send-email-drjones@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 29 Feb 2016 12:49:07 +0000 (UTC) Sender: kvm-ppc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm-ppc@vger.kernel.org Copy arm's setup code (also DT based) over to powerpc, adapting it a bit. Also bring over arm's setup selftest, giving powerpc its first test. The largest change from arm's setup.c is that instead of using a hardcoded SMP_CACHE_BYTES, cpu_set() is extended to extract the icache and dcache line sizes from the cpu DT nodes. That change also requires that we call cpu_init() before mem_init() in setup(). Signed-off-by: Andrew Jones Reviewed-by: Thomas Huth Tested-by: Laurent Vivier Reviewed-by: David Gibson --- lib/powerpc/asm/setup.h | 29 ++++++++++ lib/powerpc/setup.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/ppc64/asm/setup.h | 1 + powerpc/Makefile.common | 1 + powerpc/cstart64.S | 16 +++++- powerpc/selftest.c | 63 +++++++++++++++++++- 6 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 lib/powerpc/asm/setup.h create mode 100644 lib/powerpc/setup.c create mode 100644 lib/ppc64/asm/setup.h diff --git a/lib/powerpc/asm/setup.h b/lib/powerpc/asm/setup.h new file mode 100644 index 0000000000000..29a6d7d5f705a --- /dev/null +++ b/lib/powerpc/asm/setup.h @@ -0,0 +1,29 @@ +#ifndef _ASMPOWERPC_SETUP_H_ +#define _ASMPOWERPC_SETUP_H_ +/* + * Copyright (C) 2016, Red Hat Inc, Andrew Jones + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include +#include /* phys_addr_t */ + +#define NR_CPUS 8 /* arbitrarily set for now */ +extern u32 cpus[NR_CPUS]; +extern int nr_cpus; + +#define NR_MEM_REGIONS 8 +#define MR_F_PRIMARY (1U << 0) +struct mem_region { + phys_addr_t start; + phys_addr_t end; + unsigned int flags; +}; +extern struct mem_region mem_regions[NR_MEM_REGIONS]; +extern phys_addr_t __physical_start, __physical_end; +extern unsigned __icache_bytes, __dcache_bytes; + +#define PHYSICAL_START (__physical_start) +#define PHYSICAL_END (__physical_end) + +#endif /* _ASMPOWERPC_SETUP_H_ */ diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c new file mode 100644 index 0000000000000..0c0c882a44a10 --- /dev/null +++ b/lib/powerpc/setup.c @@ -0,0 +1,150 @@ +/* + * Initialize machine setup information and I/O. + * + * After running setup() unit tests may query how many cpus they have + * (nr_cpus), how much memory they have (PHYSICAL_END - PHYSICAL_START), + * may use dynamic memory allocation (malloc, etc.), printf, and exit. + * Finally, argc and argv are also ready to be passed to main(). + * + * Copyright (C) 2016, Red Hat Inc, Andrew Jones + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include +#include +#include +#include +#include +#include + +extern unsigned long stacktop; +extern void io_init(void); +extern void setup_args(const char *args); + +u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) }; +int nr_cpus; + +struct mem_region mem_regions[NR_MEM_REGIONS]; +phys_addr_t __physical_start, __physical_end; +unsigned __icache_bytes, __dcache_bytes; + +struct cpu_set_params { + unsigned icache_bytes; + unsigned dcache_bytes; +}; + +static void cpu_set(int fdtnode, u32 regval, void *info) +{ + static bool read_common_info = false; + struct cpu_set_params *params = info; + int cpu = nr_cpus++; + + if (cpu >= NR_CPUS) { + printf("Number cpus exceeds maximum supported (%d).\n", + NR_CPUS); + assert(0); + } + cpus[cpu] = regval; + + if (!read_common_info) { + const struct fdt_property *prop; + u32 *data; + + prop = fdt_get_property(dt_fdt(), fdtnode, + "i-cache-line-size", NULL); + assert(prop != NULL); + data = (u32 *)prop->data; + params->icache_bytes = fdt32_to_cpu(*data); + + prop = fdt_get_property(dt_fdt(), fdtnode, + "d-cache-line-size", NULL); + assert(prop != NULL); + data = (u32 *)prop->data; + params->dcache_bytes = fdt32_to_cpu(*data); + + read_common_info = true; + } +} + +static void cpu_init(void) +{ + struct cpu_set_params params; + int ret; + + nr_cpus = 0; + ret = dt_for_each_cpu_node(cpu_set, ¶ms); + assert(ret == 0); + __icache_bytes = params.icache_bytes; + __dcache_bytes = params.dcache_bytes; +} + +static void mem_init(phys_addr_t freemem_start) +{ + struct dt_pbus_reg regs[NR_MEM_REGIONS]; + struct mem_region primary, mem = { + .start = (phys_addr_t)-1, + }; + int nr_regs, i; + + nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS); + assert(nr_regs > 0); + + primary.end = 0; + + for (i = 0; i < nr_regs; ++i) { + mem_regions[i].start = regs[i].addr; + mem_regions[i].end = regs[i].addr + regs[i].size; + + /* + * pick the region we're in for our primary region + */ + if (freemem_start >= mem_regions[i].start + && freemem_start < mem_regions[i].end) { + mem_regions[i].flags |= MR_F_PRIMARY; + primary = mem_regions[i]; + } + + /* + * set the lowest and highest addresses found, + * ignoring potential gaps + */ + if (mem_regions[i].start < mem.start) + mem.start = mem_regions[i].start; + if (mem_regions[i].end > mem.end) + mem.end = mem_regions[i].end; + } + assert(primary.end != 0); +// assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK)); + + __physical_start = mem.start; /* PHYSICAL_START */ + __physical_end = mem.end; /* PHYSICAL_END */ + + phys_alloc_init(freemem_start, primary.end - freemem_start); + phys_alloc_set_minimum_alignment(__icache_bytes > __dcache_bytes + ? __icache_bytes : __dcache_bytes); +} + +void setup(const void *fdt) +{ + const char *bootargs; + u32 fdt_size; + int ret; + + /* + * Move the fdt to just above the stack. The free memory + * then starts just after the fdt. + */ + fdt_size = fdt_totalsize(fdt); + ret = fdt_move(fdt, &stacktop, fdt_size); + assert(ret == 0); + ret = dt_init(&stacktop); + assert(ret == 0); + + cpu_init(); + mem_init(PAGE_ALIGN((unsigned long)&stacktop + fdt_size)); + io_init(); + + ret = dt_get_bootargs(&bootargs); + assert(ret == 0); + setup_args(bootargs); +} diff --git a/lib/ppc64/asm/setup.h b/lib/ppc64/asm/setup.h new file mode 100644 index 0000000000000..20192985928a4 --- /dev/null +++ b/lib/ppc64/asm/setup.h @@ -0,0 +1 @@ +#include "../../powerpc/asm/setup.h" diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common index b21e3933d0643..539bd33d1c309 100644 --- a/powerpc/Makefile.common +++ b/powerpc/Makefile.common @@ -26,6 +26,7 @@ cflatobjs += lib/alloc.o cflatobjs += lib/devicetree.o cflatobjs += lib/powerpc/io.o cflatobjs += lib/powerpc/hcall.o +cflatobjs += lib/powerpc/setup.o libgcc := $(shell $(CC) $(machine) --print-libgcc-file-name) diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S index 1884d79871ba5..526452835754f 100644 --- a/powerpc/cstart64.S +++ b/powerpc/cstart64.S @@ -20,11 +20,17 @@ .section .init +/* + * start is the entry point. r3 points to the DTB + */ .globl start start: LOAD_REG_IMMEDIATE(r1, stackptr) LOAD_REG_IMMEDIATE(r2, tocptr) + /* save DTB pointer */ + std r3, 56(r1) + /* patch sc1 if needed */ bl hcall_have_broken_sc1 cmpwi r3, 0 @@ -33,7 +39,15 @@ start: LOAD_REG_IMMEDIATE(r4, SC1_REPLACEMENT) stw r4, 0(r3) -1: bl main + /* complete setup */ +1: ld r3, 56(r1) + bl setup + + /* run the test */ + LOAD_REG_IMMEDIATE(r5, __argc) + LOAD_REG_IMMEDIATE(r4, __argv) + lwz r3, 0(r5) + bl main bl exit b halt diff --git a/powerpc/selftest.c b/powerpc/selftest.c index 2f2a5215dd55c..84867e482d2a2 100644 --- a/powerpc/selftest.c +++ b/powerpc/selftest.c @@ -1,7 +1,64 @@ +/* + * Test the framework itself. These tests confirm that setup works. + * + * Copyright (C) 2016, Red Hat Inc, Andrew Jones + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ #include +#include +#include -int main(void) +static void check_setup(int argc, char **argv) { - printf("hello world\n"); - return 0; + int nr_tests = 0, len, i; + long val; + + for (i = 0; i < argc; ++i) { + + len = parse_keyval(argv[i], &val); + if (len == -1) + continue; + + argv[i][len] = '\0'; + report_prefix_push(argv[i]); + + if (strcmp(argv[i], "mem") == 0) { + + phys_addr_t memsize = PHYSICAL_END - PHYSICAL_START; + phys_addr_t expected = ((phys_addr_t)val)*1024*1024; + + report("size = %d MB", memsize == expected, + memsize/1024/1024); + ++nr_tests; + + } else if (strcmp(argv[i], "smp") == 0) { + + report("nr_cpus = %d", nr_cpus == (int)val, nr_cpus); + ++nr_tests; + } + + report_prefix_pop(); + } + + if (nr_tests < 2) + report_abort("missing input"); +} + +int main(int argc, char **argv) +{ + report_prefix_push("selftest"); + + if (!argc) + report_abort("no test specified"); + + report_prefix_push(argv[0]); + + if (strcmp(argv[0], "setup") == 0) { + + check_setup(argc-1, &argv[1]); + + } + + return report_summary(); }