From patchwork Wed Jul 24 15:31:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741102 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6CF19C49EA1 for ; Wed, 24 Jul 2024 15:32:04 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764292.1174733 (Exim 4.92) (envelope-from ) id 1sWdy5-00084N-7a; Wed, 24 Jul 2024 15:31:53 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764292.1174733; Wed, 24 Jul 2024 15:31:53 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy4-00082w-QI; Wed, 24 Jul 2024 15:31:52 +0000 Received: by outflank-mailman (input) for mailman id 764292; Wed, 24 Jul 2024 15:31:51 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy2-0005Oj-RP for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:51 +0000 Received: from mail-lj1-x22c.google.com (mail-lj1-x22c.google.com [2a00:1450:4864:20::22c]) by se1-gles-sth1.inumbo.com (Halon) with ESMTPS id d79ff371-49d1-11ef-bbfe-fd08da9f4363; Wed, 24 Jul 2024 17:31:48 +0200 (CEST) Received: by mail-lj1-x22c.google.com with SMTP id 38308e7fff4ca-2ef283c58f4so45563841fa.1 for ; Wed, 24 Jul 2024 08:31:48 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:44 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d79ff371-49d1-11ef-bbfe-fd08da9f4363 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835108; x=1722439908; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zhDenI8Vqhfps04l6z19lXhWwx3sADiuBr8IbwOHpQQ=; b=e5CD/4nsSmjuC+vTZupZL4WUhFjwUPBd+NSClhdBL+hsxT11BkMsEmaxnNG48XM7zw ecFuAe/XXMR4ylBU4dX6nnH3pzdIFP/kHBlu9Cruas0GAv9ewHfx1+bt+JuT8tnLy4S2 QEC3cgPAS4SO9P0qXtcZullwnUO/d82wMlUXQ+u/x7TFbqv1eWRjOuWH4aWjPBqwYfq0 p2Nvotd5TBByZG6CIEwRJ1hiufmEYip1I2IMvmMI+izhTlHZMiZ47X0Sjl1SDksGSilQ 8Kv/xLYVJzGYf9llW97bRDOEYh0CmqHZMX8QHBTEUgAiD3iK4euOgCr+Hm9bTBd/30+S k4yQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835108; x=1722439908; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zhDenI8Vqhfps04l6z19lXhWwx3sADiuBr8IbwOHpQQ=; b=MUrjI+BQj56LGhv26Lr6kheRxC39wAivxaY+CRGv252BKq1ehGIuDVSli/Nel4lRy1 XooBYY72oVJOmB/GABCa8MZRBom/++bCYC7utzZNvelR/aWY01mM1Tb/vC35x4QJSjvW tw87Y9NEDzgL0FIeCs61ugVO8z448C9FPOiCps6SlanRfI8ybhG72YAXRKWr/hIcEak5 mDykyLlgBqlG79MkkFG5PmLTDNT8ajqmEiIbtAVQwLJW89syGTM414E+SWxExsuaatn2 BEOLyLPy9Nqezsyu8kdHAGzxMgQDxfnqS6i07x4qqFQNr73PTDRS0LRleLWjKd0ddDer aDoQ== X-Gm-Message-State: AOJu0YyBUJLrorMCwjv/4Ys3r1gEmcer+xe3msyJPDAD5vrcJm74vM5N 5B9BCNVvdnmDEyviHrr5FE+p3OiR6Gj36IhYQ4zzwQW7bGRJylhC3QjWxMzE X-Google-Smtp-Source: AGHT+IEw2Ih7TjqWFH9quAInXrvmq/LMGy5eEqDbb+l4/OtqUgs8BZpiZZqTHUX1ITGf2HvUHoJgZQ== X-Received: by 2002:a2e:8758:0:b0:2ef:2608:2e47 with SMTP id 38308e7fff4ca-2f039e92a53mr1149591fa.13.1721835104866; Wed, 24 Jul 2024 08:31:44 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Shawn Anastasio , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk , "Daniel P. Smith" , Oleksii Kurochko Subject: [PATCH v7 1/9] xen/device-tree: Move Arm's setup.c bootinfo functions to common Date: Wed, 24 Jul 2024 17:31:32 +0200 Message-ID: <102f8b60c55cdf2db5890b9fb1c2fb66e61c4a67.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 From: Shawn Anastasio Arm's setup.c contains a collection of functions for parsing memory map and other boot information from a device tree. Since these routines are generally useful on any architecture that supports device tree booting, move them into xen/common/device-tree. Also, common/device_tree.c has been moved to the device-tree folder with the corresponding updates to common/Makefile and common/device-tree/Makefile. Suggested-by: Julien Grall Signed-off-by: Shawn Anastasio Signed-off-by: Oleksii Kurochko Acked-by: Julien Grall --- Changes in V7: - move obj-$(CONFIG_HAS_DEVICE_TREE) += device-tree/ to proper place in common/Makefile. - rename macros __XEN_BOOTFDT_H__ to XEN_BOOTFDT_H to not violate MISRA rule 21.1. - drop definition of "#define MAX_FDT_SIZE SZ_2M" in xen/bootfdt.h as it is expected to be arch-specific. Back "#define MAX_FDT_SIZE SZ_2M" to arm/setup.h where it was before these changes. - git mv xen/common/device_tree.c xen/common/device-tree/ and update correspondingly Makefiles of common/ and common/device-tree - update the commit message --- Changes in V6: - update the version of the patch to v6. --- Changes in V5: - add xen/include/xen/bootfdt.h to MAINTAINERS file. - drop message "Early device tree parsing and". - After rebase on top of the current staging the following changes were done: - init bootinfo variable in with BOOTINFO_INIT; - update the code of dt_unreserved_regions(): CONFIG_STATIC_SHM related changes and getting of reserved_mem bootinfo_get_shmem() ?? - update the code of meminfo_overlap_check(): add check ( INVALID_PADDR == bank_start ) to if case. - update the code of check_reserved_regions_overlap(): CONFIG_STATIC_SHM related changes. - struct bootinfo was updated ( CONFIG_STATIC_SHM changes ) - add shared_meminfo ( because of CONFIG_STATIC_SHM ) - struct struct membanks was update with __struct group so is neeeded to be included in bootfdt.h - move BOOTINFO_ACPI_INIT, BOOTINFO_SHMEM_INIT, BOOTINFO_INIT to generic bootfdt.h - bootinfo_get_reserved_mem(), bootinfo_get_mem(), bootinfo_get_acpi(), bootinfo_get_shmem() and bootinfo_get_shmem_extra() were moved to xen/bootfdt.h - s/arm32/CONFIG_SEPARATE_XENHEAP/ - add inclusion of because there are function in which are using container_of(). --- Changes in v4: - create new xen/include/bootinfo.h rather than relying on arch's asm/setup.h to provide required definitions for bootinfo.c - build bootinfo.c as .init.o - clean up and sort bootinfo.c's #includes - use CONFIG_SEPARATE_XENHEAP rather than CONFIG_ARM_32 to guard xenheap-specific behavior of populate_boot_allocator - (MAINTAINERS) include all of common/device-tree rather than just bootinfo.c --- MAINTAINERS | 2 + xen/arch/arm/include/asm/setup.h | 185 +-- xen/arch/arm/setup.c | 432 ----- xen/common/Makefile | 2 +- xen/common/device-tree/Makefile | 2 + xen/common/device-tree/bootinfo.c | 459 ++++++ xen/common/device-tree/device_tree.c | 2253 ++++++++++++++++++++++++++ xen/common/device_tree.c | 2253 -------------------------- xen/include/xen/bootfdt.h | 195 +++ 9 files changed, 2913 insertions(+), 2870 deletions(-) create mode 100644 xen/common/device-tree/Makefile create mode 100644 xen/common/device-tree/bootinfo.c create mode 100644 xen/common/device-tree/device_tree.c delete mode 100644 xen/common/device_tree.c create mode 100644 xen/include/xen/bootfdt.h diff --git a/MAINTAINERS b/MAINTAINERS index 2b0c894527..505915b6b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -295,9 +295,11 @@ M: Stefano Stabellini M: Julien Grall S: Supported F: xen/common/libfdt/ +F: xen/common/device-tree/ F: xen/common/device_tree.c F: xen/common/dt-overlay.c F: xen/include/xen/libfdt/ +F: xen/include/xen/bootfdt.h F: xen/include/xen/device_tree.h F: xen/drivers/passthrough/device_tree.c diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index c34179da93..1748be29e5 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -3,159 +3,11 @@ #include #include +#include #include -#define MIN_FDT_ALIGN 8 #define MAX_FDT_SIZE SZ_2M -#define NR_MEM_BANKS 256 -#define NR_SHMEM_BANKS 32 - -#define MAX_MODULES 32 /* Current maximum useful modules */ - -typedef enum { - BOOTMOD_XEN, - BOOTMOD_FDT, - BOOTMOD_KERNEL, - BOOTMOD_RAMDISK, - BOOTMOD_XSM, - BOOTMOD_GUEST_DTB, - BOOTMOD_UNKNOWN -} bootmodule_kind; - -enum membank_type { - /* - * The MEMBANK_DEFAULT type refers to either reserved memory for the - * device/firmware (when the bank is in 'reserved_mem') or any RAM (when - * the bank is in 'mem'). - */ - MEMBANK_DEFAULT, - /* - * The MEMBANK_STATIC_DOMAIN type is used to indicate whether the memory - * bank is bound to a static Xen domain. It is only valid when the bank - * is in reserved_mem. - */ - MEMBANK_STATIC_DOMAIN, - /* - * The MEMBANK_STATIC_HEAP type is used to indicate whether the memory - * bank is reserved as static heap. It is only valid when the bank is - * in reserved_mem. - */ - MEMBANK_STATIC_HEAP, - /* - * The MEMBANK_FDT_RESVMEM type is used to indicate whether the memory - * bank is from the FDT reserve map. - */ - MEMBANK_FDT_RESVMEM, -}; - -/* Indicates the maximum number of characters(\0 included) for shm_id */ -#define MAX_SHM_ID_LENGTH 16 - -struct shmem_membank_extra { - char shm_id[MAX_SHM_ID_LENGTH]; - unsigned int nr_shm_borrowers; -}; - -struct membank { - paddr_t start; - paddr_t size; - union { - enum membank_type type; -#ifdef CONFIG_STATIC_SHM - struct shmem_membank_extra *shmem_extra; -#endif - }; -}; - -struct membanks { - __struct_group(membanks_hdr, common, , - unsigned int nr_banks; - unsigned int max_banks; - ); - struct membank bank[]; -}; - -struct meminfo { - struct membanks_hdr common; - struct membank bank[NR_MEM_BANKS]; -}; - -struct shared_meminfo { - struct membanks_hdr common; - struct membank bank[NR_SHMEM_BANKS]; - struct shmem_membank_extra extra[NR_SHMEM_BANKS]; -}; - -/* - * The domU flag is set for kernels and ramdisks of "xen,domain" nodes. - * The purpose of the domU flag is to avoid getting confused in - * kernel_probe, where we try to guess which is the dom0 kernel and - * initrd to be compatible with all versions of the multiboot spec. - */ -#define BOOTMOD_MAX_CMDLINE 1024 -struct bootmodule { - bootmodule_kind kind; - bool domU; - paddr_t start; - paddr_t size; -}; - -/* DT_MAX_NAME is the node name max length according the DT spec */ -#define DT_MAX_NAME 41 -struct bootcmdline { - bootmodule_kind kind; - bool domU; - paddr_t start; - char dt_name[DT_MAX_NAME]; - char cmdline[BOOTMOD_MAX_CMDLINE]; -}; - -struct bootmodules { - int nr_mods; - struct bootmodule module[MAX_MODULES]; -}; - -struct bootcmdlines { - unsigned int nr_mods; - struct bootcmdline cmdline[MAX_MODULES]; -}; - -struct bootinfo { - struct meminfo mem; - /* The reserved regions are only used when booting using Device-Tree */ - struct meminfo reserved_mem; - struct bootmodules modules; - struct bootcmdlines cmdlines; -#ifdef CONFIG_ACPI - struct meminfo acpi; -#endif -#ifdef CONFIG_STATIC_SHM - struct shared_meminfo shmem; -#endif - bool static_heap; -}; - -#ifdef CONFIG_ACPI -#define BOOTINFO_ACPI_INIT .acpi.common.max_banks = NR_MEM_BANKS, -#else -#define BOOTINFO_ACPI_INIT -#endif - -#ifdef CONFIG_STATIC_SHM -#define BOOTINFO_SHMEM_INIT .shmem.common.max_banks = NR_SHMEM_BANKS, -#else -#define BOOTINFO_SHMEM_INIT -#endif - -#define BOOTINFO_INIT \ -{ \ - .mem.common.max_banks = NR_MEM_BANKS, \ - .reserved_mem.common.max_banks = NR_MEM_BANKS, \ - BOOTINFO_ACPI_INIT \ - BOOTINFO_SHMEM_INIT \ -} - struct map_range_data { struct domain *d; @@ -167,39 +19,8 @@ struct map_range_data struct rangeset *irq_ranges; }; -extern struct bootinfo bootinfo; - extern domid_t max_init_domid; -static inline struct membanks *bootinfo_get_mem(void) -{ - return container_of(&bootinfo.mem.common, struct membanks, common); -} - -static inline struct membanks *bootinfo_get_reserved_mem(void) -{ - return container_of(&bootinfo.reserved_mem.common, struct membanks, common); -} - -#ifdef CONFIG_ACPI -static inline struct membanks *bootinfo_get_acpi(void) -{ - return container_of(&bootinfo.acpi.common, struct membanks, common); -} -#endif - -#ifdef CONFIG_STATIC_SHM -static inline struct membanks *bootinfo_get_shmem(void) -{ - return container_of(&bootinfo.shmem.common, struct membanks, common); -} - -static inline struct shmem_membank_extra *bootinfo_get_shmem_extra(void) -{ - return bootinfo.shmem.extra; -} -#endif - void copy_from_paddr(void *dst, paddr_t paddr, unsigned long len); size_t estimate_efi_size(unsigned int mem_nr_banks); @@ -220,9 +41,6 @@ void fw_unreserved_regions(paddr_t s, paddr_t e, void (*cb)(paddr_t ps, paddr_t pe), unsigned int first); -size_t boot_fdt_info(const void *fdt, paddr_t paddr); -const char *boot_fdt_cmdline(const void *fdt); - bool check_reserved_regions_overlap(paddr_t region_start, paddr_t region_size); struct bootmodule *add_boot_module(bootmodule_kind kind, @@ -237,7 +55,6 @@ struct bootcmdline * boot_cmdline_find_by_name(const char *name); const char *boot_module_kind_as_string(bootmodule_kind kind); void init_pdx(void); -void populate_boot_allocator(void); void setup_mm(void); extern uint32_t hyp_traps_vector[]; diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index 0c2fdaceaf..cb2c0a16b8 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -48,8 +48,6 @@ #include #include -struct bootinfo __initdata bootinfo = BOOTINFO_INIT; - /* * Sanitized version of cpuinfo containing only features available on all * cores (only on arm64 as there is no sanitization support on arm32). @@ -203,321 +201,6 @@ static void __init processor_id(void) processor_setup(); } -static void __init dt_unreserved_regions(paddr_t s, paddr_t e, - void (*cb)(paddr_t ps, paddr_t pe), - unsigned int first) -{ - const struct membanks *reserved_mem = bootinfo_get_reserved_mem(); -#ifdef CONFIG_STATIC_SHM - const struct membanks *shmem = bootinfo_get_shmem(); - unsigned int offset; -#endif - unsigned int i; - - /* - * i is the current bootmodule we are evaluating across all possible - * kinds. - */ - for ( i = first; i < reserved_mem->nr_banks; i++ ) - { - paddr_t r_s = reserved_mem->bank[i].start; - paddr_t r_e = r_s + reserved_mem->bank[i].size; - - if ( s < r_e && r_s < e ) - { - dt_unreserved_regions(r_e, e, cb, i + 1); - dt_unreserved_regions(s, r_s, cb, i + 1); - return; - } - } - -#ifdef CONFIG_STATIC_SHM - /* - * When retrieving the corresponding shared memory addresses - * below, we need to index the shmem->bank starting from 0, hence - * we need to use i - reserved_mem->nr_banks. - */ - offset = reserved_mem->nr_banks; - for ( ; i - offset < shmem->nr_banks; i++ ) - { - paddr_t r_s, r_e; - - r_s = shmem->bank[i - offset].start; - - /* Shared memory banks can contain INVALID_PADDR as start */ - if ( INVALID_PADDR == r_s ) - continue; - - r_e = r_s + shmem->bank[i - offset].size; - - if ( s < r_e && r_s < e ) - { - dt_unreserved_regions(r_e, e, cb, i + 1); - dt_unreserved_regions(s, r_s, cb, i + 1); - return; - } - } -#endif - - cb(s, e); -} - -/* - * TODO: '*_end' could be 0 if the bank/region is at the end of the physical - * address space. This is for now not handled as it requires more rework. - */ -static bool __init meminfo_overlap_check(const struct membanks *mem, - paddr_t region_start, - paddr_t region_size) -{ - paddr_t bank_start = INVALID_PADDR, bank_end = 0; - paddr_t region_end = region_start + region_size; - unsigned int i, bank_num = mem->nr_banks; - - for ( i = 0; i < bank_num; i++ ) - { - bank_start = mem->bank[i].start; - bank_end = bank_start + mem->bank[i].size; - - if ( INVALID_PADDR == bank_start || region_end <= bank_start || - region_start >= bank_end ) - continue; - else - { - printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with bank[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", - region_start, region_end, i, bank_start, bank_end); - return true; - } - } - - return false; -} - -/* - * TODO: '*_end' could be 0 if the module/region is at the end of the physical - * address space. This is for now not handled as it requires more rework. - */ -static bool __init bootmodules_overlap_check(struct bootmodules *bootmodules, - paddr_t region_start, - paddr_t region_size) -{ - paddr_t mod_start = INVALID_PADDR, mod_end = 0; - paddr_t region_end = region_start + region_size; - unsigned int i, mod_num = bootmodules->nr_mods; - - for ( i = 0; i < mod_num; i++ ) - { - mod_start = bootmodules->module[i].start; - mod_end = mod_start + bootmodules->module[i].size; - - if ( region_end <= mod_start || region_start >= mod_end ) - continue; - else - { - printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with mod[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", - region_start, region_end, i, mod_start, mod_end); - return true; - } - } - - return false; -} - -void __init fw_unreserved_regions(paddr_t s, paddr_t e, - void (*cb)(paddr_t ps, paddr_t pe), - unsigned int first) -{ - if ( acpi_disabled ) - dt_unreserved_regions(s, e, cb, first); - else - cb(s, e); -} - -/* - * Given an input physical address range, check if this range is overlapping - * with the existing reserved memory regions defined in bootinfo. - * Return true if the input physical address range is overlapping with any - * existing reserved memory regions, otherwise false. - */ -bool __init check_reserved_regions_overlap(paddr_t region_start, - paddr_t region_size) -{ - const struct membanks *mem_banks[] = { - bootinfo_get_reserved_mem(), -#ifdef CONFIG_ACPI - bootinfo_get_acpi(), -#endif -#ifdef CONFIG_STATIC_SHM - bootinfo_get_shmem(), -#endif - }; - unsigned int i; - - /* - * Check if input region is overlapping with reserved memory banks or - * ACPI EfiACPIReclaimMemory (when ACPI feature is enabled) or static - * shared memory banks (when static shared memory feature is enabled) - */ - for ( i = 0; i < ARRAY_SIZE(mem_banks); i++ ) - if ( meminfo_overlap_check(mem_banks[i], region_start, region_size) ) - return true; - - /* Check if input region is overlapping with bootmodules */ - if ( bootmodules_overlap_check(&bootinfo.modules, - region_start, region_size) ) - return true; - - return false; -} - -struct bootmodule __init *add_boot_module(bootmodule_kind kind, - paddr_t start, paddr_t size, - bool domU) -{ - struct bootmodules *mods = &bootinfo.modules; - struct bootmodule *mod; - unsigned int i; - - if ( mods->nr_mods == MAX_MODULES ) - { - printk("Ignoring %s boot module at %"PRIpaddr"-%"PRIpaddr" (too many)\n", - boot_module_kind_as_string(kind), start, start + size); - return NULL; - } - - if ( check_reserved_regions_overlap(start, size) ) - return NULL; - - for ( i = 0 ; i < mods->nr_mods ; i++ ) - { - mod = &mods->module[i]; - if ( mod->kind == kind && mod->start == start ) - { - if ( !domU ) - mod->domU = false; - return mod; - } - } - - mod = &mods->module[mods->nr_mods++]; - mod->kind = kind; - mod->start = start; - mod->size = size; - mod->domU = domU; - - return mod; -} - -/* - * boot_module_find_by_kind can only be used to return Xen modules (e.g - * XSM, DTB) or Dom0 modules. This is not suitable for looking up guest - * modules. - */ -struct bootmodule * __init boot_module_find_by_kind(bootmodule_kind kind) -{ - struct bootmodules *mods = &bootinfo.modules; - struct bootmodule *mod; - int i; - for (i = 0 ; i < mods->nr_mods ; i++ ) - { - mod = &mods->module[i]; - if ( mod->kind == kind && !mod->domU ) - return mod; - } - return NULL; -} - -void __init add_boot_cmdline(const char *name, const char *cmdline, - bootmodule_kind kind, paddr_t start, bool domU) -{ - struct bootcmdlines *cmds = &bootinfo.cmdlines; - struct bootcmdline *cmd; - - if ( cmds->nr_mods == MAX_MODULES ) - { - printk("Ignoring %s cmdline (too many)\n", name); - return; - } - - cmd = &cmds->cmdline[cmds->nr_mods++]; - cmd->kind = kind; - cmd->domU = domU; - cmd->start = start; - - ASSERT(strlen(name) <= DT_MAX_NAME); - safe_strcpy(cmd->dt_name, name); - - if ( strlen(cmdline) > BOOTMOD_MAX_CMDLINE ) - panic("module %s command line too long\n", name); - safe_strcpy(cmd->cmdline, cmdline); -} - -/* - * boot_cmdline_find_by_kind can only be used to return Xen modules (e.g - * XSM, DTB) or Dom0 modules. This is not suitable for looking up guest - * modules. - */ -struct bootcmdline * __init boot_cmdline_find_by_kind(bootmodule_kind kind) -{ - struct bootcmdlines *cmds = &bootinfo.cmdlines; - struct bootcmdline *cmd; - int i; - - for ( i = 0 ; i < cmds->nr_mods ; i++ ) - { - cmd = &cmds->cmdline[i]; - if ( cmd->kind == kind && !cmd->domU ) - return cmd; - } - return NULL; -} - -struct bootcmdline * __init boot_cmdline_find_by_name(const char *name) -{ - struct bootcmdlines *mods = &bootinfo.cmdlines; - struct bootcmdline *mod; - unsigned int i; - - for (i = 0 ; i < mods->nr_mods ; i++ ) - { - mod = &mods->cmdline[i]; - if ( strcmp(mod->dt_name, name) == 0 ) - return mod; - } - return NULL; -} - -struct bootmodule * __init boot_module_find_by_addr_and_kind(bootmodule_kind kind, - paddr_t start) -{ - struct bootmodules *mods = &bootinfo.modules; - struct bootmodule *mod; - unsigned int i; - - for (i = 0 ; i < mods->nr_mods ; i++ ) - { - mod = &mods->module[i]; - if ( mod->kind == kind && mod->start == start ) - return mod; - } - return NULL; -} - -const char * __init boot_module_kind_as_string(bootmodule_kind kind) -{ - switch ( kind ) - { - case BOOTMOD_XEN: return "Xen"; - case BOOTMOD_FDT: return "Device Tree"; - case BOOTMOD_KERNEL: return "Kernel"; - case BOOTMOD_RAMDISK: return "Ramdisk"; - case BOOTMOD_XSM: return "XSM"; - case BOOTMOD_GUEST_DTB: return "DTB"; - case BOOTMOD_UNKNOWN: return "Unknown"; - default: BUG(); - } -} - void __init discard_initial_modules(void) { struct bootmodules *mi = &bootinfo.modules; @@ -556,40 +239,6 @@ static void * __init relocate_fdt(paddr_t dtb_paddr, size_t dtb_size) return fdt; } -/* - * Return the end of the non-module region starting at s. In other - * words return s the start of the next modules after s. - * - * On input *end is the end of the region which should be considered - * and it is updated to reflect the end of the module, clipped to the - * end of the region if it would run over. - */ -static paddr_t __init next_module(paddr_t s, paddr_t *end) -{ - struct bootmodules *mi = &bootinfo.modules; - paddr_t lowest = ~(paddr_t)0; - int i; - - for ( i = 0; i < mi->nr_mods; i++ ) - { - paddr_t mod_s = mi->module[i].start; - paddr_t mod_e = mod_s + mi->module[i].size; - - if ( !mi->module[i].size ) - continue; - - if ( mod_s < s ) - continue; - if ( mod_s > lowest ) - continue; - if ( mod_s > *end ) - continue; - lowest = mod_s; - *end = min(*end, mod_e); - } - return lowest; -} - void __init init_pdx(void) { const struct membanks *mem = bootinfo_get_mem(); @@ -635,87 +284,6 @@ void __init init_pdx(void) } } -/* - * Populate the boot allocator. - * If a static heap was not provided by the admin, all the RAM but the - * following regions will be added: - * - Modules (e.g., Xen, Kernel) - * - Reserved regions - * - Xenheap (arm32 only) - * If a static heap was provided by the admin, populate the boot - * allocator with the corresponding regions only, but with Xenheap excluded - * on arm32. - */ -void __init populate_boot_allocator(void) -{ - unsigned int i; - const struct membanks *banks = bootinfo_get_mem(); - const struct membanks *reserved_mem = bootinfo_get_reserved_mem(); - paddr_t s, e; - - if ( bootinfo.static_heap ) - { - for ( i = 0 ; i < reserved_mem->nr_banks; i++ ) - { - if ( reserved_mem->bank[i].type != MEMBANK_STATIC_HEAP ) - continue; - - s = reserved_mem->bank[i].start; - e = s + reserved_mem->bank[i].size; -#ifdef CONFIG_ARM_32 - /* Avoid the xenheap, note that the xenheap cannot across a bank */ - if ( s <= mfn_to_maddr(directmap_mfn_start) && - e >= mfn_to_maddr(directmap_mfn_end) ) - { - init_boot_pages(s, mfn_to_maddr(directmap_mfn_start)); - init_boot_pages(mfn_to_maddr(directmap_mfn_end), e); - } - else -#endif - init_boot_pages(s, e); - } - - return; - } - - for ( i = 0; i < banks->nr_banks; i++ ) - { - const struct membank *bank = &banks->bank[i]; - paddr_t bank_end = bank->start + bank->size; - - s = bank->start; - while ( s < bank_end ) - { - paddr_t n = bank_end; - - e = next_module(s, &n); - - if ( e == ~(paddr_t)0 ) - e = n = bank_end; - - /* - * Module in a RAM bank other than the one which we are - * not dealing with here. - */ - if ( e > bank_end ) - e = bank_end; - -#ifdef CONFIG_ARM_32 - /* Avoid the xenheap */ - if ( s < mfn_to_maddr(directmap_mfn_end) && - mfn_to_maddr(directmap_mfn_start) < e ) - { - e = mfn_to_maddr(directmap_mfn_start); - n = mfn_to_maddr(directmap_mfn_end); - } -#endif - - fw_unreserved_regions(s, e, init_boot_pages, 0); - s = n; - } - } -} - size_t __read_mostly dcache_line_bytes; /* C entry point for boot CPU */ diff --git a/xen/common/Makefile b/xen/common/Makefile index f12a474d40..7e66802a9e 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_HYPFS_CONFIG) += config_data.o obj-$(CONFIG_CORE_PARKING) += core_parking.o obj-y += cpu.o obj-$(CONFIG_DEBUG_TRACE) += debugtrace.o -obj-$(CONFIG_HAS_DEVICE_TREE) += device_tree.o +obj-$(CONFIG_HAS_DEVICE_TREE) += device-tree/ obj-$(CONFIG_IOREQ_SERVER) += dm.o obj-y += domain.o obj-$(CONFIG_OVERLAY_DTB) += dt-overlay.o diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile new file mode 100644 index 0000000000..da892dd55d --- /dev/null +++ b/xen/common/device-tree/Makefile @@ -0,0 +1,2 @@ +obj-y += bootinfo.init.o +obj-y += device_tree.o diff --git a/xen/common/device-tree/bootinfo.c b/xen/common/device-tree/bootinfo.c new file mode 100644 index 0000000000..dcac9a40a0 --- /dev/null +++ b/xen/common/device-tree/bootinfo.c @@ -0,0 +1,459 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Derived from $xen/arch/arm/setup.c. + * + * bookkeeping routines. + * + * Tim Deegan + * Copyright (c) 2011 Citrix Systems. + * Copyright (c) 2024 Raptor Engineering LLC + */ + +#include +#include +#include +#include +#include +#include +#include + +struct bootinfo __initdata bootinfo = BOOTINFO_INIT; + +const char * __init boot_module_kind_as_string(bootmodule_kind kind) +{ + switch ( kind ) + { + case BOOTMOD_XEN: return "Xen"; + case BOOTMOD_FDT: return "Device Tree"; + case BOOTMOD_KERNEL: return "Kernel"; + case BOOTMOD_RAMDISK: return "Ramdisk"; + case BOOTMOD_XSM: return "XSM"; + case BOOTMOD_GUEST_DTB: return "DTB"; + case BOOTMOD_UNKNOWN: return "Unknown"; + default: BUG(); + } +} + +static void __init dt_unreserved_regions(paddr_t s, paddr_t e, + void (*cb)(paddr_t ps, paddr_t pe), + unsigned int first) +{ + const struct membanks *reserved_mem = bootinfo_get_reserved_mem(); +#ifdef CONFIG_STATIC_SHM + const struct membanks *shmem = bootinfo_get_shmem(); + unsigned int offset; +#endif + unsigned int i; + + /* + * i is the current bootmodule we are evaluating across all possible + * kinds. + */ + for ( i = first; i < reserved_mem->nr_banks; i++ ) + { + paddr_t r_s = reserved_mem->bank[i].start; + paddr_t r_e = r_s + reserved_mem->bank[i].size; + + if ( s < r_e && r_s < e ) + { + dt_unreserved_regions(r_e, e, cb, i + 1); + dt_unreserved_regions(s, r_s, cb, i + 1); + return; + } + } + +#ifdef CONFIG_STATIC_SHM + /* + * When retrieving the corresponding shared memory addresses + * below, we need to index the shmem->bank starting from 0, hence + * we need to use i - reserved_mem->nr_banks. + */ + offset = reserved_mem->nr_banks; + for ( ; i - offset < shmem->nr_banks; i++ ) + { + paddr_t r_s, r_e; + + r_s = shmem->bank[i - offset].start; + + /* Shared memory banks can contain INVALID_PADDR as start */ + if ( INVALID_PADDR == r_s ) + continue; + + r_e = r_s + shmem->bank[i - offset].size; + + if ( s < r_e && r_s < e ) + { + dt_unreserved_regions(r_e, e, cb, i + 1); + dt_unreserved_regions(s, r_s, cb, i + 1); + return; + } + } +#endif + + cb(s, e); +} + +/* + * TODO: '*_end' could be 0 if the bank/region is at the end of the physical + * address space. This is for now not handled as it requires more rework. + */ +static bool __init meminfo_overlap_check(const struct membanks *mem, + paddr_t region_start, + paddr_t region_size) +{ + paddr_t bank_start = INVALID_PADDR, bank_end = 0; + paddr_t region_end = region_start + region_size; + unsigned int i, bank_num = mem->nr_banks; + + for ( i = 0; i < bank_num; i++ ) + { + bank_start = mem->bank[i].start; + bank_end = bank_start + mem->bank[i].size; + + if ( INVALID_PADDR == bank_start || region_end <= bank_start || + region_start >= bank_end ) + continue; + else + { + printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with bank[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", + region_start, region_end, i, bank_start, bank_end); + return true; + } + } + + return false; +} + +/* + * TODO: '*_end' could be 0 if the module/region is at the end of the physical + * address space. This is for now not handled as it requires more rework. + */ +static bool __init bootmodules_overlap_check(struct bootmodules *bootmodules, + paddr_t region_start, + paddr_t region_size) +{ + paddr_t mod_start = INVALID_PADDR, mod_end = 0; + paddr_t region_end = region_start + region_size; + unsigned int i, mod_num = bootmodules->nr_mods; + + for ( i = 0; i < mod_num; i++ ) + { + mod_start = bootmodules->module[i].start; + mod_end = mod_start + bootmodules->module[i].size; + + if ( region_end <= mod_start || region_start >= mod_end ) + continue; + else + { + printk("Region: [%#"PRIpaddr", %#"PRIpaddr") overlapping with mod[%u]: [%#"PRIpaddr", %#"PRIpaddr")\n", + region_start, region_end, i, mod_start, mod_end); + return true; + } + } + + return false; +} + +void __init fw_unreserved_regions(paddr_t s, paddr_t e, + void (*cb)(paddr_t ps, paddr_t pe), + unsigned int first) +{ + if ( acpi_disabled ) + dt_unreserved_regions(s, e, cb, first); + else + cb(s, e); +} + +/* + * Given an input physical address range, check if this range is overlapping + * with the existing reserved memory regions defined in bootinfo. + * Return true if the input physical address range is overlapping with any + * existing reserved memory regions, otherwise false. + */ +bool __init check_reserved_regions_overlap(paddr_t region_start, + paddr_t region_size) +{ + const struct membanks *mem_banks[] = { + bootinfo_get_reserved_mem(), +#ifdef CONFIG_ACPI + bootinfo_get_acpi(), +#endif +#ifdef CONFIG_STATIC_SHM + bootinfo_get_shmem(), +#endif + }; + unsigned int i; + + /* + * Check if input region is overlapping with reserved memory banks or + * ACPI EfiACPIReclaimMemory (when ACPI feature is enabled) or static + * shared memory banks (when static shared memory feature is enabled) + */ + for ( i = 0; i < ARRAY_SIZE(mem_banks); i++ ) + if ( meminfo_overlap_check(mem_banks[i], region_start, region_size) ) + return true; + + /* Check if input region is overlapping with bootmodules */ + if ( bootmodules_overlap_check(&bootinfo.modules, + region_start, region_size) ) + return true; + + return false; +} + +struct bootmodule __init *add_boot_module(bootmodule_kind kind, + paddr_t start, paddr_t size, + bool domU) +{ + struct bootmodules *mods = &bootinfo.modules; + struct bootmodule *mod; + unsigned int i; + + if ( mods->nr_mods == MAX_MODULES ) + { + printk("Ignoring %s boot module at %"PRIpaddr"-%"PRIpaddr" (too many)\n", + boot_module_kind_as_string(kind), start, start + size); + return NULL; + } + + if ( check_reserved_regions_overlap(start, size) ) + return NULL; + + for ( i = 0 ; i < mods->nr_mods ; i++ ) + { + mod = &mods->module[i]; + if ( mod->kind == kind && mod->start == start ) + { + if ( !domU ) + mod->domU = false; + return mod; + } + } + + mod = &mods->module[mods->nr_mods++]; + mod->kind = kind; + mod->start = start; + mod->size = size; + mod->domU = domU; + + return mod; +} + +/* + * boot_module_find_by_kind can only be used to return Xen modules (e.g + * XSM, DTB) or Dom0 modules. This is not suitable for looking up guest + * modules. + */ +struct bootmodule * __init boot_module_find_by_kind(bootmodule_kind kind) +{ + struct bootmodules *mods = &bootinfo.modules; + struct bootmodule *mod; + int i; + for (i = 0 ; i < mods->nr_mods ; i++ ) + { + mod = &mods->module[i]; + if ( mod->kind == kind && !mod->domU ) + return mod; + } + return NULL; +} + +void __init add_boot_cmdline(const char *name, const char *cmdline, + bootmodule_kind kind, paddr_t start, bool domU) +{ + struct bootcmdlines *cmds = &bootinfo.cmdlines; + struct bootcmdline *cmd; + + if ( cmds->nr_mods == MAX_MODULES ) + { + printk("Ignoring %s cmdline (too many)\n", name); + return; + } + + cmd = &cmds->cmdline[cmds->nr_mods++]; + cmd->kind = kind; + cmd->domU = domU; + cmd->start = start; + + ASSERT(strlen(name) <= DT_MAX_NAME); + safe_strcpy(cmd->dt_name, name); + + if ( strlen(cmdline) > BOOTMOD_MAX_CMDLINE ) + panic("module %s command line too long\n", name); + safe_strcpy(cmd->cmdline, cmdline); +} + +/* + * boot_cmdline_find_by_kind can only be used to return Xen modules (e.g + * XSM, DTB) or Dom0 modules. This is not suitable for looking up guest + * modules. + */ +struct bootcmdline * __init boot_cmdline_find_by_kind(bootmodule_kind kind) +{ + struct bootcmdlines *cmds = &bootinfo.cmdlines; + struct bootcmdline *cmd; + int i; + + for ( i = 0 ; i < cmds->nr_mods ; i++ ) + { + cmd = &cmds->cmdline[i]; + if ( cmd->kind == kind && !cmd->domU ) + return cmd; + } + return NULL; +} + +struct bootcmdline * __init boot_cmdline_find_by_name(const char *name) +{ + struct bootcmdlines *mods = &bootinfo.cmdlines; + struct bootcmdline *mod; + unsigned int i; + + for (i = 0 ; i < mods->nr_mods ; i++ ) + { + mod = &mods->cmdline[i]; + if ( strcmp(mod->dt_name, name) == 0 ) + return mod; + } + return NULL; +} + +struct bootmodule * __init boot_module_find_by_addr_and_kind(bootmodule_kind kind, + paddr_t start) +{ + struct bootmodules *mods = &bootinfo.modules; + struct bootmodule *mod; + unsigned int i; + + for (i = 0 ; i < mods->nr_mods ; i++ ) + { + mod = &mods->module[i]; + if ( mod->kind == kind && mod->start == start ) + return mod; + } + return NULL; +} + +/* + * Return the end of the non-module region starting at s. In other + * words return s the start of the next modules after s. + * + * On input *end is the end of the region which should be considered + * and it is updated to reflect the end of the module, clipped to the + * end of the region if it would run over. + */ +static paddr_t __init next_module(paddr_t s, paddr_t *end) +{ + struct bootmodules *mi = &bootinfo.modules; + paddr_t lowest = ~(paddr_t)0; + int i; + + for ( i = 0; i < mi->nr_mods; i++ ) + { + paddr_t mod_s = mi->module[i].start; + paddr_t mod_e = mod_s + mi->module[i].size; + + if ( !mi->module[i].size ) + continue; + + if ( mod_s < s ) + continue; + if ( mod_s > lowest ) + continue; + if ( mod_s > *end ) + continue; + lowest = mod_s; + *end = min(*end, mod_e); + } + return lowest; +} + +/* + * Populate the boot allocator. + * If a static heap was not provided by the admin, all the RAM but the + * following regions will be added: + * - Modules (e.g., Xen, Kernel) + * - Reserved regions + * - Xenheap (CONFIG_SEPARATE_XENHEAP only) + * If a static heap was provided by the admin, populate the boot + * allocator with the corresponding regions only, but with Xenheap excluded + * on CONFIG_SEPARATE_XENHEAP. + */ +void __init populate_boot_allocator(void) +{ + unsigned int i; + const struct membanks *banks = bootinfo_get_mem(); + const struct membanks *reserved_mem = bootinfo_get_reserved_mem(); + paddr_t s, e; + + if ( bootinfo.static_heap ) + { + for ( i = 0 ; i < reserved_mem->nr_banks; i++ ) + { + if ( reserved_mem->bank[i].type != MEMBANK_STATIC_HEAP ) + continue; + + s = reserved_mem->bank[i].start; + e = s + reserved_mem->bank[i].size; +#ifdef CONFIG_SEPARATE_XENHEAP + /* Avoid the xenheap, note that the xenheap cannot across a bank */ + if ( s <= mfn_to_maddr(directmap_mfn_start) && + e >= mfn_to_maddr(directmap_mfn_end) ) + { + init_boot_pages(s, mfn_to_maddr(directmap_mfn_start)); + init_boot_pages(mfn_to_maddr(directmap_mfn_end), e); + } + else +#endif + init_boot_pages(s, e); + } + + return; + } + + for ( i = 0; i < banks->nr_banks; i++ ) + { + const struct membank *bank = &banks->bank[i]; + paddr_t bank_end = bank->start + bank->size; + + s = bank->start; + while ( s < bank_end ) + { + paddr_t n = bank_end; + + e = next_module(s, &n); + + if ( e == ~(paddr_t)0 ) + e = n = bank_end; + + /* + * Module in a RAM bank other than the one which we are + * not dealing with here. + */ + if ( e > bank_end ) + e = bank_end; + +#ifdef CONFIG_SEPARATE_XENHEAP + /* Avoid the xenheap */ + if ( s < mfn_to_maddr(directmap_mfn_end) && + mfn_to_maddr(directmap_mfn_start) < e ) + { + e = mfn_to_maddr(directmap_mfn_start); + n = mfn_to_maddr(directmap_mfn_end); + } +#endif + + fw_unreserved_regions(s, e, init_boot_pages, 0); + s = n; + } + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/device-tree/device_tree.c b/xen/common/device-tree/device_tree.c new file mode 100644 index 0000000000..8d1017a49d --- /dev/null +++ b/xen/common/device-tree/device_tree.c @@ -0,0 +1,2253 @@ +/* + * Device Tree + * + * Copyright (C) 2012 Citrix Systems, Inc. + * Copyright 2009 Benjamin Herrenschmidt, IBM Corp + * benh@kernel.crashing.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const void *device_tree_flattened; +dt_irq_xlate_func dt_irq_xlate; +/* Host device tree */ +struct dt_device_node *dt_host; +/* Interrupt controller node*/ +const struct dt_device_node *dt_interrupt_controller; +DEFINE_RWLOCK(dt_host_lock); + +/** + * struct dt_alias_prop - Alias property in 'aliases' node + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + * + * The structure represents one alias property of 'aliases' node as + * an entry in aliases_lookup list. + */ +struct dt_alias_prop { + struct list_head link; + const char *alias; + struct dt_device_node *np; + int id; + char stem[0]; +}; + +static LIST_HEAD(aliases_lookup); + +#ifdef CONFIG_DEVICE_TREE_DEBUG +static void dt_dump_addr(const char *s, const __be32 *addr, int na) +{ + dt_dprintk("%s", s); + while ( na-- ) + dt_dprintk(" %08x", be32_to_cpu(*(addr++))); + dt_dprintk("\n"); +} +#else +static void dt_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +#define DT_BAD_ADDR ((u64)-1) + +/* Max address size we deal with */ +#define DT_MAX_ADDR_CELLS 4 +#define DT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= DT_MAX_ADDR_CELLS) +#define DT_CHECK_COUNTS(na, ns) (DT_CHECK_ADDR_COUNT(na) && (ns) > 0) + +/* Callbacks for bus specific translators */ +struct dt_bus +{ + const char *name; + const char *addresses; + bool (*match)(const struct dt_device_node *node); + void (*count_cells)(const struct dt_device_node *child, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); + unsigned int (*get_flags)(const __be32 *addr); +}; + +void dt_get_range(const __be32 **cellp, const struct dt_device_node *np, + u64 *address, u64 *size) +{ + *address = dt_next_cell(dt_n_addr_cells(np), cellp); + *size = dt_next_cell(dt_n_size_cells(np), cellp); +} + +void dt_set_cell(__be32 **cellp, int size, u64 val) +{ + int cells = size; + + while ( size-- ) + { + (*cellp)[size] = cpu_to_fdt32(val); + val >>= 32; + } + + (*cellp) += cells; +} + +void dt_set_range(__be32 **cellp, const struct dt_device_node *np, + u64 address, u64 size) +{ + dt_set_cell(cellp, dt_n_addr_cells(np), address); + dt_set_cell(cellp, dt_n_size_cells(np), size); +} + +void dt_child_set_range(__be32 **cellp, int addrcells, int sizecells, + u64 address, u64 size) +{ + dt_set_cell(cellp, addrcells, address); + dt_set_cell(cellp, sizecells, size); +} + +static void __init *unflatten_dt_alloc(unsigned long *mem, unsigned long size, + unsigned long align) +{ + void *res; + + *mem = ROUNDUP(*mem, align); + res = (void *)*mem; + *mem += size; + + return res; +} + +/* Find a property with a given name for a given node and return it. */ +const struct dt_property *dt_find_property(const struct dt_device_node *np, + const char *name, u32 *lenp) +{ + const struct dt_property *pp; + + if ( !np ) + return NULL; + + for ( pp = np->properties; pp; pp = pp->next ) + { + if ( dt_prop_cmp(pp->name, name) == 0 ) + { + if ( lenp ) + *lenp = pp->length; + break; + } + } + + return pp; +} + +const void *dt_get_property(const struct dt_device_node *np, + const char *name, u32 *lenp) +{ + const struct dt_property *pp = dt_find_property(np, name, lenp); + + return pp ? pp->value : NULL; +} + +bool dt_property_read_u32(const struct dt_device_node *np, + const char *name, u32 *out_value) +{ + u32 len; + const __be32 *val; + + val = dt_get_property(np, name, &len); + if ( !val || len < sizeof(*out_value) ) + return 0; + + *out_value = be32_to_cpup(val); + + return 1; +} + + +bool dt_property_read_u64(const struct dt_device_node *np, + const char *name, u64 *out_value) +{ + u32 len; + const __be32 *val; + + val = dt_get_property(np, name, &len); + if ( !val || len < sizeof(*out_value) ) + return 0; + + *out_value = dt_read_number(val, 2); + + return 1; +} +int dt_property_read_string(const struct dt_device_node *np, + const char *propname, const char **out_string) +{ + const struct dt_property *pp = dt_find_property(np, propname, NULL); + + if ( !pp ) + return -EINVAL; + if ( !pp->length ) + return -ENODATA; + if ( strnlen(pp->value, pp->length) >= pp->length ) + return -EILSEQ; + + *out_string = pp->value; + + return 0; +} + +/** + * dt_find_property_value_of_size + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @min: minimum allowed length of property value + * @max: maximum allowed length of property value (0 means unlimited) + * @len: if !=NULL, actual length is written to here + * + * Search for a property in a device node and valid the requested size. + * + * Return: The property value on success, -EINVAL if the property does not + * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data is too small or too large. + */ +static void *dt_find_property_value_of_size(const struct dt_device_node *np, + const char *propname, u32 min, + u32 max, size_t *len) +{ + const struct dt_property *prop = dt_find_property(np, propname, NULL); + + if ( !prop ) + return ERR_PTR(-EINVAL); + if ( !prop->value ) + return ERR_PTR(-ENODATA); + if ( prop->length < min ) + return ERR_PTR(-EOVERFLOW); + if ( max && prop->length > max ) + return ERR_PTR(-EOVERFLOW); + + if ( len ) + *len = prop->length; + + return prop->value; +} + +int dt_property_read_variable_u32_array(const struct dt_device_node *np, + const char *propname, u32 *out_values, + size_t sz_min, size_t sz_max) +{ + size_t sz, count; + const __be32 *val = dt_find_property_value_of_size(np, propname, + (sz_min * sizeof(*out_values)), + (sz_max * sizeof(*out_values)), + &sz); + + if ( IS_ERR(val) ) + return PTR_ERR(val); + + if ( !sz_max ) + sz = sz_min; + else + sz /= sizeof(*out_values); + + count = sz; + while ( count-- ) + *out_values++ = be32_to_cpup(val++); + + return sz; +} + +int dt_property_match_string(const struct dt_device_node *np, + const char *propname, const char *string) +{ + const struct dt_property *dtprop = dt_find_property(np, propname, NULL); + size_t l; + int i; + const char *p, *end; + + if ( !dtprop ) + return -EINVAL; + if ( !dtprop->value ) + return -ENODATA; + + p = dtprop->value; + end = p + dtprop->length; + + for ( i = 0; p < end; i++, p += l ) + { + l = strnlen(p, end - p) + 1; + if ( p + l > end ) + return -EILSEQ; + if ( strcmp(string, p) == 0 ) + return i; /* Found it; return index */ + } + return -ENODATA; +} + +bool dt_device_is_compatible(const struct dt_device_node *device, + const char *compat) +{ + const char* cp; + u32 cplen, l; + + cp = dt_get_property(device, "compatible", &cplen); + if ( cp == NULL ) + return 0; + while ( cplen > 0 ) + { + if ( dt_compat_cmp(cp, compat) == 0 ) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + +bool dt_machine_is_compatible(const char *compat) +{ + const struct dt_device_node *root; + bool rc = false; + + root = dt_find_node_by_path("/"); + if ( root ) + { + rc = dt_device_is_compatible(root, compat); + } + return rc; +} + +struct dt_device_node *dt_find_node_by_name(struct dt_device_node *from, + const char *name) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + dt_for_each_device_node(dt, np) + if ( np->name && (dt_node_cmp(np->name, name) == 0) ) + break; + + return np; +} + +struct dt_device_node *dt_find_node_by_type(struct dt_device_node *from, + const char *type) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + dt_for_each_device_node(dt, np) + if ( np->type && (dt_node_cmp(np->type, type) == 0) ) + break; + + return np; +} + +struct dt_device_node *dt_find_node_by_path_from(struct dt_device_node *from, + const char *path) +{ + struct dt_device_node *np; + + dt_for_each_device_node(from, np) + if ( np->full_name && (dt_node_cmp(np->full_name, path) == 0) ) + break; + + return np; +} + +int dt_find_node_by_gpath(XEN_GUEST_HANDLE(char) u_path, uint32_t u_plen, + struct dt_device_node **node) +{ + char *path; + + path = safe_copy_string_from_guest(u_path, u_plen, PAGE_SIZE); + if ( IS_ERR(path) ) + return PTR_ERR(path); + + *node = dt_find_node_by_path(path); + + xfree(path); + + return (*node == NULL) ? -ESRCH : 0; +} + +struct dt_device_node *dt_find_node_by_alias(const char *alias) +{ + const struct dt_alias_prop *app; + + list_for_each_entry( app, &aliases_lookup, link ) + { + if ( !strcmp(app->alias, alias) ) + return app->np; + } + + return NULL; +} + +const struct dt_device_match * +dt_match_node(const struct dt_device_match *matches, + const struct dt_device_node *node) +{ + if ( !matches ) + return NULL; + + while ( matches->path || matches->type || + matches->compatible || matches->not_available || matches->prop ) + { + bool match = true; + + if ( matches->path ) + match &= dt_node_path_is_equal(node, matches->path); + + if ( matches->type ) + match &= dt_device_type_is_equal(node, matches->type); + + if ( matches->compatible ) + match &= dt_device_is_compatible(node, matches->compatible); + + if ( matches->not_available ) + match &= !dt_device_is_available(node); + + if ( matches->prop ) + match &= dt_find_property(node, matches->prop, NULL) != NULL; + + if ( match ) + return matches; + matches++; + } + + return NULL; +} + +const struct dt_device_node *dt_get_parent(const struct dt_device_node *node) +{ + if ( !node ) + return NULL; + + return node->parent; +} + +struct dt_device_node * +dt_find_compatible_node(struct dt_device_node *from, + const char *type, + const char *compatible) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + dt_for_each_device_node(dt, np) + { + if ( type + && !(np->type && (dt_node_cmp(np->type, type) == 0)) ) + continue; + if ( dt_device_is_compatible(np, compatible) ) + break; + } + + return np; +} + +struct dt_device_node * +dt_find_matching_node(struct dt_device_node *from, + const struct dt_device_match *matches) +{ + struct dt_device_node *np; + struct dt_device_node *dt; + + dt = from ? from->allnext : dt_host; + dt_for_each_device_node(dt, np) + { + if ( dt_match_node(matches, np) ) + return np; + } + + return NULL; +} + +static int __dt_n_addr_cells(const struct dt_device_node *np, bool parent) +{ + const __be32 *ip; + + do { + if ( np->parent && !parent ) + np = np->parent; + parent = false; + + ip = dt_get_property(np, "#address-cells", NULL); + if ( ip ) + return be32_to_cpup(ip); + } while ( np->parent ); + /* No #address-cells property for the root node */ + return DT_ROOT_NODE_ADDR_CELLS_DEFAULT; +} + +static int __dt_n_size_cells(const struct dt_device_node *np, bool parent) +{ + const __be32 *ip; + + do { + if ( np->parent && !parent ) + np = np->parent; + parent = false; + + ip = dt_get_property(np, "#size-cells", NULL); + if ( ip ) + return be32_to_cpup(ip); + } while ( np->parent ); + /* No #address-cells property for the root node */ + return DT_ROOT_NODE_SIZE_CELLS_DEFAULT; +} + +int dt_n_addr_cells(const struct dt_device_node *np) +{ + return __dt_n_addr_cells(np, false); +} + +int dt_n_size_cells(const struct dt_device_node *np) +{ + return __dt_n_size_cells(np, false); +} + +int dt_child_n_addr_cells(const struct dt_device_node *parent) +{ + return __dt_n_addr_cells(parent, true); +} + +int dt_child_n_size_cells(const struct dt_device_node *parent) +{ + return __dt_n_size_cells(parent, true); +} + +/* + * These are defined in Linux where much of this code comes from, but + * are currently unused outside this file in the context of Xen. + */ +#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ + +#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ +#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ +#define IORESOURCE_MEM 0x00000200 +#define IORESOURCE_REG 0x00000300 /* Register offsets */ +#define IORESOURCE_IRQ 0x00000400 +#define IORESOURCE_DMA 0x00000800 +#define IORESOURCE_BUS 0x00001000 + +#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ +#define IORESOURCE_READONLY 0x00004000 +#define IORESOURCE_CACHEABLE 0x00008000 +#define IORESOURCE_RANGELENGTH 0x00010000 +#define IORESOURCE_SHADOWABLE 0x00020000 + +/* + * Default translator (generic bus) + */ +static bool dt_bus_default_match(const struct dt_device_node *node) +{ + /* Root node doesn't have "ranges" property */ + if ( node->parent == NULL ) + return 1; + + /* The default bus is only used when the "ranges" property exists. + * Otherwise we can't translate the address + */ + return (dt_get_property(node, "ranges", NULL) != NULL); +} + +static void dt_bus_default_count_cells(const struct dt_device_node *dev, + int *addrc, int *sizec) +{ + if ( addrc ) + *addrc = dt_n_addr_cells(dev); + if ( sizec ) + *sizec = dt_n_size_cells(dev); +} + +static u64 dt_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = dt_read_number(range, na); + s = dt_read_number(range + na + pna, ns); + da = dt_read_number(addr, na); + + dt_dprintk("DT: default map, cp=%llx, s=%llx, da=%llx\n", + (unsigned long long)cp, (unsigned long long)s, + (unsigned long long)da); + + /* + * If the number of address cells is larger than 2 we assume the + * mapping doesn't specify a physical address. Rather, the address + * specifies an identifier that must match exactly. + */ + if ( na > 2 && memcmp(range, addr, na * 4) != 0 ) + return DT_BAD_ADDR; + + if ( da < cp || da >= (cp + s) ) + return DT_BAD_ADDR; + return da - cp; +} + +static int dt_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = dt_read_number(addr, na); + + memset(addr, 0, na * 4); + a += offset; + if ( na > 1 ) + addr[na - 2] = cpu_to_be32(a >> 32); + addr[na - 1] = cpu_to_be32(a & 0xffffffffu); + + return 0; +} +static unsigned int dt_bus_default_get_flags(const __be32 *addr) +{ + return IORESOURCE_MEM; +} + +/* + * PCI bus specific translator + */ + +static bool dt_node_is_pci(const struct dt_device_node *np) +{ + bool is_pci = !strcmp(np->name, "pcie") || !strcmp(np->name, "pci"); + + if ( is_pci ) + printk(XENLOG_WARNING "%s: Missing device_type\n", np->full_name); + + return is_pci; +} + +static bool dt_bus_pci_match(const struct dt_device_node *np) +{ + /* + * "pciex" is PCI Express "vci" is for the /chaos bridge on 1st-gen PCI + * powermacs "ht" is hypertransport + * + * If none of the device_type match, and that the node name is + * "pcie" or "pci", accept the device as PCI (with a warning). + */ + return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex") || + !strcmp(np->type, "vci") || !strcmp(np->type, "ht") || + dt_node_is_pci(np); +} + +static void dt_bus_pci_count_cells(const struct dt_device_node *np, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = 3; + if (sizec) + *sizec = 2; +} + +static unsigned int dt_bus_pci_get_flags(const __be32 *addr) +{ + unsigned int flags = 0; + u32 w = be32_to_cpup(addr); + + switch((w >> 24) & 0x03) { + case 0x01: + flags |= IORESOURCE_IO; + break; + case 0x02: /* 32 bits */ + case 0x03: /* 64 bits */ + flags |= IORESOURCE_MEM; + break; + } + if (w & 0x40000000) + flags |= IORESOURCE_PREFETCH; + return flags; +} + +static u64 dt_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, + int pna) +{ + u64 cp, s, da; + unsigned int af, rf; + + af = dt_bus_pci_get_flags(addr); + rf = dt_bus_pci_get_flags(range); + + /* Check address type match */ + if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO)) + return DT_BAD_ADDR; + + /* Read address values, skipping high cell */ + cp = dt_read_number(range + 1, na - 1); + s = dt_read_number(range + na + pna, ns); + da = dt_read_number(addr + 1, na - 1); + + dt_dprintk("DT: PCI map, cp=%llx, s=%llx, da=%llx\n", + (unsigned long long)cp, (unsigned long long)s, + (unsigned long long)da); + + if (da < cp || da >= (cp + s)) + return DT_BAD_ADDR; + return da - cp; +} + +static int dt_bus_pci_translate(__be32 *addr, u64 offset, int na) +{ + return dt_bus_default_translate(addr + 1, offset, na - 1); +} + +/* + * Array of bus specific translators + */ +static const struct dt_bus dt_busses[] = +{ + /* PCI */ + { + .name = "pci", + .addresses = "assigned-addresses", + .match = dt_bus_pci_match, + .count_cells = dt_bus_pci_count_cells, + .map = dt_bus_pci_map, + .translate = dt_bus_pci_translate, + .get_flags = dt_bus_pci_get_flags, + }, + /* Default */ + { + .name = "default", + .addresses = "reg", + .match = dt_bus_default_match, + .count_cells = dt_bus_default_count_cells, + .map = dt_bus_default_map, + .translate = dt_bus_default_translate, + .get_flags = dt_bus_default_get_flags, + }, +}; + +static const struct dt_bus *dt_match_bus(const struct dt_device_node *np) +{ + int i; + + for ( i = 0; i < ARRAY_SIZE(dt_busses); i++ ) + if ( !dt_busses[i].match || dt_busses[i].match(np) ) + return &dt_busses[i]; + + return NULL; +} + +static const __be32 *dt_get_address(const struct dt_device_node *dev, + unsigned int index, u64 *size, + unsigned int *flags) +{ + const __be32 *prop; + u32 psize; + const struct dt_device_node *parent; + const struct dt_bus *bus; + int onesize, i, na, ns; + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + return NULL; + + bus = dt_match_bus(parent); + if ( !bus ) + return NULL; + bus->count_cells(dev, &na, &ns); + + if ( !DT_CHECK_ADDR_COUNT(na) ) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = dt_get_property(dev, bus->addresses, &psize); + if ( prop == NULL ) + return NULL; + psize /= 4; + + onesize = na + ns; + for ( i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++ ) + { + if ( i == index ) + { + if ( size ) + *size = dt_read_number(prop + na, ns); + if ( flags ) + *flags = bus->get_flags(prop); + return prop; + } + } + return NULL; +} + +static int dt_translate_one(const struct dt_device_node *parent, + const struct dt_bus *bus, + const struct dt_bus *pbus, + __be32 *addr, int na, int ns, + int pna, const char *rprop) +{ + const __be32 *ranges; + unsigned int rlen; + int rone; + u64 offset = DT_BAD_ADDR; + + ranges = dt_get_property(parent, rprop, &rlen); + if ( ranges == NULL ) + { + printk(XENLOG_ERR "DT: no ranges; cannot translate\n"); + return 1; + } + if ( rlen == 0 ) + { + offset = dt_read_number(addr, na); + memset(addr, 0, pna * 4); + dt_dprintk("DT: empty ranges; 1:1 translation\n"); + goto finish; + } + + dt_dprintk("DT: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for ( ; rlen >= rone; rlen -= rone, ranges += rone ) + { + offset = bus->map(addr, ranges, na, ns, pna); + if ( offset != DT_BAD_ADDR ) + break; + } + if ( offset == DT_BAD_ADDR ) + { + dt_dprintk("DT: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + +finish: + dt_dump_addr("DT: parent translation for:", addr, pna); + dt_dprintk("DT: with offset: %llx\n", (unsigned long long)offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +static u64 __dt_translate_address(const struct dt_device_node *dev, + const __be32 *in_addr, const char *rprop) +{ + const struct dt_device_node *parent = NULL; + const struct dt_bus *bus, *pbus; + __be32 addr[DT_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = DT_BAD_ADDR; + + dt_dprintk("DT: ** translation for device %s **\n", dev->full_name); + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + goto bail; + bus = dt_match_bus(parent); + if ( !bus ) + goto bail; + + /* Count address cells & copy address locally */ + bus->count_cells(dev, &na, &ns); + if ( !DT_CHECK_COUNTS(na, ns) ) + { + printk(XENLOG_ERR "dt_parse: Bad cell count for device %s\n", + dev->full_name); + goto bail; + } + memcpy(addr, in_addr, na * 4); + + dt_dprintk("DT: bus is %s (na=%d, ns=%d) on %s\n", + bus->name, na, ns, parent->full_name); + dt_dump_addr("DT: translating address:", addr, na); + + /* Translate */ + for ( ;; ) + { + /* Switch to parent bus */ + dev = parent; + parent = dt_get_parent(dev); + + /* If root, we have finished */ + if ( parent == NULL ) + { + dt_dprintk("DT: reached root node\n"); + result = dt_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = dt_match_bus(parent); + if ( pbus == NULL ) + { + printk("DT: %s is not a valid bus\n", parent->full_name); + break; + } + pbus->count_cells(dev, &pna, &pns); + if ( !DT_CHECK_COUNTS(pna, pns) ) + { + printk(XENLOG_ERR "dt_parse: Bad cell count for parent %s\n", + dev->full_name); + break; + } + + dt_dprintk("DT: parent bus is %s (na=%d, ns=%d) on %s\n", + pbus->name, pna, pns, parent->full_name); + + /* Apply bus translation */ + if ( dt_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop) ) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + dt_dump_addr("DT: one level translation:", addr, na); + } + +bail: + return result; +} + +/* dt_device_address - Translate device tree address and return it */ +int dt_device_get_address(const struct dt_device_node *dev, unsigned int index, + u64 *addr, u64 *size) +{ + const __be32 *addrp; + unsigned int flags; + + addrp = dt_get_address(dev, index, size, &flags); + if ( addrp == NULL ) + return -EINVAL; + + if ( !addr ) + return -EINVAL; + + *addr = __dt_translate_address(dev, addrp, "ranges"); + + if ( *addr == DT_BAD_ADDR ) + return -EINVAL; + + return 0; +} + +int dt_device_get_paddr(const struct dt_device_node *dev, unsigned int index, + paddr_t *addr, paddr_t *size) +{ + uint64_t dt_addr, dt_size; + int ret; + + ret = dt_device_get_address(dev, index, &dt_addr, &dt_size); + if ( ret ) + return ret; + + if ( !addr ) + return -EINVAL; + + if ( dt_addr != (paddr_t)dt_addr ) + { + printk("Error: Physical address 0x%"PRIx64" for node=%s is greater than max width (%zu bytes) supported\n", + dt_addr, dev->name, sizeof(paddr_t)); + return -ERANGE; + } + + *addr = dt_addr; + + if ( size ) + { + if ( dt_size != (paddr_t)dt_size ) + { + printk("Error: Physical size 0x%"PRIx64" for node=%s is greater than max width (%zu bytes) supported\n", + dt_size, dev->name, sizeof(paddr_t)); + return -ERANGE; + } + + *size = dt_size; + } + + return ret; +} + +int dt_for_each_range(const struct dt_device_node *dev, + int (*cb)(const struct dt_device_node *dev, + uint64_t addr, uint64_t length, + void *data), + void *data) +{ + const struct dt_device_node *parent = NULL; + const struct dt_bus *bus, *pbus; + const __be32 *ranges; + __be32 addr[DT_MAX_ADDR_CELLS]; + unsigned int rlen; + int na, ns, pna, pns, rone; + + bus = dt_match_bus(dev); + if ( !bus ) + return 0; /* device is not a bus */ + + parent = dt_get_parent(dev); + if ( parent == NULL ) + return -EINVAL; + + ranges = dt_get_property(dev, "ranges", &rlen); + if ( ranges == NULL ) + { + printk(XENLOG_ERR "DT: no ranges; cannot enumerate %s\n", + dev->full_name); + return -EINVAL; + } + if ( rlen == 0 ) /* Nothing to do */ + return 0; + + bus->count_cells(dev, &na, &ns); + if ( !DT_CHECK_COUNTS(na, ns) ) + { + printk(XENLOG_ERR "dt_parse: Bad cell count for device %s\n", + dev->full_name); + return -EINVAL; + } + + pbus = dt_match_bus(parent); + if ( pbus == NULL ) + { + printk("DT: %s is not a valid bus\n", parent->full_name); + return -EINVAL; + } + + pbus->count_cells(dev, &pna, &pns); + if ( !DT_CHECK_COUNTS(pna, pns) ) + { + printk(XENLOG_ERR "dt_parse: Bad cell count for parent %s\n", + dev->full_name); + return -EINVAL; + } + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + + dt_dprintk("%s: dev=%s, bus=%s, parent=%s, rlen=%d, rone=%d\n", + __func__, + dt_node_name(dev), bus->name, + dt_node_name(parent), rlen, rone); + + for ( ; rlen >= rone; rlen -= rone, ranges += rone ) + { + uint64_t a, s; + int ret; + + memcpy(addr, ranges + na, 4 * pna); + + a = __dt_translate_address(dev, addr, "ranges"); + s = dt_read_number(ranges + na + pna, ns); + + ret = cb(dev, a, s, data); + if ( ret ) + { + dt_dprintk(" -> callback failed=%d\n", ret); + return ret; + } + + } + + return 0; +} + +/** + * dt_find_node_by_phandle - Find a node given a phandle + * @handle: phandle of the node to find + * + * Returns a node pointer. + */ +struct dt_device_node *dt_find_node_by_phandle(dt_phandle handle) +{ + struct dt_device_node *np; + + dt_for_each_device_node(dt_host, np) + if ( np->phandle == handle ) + break; + + return np; +} + +/** + * dt_irq_find_parent - Given a device node, find its interrupt parent node + * @child: pointer to device node + * + * Returns a pointer to the interrupt parent node, or NULL if the interrupt + * parent could not be determined. + */ +static const struct dt_device_node * +dt_irq_find_parent(const struct dt_device_node *child) +{ + const struct dt_device_node *p; + const __be32 *parp; + + do + { + parp = dt_get_property(child, "interrupt-parent", NULL); + if ( parp == NULL ) + p = dt_get_parent(child); + else + p = dt_find_node_by_phandle(be32_to_cpup(parp)); + child = p; + } while ( p && dt_get_property(p, "#interrupt-cells", NULL) == NULL ); + + return p; +} + +unsigned int dt_number_of_irq(const struct dt_device_node *device) +{ + const struct dt_device_node *p; + const __be32 *intspec, *tmp; + u32 intsize, intlen; + int intnum; + + dt_dprintk("dt_irq_number: dev=%s\n", device->full_name); + + /* Try the new-style interrupts-extended first */ + intnum = dt_count_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells"); + if ( intnum >= 0 ) + { + dt_dprintk(" using 'interrupts-extended' property\n"); + dt_dprintk(" intnum=%d\n", intnum); + return intnum; + } + + /* Get the interrupts property */ + intspec = dt_get_property(device, "interrupts", &intlen); + if ( intspec == NULL ) + return 0; + intlen /= sizeof(*intspec); + + dt_dprintk(" using 'interrupts' property\n"); + dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Look for the interrupt parent. */ + p = dt_irq_find_parent(device); + if ( p == NULL ) + return 0; + + /* Get size of interrupt specifier */ + tmp = dt_get_property(p, "#interrupt-cells", NULL); + if ( tmp == NULL ) + return 0; + intsize = be32_to_cpu(*tmp); + + dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); + + return (intlen / intsize); +} + +unsigned int dt_number_of_address(const struct dt_device_node *dev) +{ + const __be32 *prop; + u32 psize; + const struct dt_device_node *parent; + const struct dt_bus *bus; + int onesize, na, ns; + + /* Get parent & match bus type */ + parent = dt_get_parent(dev); + if ( parent == NULL ) + return 0; + + bus = dt_match_bus(parent); + if ( !bus ) + return 0; + bus->count_cells(dev, &na, &ns); + + if ( !DT_CHECK_COUNTS(na, ns) ) + return 0; + + /* Get "reg" or "assigned-addresses" property */ + prop = dt_get_property(dev, bus->addresses, &psize); + if ( prop == NULL ) + return 0; + + psize /= 4; + onesize = na + ns; + + return (psize / onesize); +} + +int dt_for_each_irq_map(const struct dt_device_node *dev, + int (*cb)(const struct dt_device_node *dev, + const struct dt_irq *dt_irq, + void *data), + void *data) +{ + const struct dt_device_node *ipar, *tnode, *old = NULL; + const __be32 *tmp, *imap; + u32 intsize = 1, addrsize, pintsize = 0, paddrsize = 0; + u32 imaplen; + int i, ret; + + struct dt_raw_irq dt_raw_irq; + struct dt_irq dt_irq; + + dt_dprintk("%s: par=%s cb=%p data=%p\n", __func__, + dev->full_name, cb, data); + + ipar = dev; + + /* First get the #interrupt-cells property of the current cursor + * that tells us how to interpret the passed-in intspec. If there + * is none, we are nice and just walk up the tree + */ + do { + tmp = dt_get_property(ipar, "#interrupt-cells", NULL); + if ( tmp != NULL ) + { + intsize = be32_to_cpu(*tmp); + break; + } + tnode = ipar; + ipar = dt_irq_find_parent(ipar); + } while ( ipar ); + if ( ipar == NULL ) + { + dt_dprintk(" -> no parent found !\n"); + goto fail; + } + + dt_dprintk("%s: ipar=%s, size=%d\n", __func__, ipar->full_name, intsize); + + if ( intsize > DT_MAX_IRQ_SPEC ) + { + dt_dprintk(" -> too many irq specifier cells\n"); + goto fail; + } + + /* Look for this #address-cells. We have to implement the old linux + * trick of looking for the parent here as some device-trees rely on it + */ + old = ipar; + do { + tmp = dt_get_property(old, "#address-cells", NULL); + tnode = dt_get_parent(old); + old = tnode; + } while ( old && tmp == NULL ); + + old = NULL; + addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); + + dt_dprintk(" -> addrsize=%d\n", addrsize); + + /* Now look for an interrupt-map */ + imap = dt_get_property(dev, "interrupt-map", &imaplen); + /* No interrupt-map found. Ignore */ + if ( imap == NULL ) + { + dt_dprintk(" -> no map, ignoring\n"); + return 0; + } + imaplen /= sizeof(u32); + + /* Parse interrupt-map */ + while ( imaplen > (addrsize + intsize + 1) ) + { + /* skip child unit address and child interrupt specifier */ + imap += addrsize + intsize; + imaplen -= addrsize + intsize; + + /* Get the interrupt parent */ + ipar = dt_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + --imaplen; + + /* Check if not found */ + if ( ipar == NULL ) + { + dt_dprintk(" -> imap parent not found !\n"); + goto fail; + } + + dt_dprintk(" -> ipar %s\n", dt_node_name(ipar)); + + /* Get #interrupt-cells and #address-cells of new + * parent + */ + tmp = dt_get_property(ipar, "#interrupt-cells", NULL); + if ( tmp == NULL ) + { + dt_dprintk(" -> parent lacks #interrupt-cells!\n"); + goto fail; + } + pintsize = be32_to_cpu(*tmp); + tmp = dt_get_property(ipar, "#address-cells", NULL); + paddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); + + dt_dprintk(" -> pintsize=%d, paddrsize=%d\n", + pintsize, paddrsize); + + if ( pintsize > DT_MAX_IRQ_SPEC ) + { + dt_dprintk(" -> too many irq specifier cells in parent\n"); + goto fail; + } + + /* Check for malformed properties */ + if ( imaplen < (paddrsize + pintsize) ) + goto fail; + + imap += paddrsize; + imaplen -= paddrsize; + + dt_raw_irq.controller = ipar; + dt_raw_irq.size = pintsize; + for ( i = 0; i < pintsize; i++ ) + dt_raw_irq.specifier[i] = dt_read_number(imap + i, 1); + + if ( dt_raw_irq.controller != dt_interrupt_controller ) + { + /* + * We don't map IRQs connected to secondary IRQ controllers as + * these IRQs have no meaning to us until they connect to the + * primary controller. + * + * Secondary IRQ controllers will at some point connect to + * the primary controller (possibly via other IRQ controllers). + * We map the IRQs at that last connection point. + */ + imap += pintsize; + imaplen -= pintsize; + dt_dprintk(" -> Skipped IRQ for secondary IRQ controller\n"); + continue; + } + + ret = dt_irq_translate(&dt_raw_irq, &dt_irq); + if ( ret ) + { + dt_dprintk(" -> failed to translate IRQ: %d\n", ret); + return ret; + } + + ret = cb(dev, &dt_irq, data); + if ( ret ) + { + dt_dprintk(" -> callback failed=%d\n", ret); + return ret; + } + + imap += pintsize; + imaplen -= pintsize; + + dt_dprintk(" -> imaplen=%d\n", imaplen); + } + + return 0; + +fail: + return -EINVAL; +} + +/** + * dt_irq_map_raw - Low level interrupt tree parsing + * @parent: the device interrupt parent + * @intspec: interrupt specifier ("interrupts" property of the device) + * @ointsize: size of the passed in interrupt specifier + * @addr: address specifier (start of "reg" property of the device) + * @oirq: structure dt_raw_irq filled by this function + * + * Returns 0 on success and a negative number on error + * + * This function is a low-level interrupt tree walking function. It + * can be used to do a partial walk with synthesized reg and interrupts + * properties, for example when resolving PCI interrupts when no device + * node exist for the parent. + */ +static int dt_irq_map_raw(const struct dt_device_node *parent, + const __be32 *intspec, u32 ointsize, + const __be32 *addr, + struct dt_raw_irq *oirq) +{ + const struct dt_device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + const __be32 *tmp, *imap, *imask; + u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; + u32 imaplen; + int match, i; + + dt_dprintk("dt_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", + parent->full_name, be32_to_cpup(intspec), + be32_to_cpup(intspec + 1), ointsize); + + ipar = parent; + + /* First get the #interrupt-cells property of the current cursor + * that tells us how to interpret the passed-in intspec. If there + * is none, we are nice and just walk up the tree + */ + do { + tmp = dt_get_property(ipar, "#interrupt-cells", NULL); + if ( tmp != NULL ) + { + intsize = be32_to_cpu(*tmp); + break; + } + tnode = ipar; + ipar = dt_irq_find_parent(ipar); + } while ( ipar ); + if ( ipar == NULL ) + { + dt_dprintk(" -> no parent found !\n"); + goto fail; + } + + dt_dprintk("dt_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize); + + if ( ointsize != intsize ) + return -EINVAL; + + /* Look for this #address-cells. We have to implement the old linux + * trick of looking for the parent here as some device-trees rely on it + */ + old = ipar; + do { + tmp = dt_get_property(old, "#address-cells", NULL); + tnode = dt_get_parent(old); + old = tnode; + } while ( old && tmp == NULL ); + + old = NULL; + addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); + + dt_dprintk(" -> addrsize=%d\n", addrsize); + + /* Now start the actual "proper" walk of the interrupt tree */ + while ( ipar != NULL ) + { + /* Now check if cursor is an interrupt-controller and if it is + * then we are done + */ + if ( dt_get_property(ipar, "interrupt-controller", NULL) != NULL ) + { + dt_dprintk(" -> got it !\n"); + if ( intsize > DT_MAX_IRQ_SPEC ) + { + dt_dprintk(" -> intsize(%u) greater than DT_MAX_IRQ_SPEC(%u)\n", + intsize, DT_MAX_IRQ_SPEC); + goto fail; + } + for ( i = 0; i < intsize; i++ ) + oirq->specifier[i] = dt_read_number(intspec + i, 1); + oirq->size = intsize; + oirq->controller = ipar; + return 0; + } + + /* Now look for an interrupt-map */ + imap = dt_get_property(ipar, "interrupt-map", &imaplen); + /* No interrupt map, check for an interrupt parent */ + if ( imap == NULL ) + { + dt_dprintk(" -> no map, getting parent\n"); + newpar = dt_irq_find_parent(ipar); + goto skiplevel; + } + imaplen /= sizeof(u32); + + /* Look for a mask */ + imask = dt_get_property(ipar, "interrupt-map-mask", NULL); + + /* If we were passed no "reg" property and we attempt to parse + * an interrupt-map, then #address-cells must be 0. + * Fail if it's not. + */ + if ( addr == NULL && addrsize != 0 ) + { + dt_dprintk(" -> no reg passed in when needed !\n"); + goto fail; + } + + /* Parse interrupt-map */ + match = 0; + while ( imaplen > (addrsize + intsize + 1) && !match ) + { + /* Compare specifiers */ + match = 1; + for ( i = 0; i < addrsize && match; ++i ) + { + __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); + match = ((addr[i] ^ imap[i]) & mask) == 0; + } + for ( ; i < (addrsize + intsize) && match; ++i ) + { + __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); + match = ((intspec[i-addrsize] ^ imap[i]) & mask) == 0; + } + imap += addrsize + intsize; + imaplen -= addrsize + intsize; + + dt_dprintk(" -> match=%d (imaplen=%d)\n", match, imaplen); + + /* Get the interrupt parent */ + newpar = dt_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + --imaplen; + + /* Check if not found */ + if ( newpar == NULL ) + { + dt_dprintk(" -> imap parent not found !\n"); + goto fail; + } + + /* Get #interrupt-cells and #address-cells of new + * parent + */ + tmp = dt_get_property(newpar, "#interrupt-cells", NULL); + if ( tmp == NULL ) + { + dt_dprintk(" -> parent lacks #interrupt-cells!\n"); + goto fail; + } + newintsize = be32_to_cpu(*tmp); + tmp = dt_get_property(newpar, "#address-cells", NULL); + newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); + + dt_dprintk(" -> newintsize=%d, newaddrsize=%d\n", + newintsize, newaddrsize); + + /* Check for malformed properties */ + if ( imaplen < (newaddrsize + newintsize) ) + goto fail; + + imap += newaddrsize + newintsize; + imaplen -= newaddrsize + newintsize; + + dt_dprintk(" -> imaplen=%d\n", imaplen); + } + if ( !match ) + goto fail; + + old = newpar; + addrsize = newaddrsize; + intsize = newintsize; + intspec = imap - intsize; + addr = intspec - addrsize; + + skiplevel: + /* Iterate again with new parent */ + dt_dprintk(" -> new parent: %s\n", dt_node_full_name(newpar)); + ipar = newpar; + newpar = NULL; + } +fail: + return -EINVAL; +} + +int dt_device_get_raw_irq(const struct dt_device_node *device, + unsigned int index, + struct dt_raw_irq *out_irq) +{ + const struct dt_device_node *p; + const __be32 *intspec, *tmp, *addr; + u32 intsize, intlen; + int res = -EINVAL; + struct dt_phandle_args args; + int i; + + dt_dprintk("dt_device_get_raw_irq: dev=%s, index=%u\n", + device->full_name, index); + + /* Get the reg property (if any) */ + addr = dt_get_property(device, "reg", NULL); + + /* Try the new-style interrupts-extended first */ + res = dt_parse_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells", index, &args); + if ( !res ) + { + dt_dprintk(" using 'interrupts-extended' property\n"); + dt_dprintk(" intspec=%d intsize=%d\n", args.args[0], args.args_count); + + for ( i = 0; i < args.args_count; i++ ) + args.args[i] = cpu_to_be32(args.args[i]); + + return dt_irq_map_raw(args.np, args.args, args.args_count, + addr, out_irq); + } + + /* Get the interrupts property */ + intspec = dt_get_property(device, "interrupts", &intlen); + if ( intspec == NULL ) + return -EINVAL; + intlen /= sizeof(*intspec); + + dt_dprintk(" using 'interrupts' property\n"); + dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Look for the interrupt parent. */ + p = dt_irq_find_parent(device); + if ( p == NULL ) + return -EINVAL; + + /* Get size of interrupt specifier */ + tmp = dt_get_property(p, "#interrupt-cells", NULL); + if ( tmp == NULL ) + goto out; + intsize = be32_to_cpu(*tmp); + + dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); + + /* Check index */ + if ( (index + 1) * intsize > intlen ) + goto out; + + /* Get new specifier and map it */ + res = dt_irq_map_raw(p, intspec + index * intsize, intsize, + addr, out_irq); + if ( res ) + goto out; +out: + return res; +} + +int dt_irq_translate(const struct dt_raw_irq *raw, + struct dt_irq *out_irq) +{ + ASSERT(dt_irq_xlate != NULL); + ASSERT(dt_interrupt_controller != NULL); + + /* + * TODO: Retrieve the right irq_xlate. This is only works for the primary + * interrupt controller. + */ + if ( raw->controller != dt_interrupt_controller ) + return -EINVAL; + + return dt_irq_xlate(raw->specifier, raw->size, + &out_irq->irq, &out_irq->type); +} + +int dt_device_get_irq(const struct dt_device_node *device, unsigned int index, + struct dt_irq *out_irq) +{ + struct dt_raw_irq raw; + int res; + + res = dt_device_get_raw_irq(device, index, &raw); + + if ( res ) + return res; + + return dt_irq_translate(&raw, out_irq); +} + +bool dt_device_is_available(const struct dt_device_node *device) +{ + const char *status; + u32 statlen; + + status = dt_get_property(device, "status", &statlen); + if ( status == NULL ) + return 1; + + if ( statlen > 0 ) + { + if ( !strcmp(status, "okay") || !strcmp(status, "ok") ) + return 1; + } + + return 0; +} + +bool dt_device_for_passthrough(const struct dt_device_node *device) +{ + return (dt_find_property(device, "xen,passthrough", NULL) != NULL); + +} + +static int __dt_parse_phandle_with_args(const struct dt_device_node *np, + const char *list_name, + const char *cells_name, + int cell_count, int index, + struct dt_phandle_args *out_args) +{ + const __be32 *list, *list_end; + int rc = 0, cur_index = 0; + u32 size, count = 0; + struct dt_device_node *node = NULL; + dt_phandle phandle; + + /* Retrieve the phandle list property */ + list = dt_get_property(np, list_name, &size); + if ( !list ) + return -ENOENT; + list_end = list + size / sizeof(*list); + + /* Loop over the phandles until all the requested entry is found */ + while ( list < list_end ) + { + rc = -EINVAL; + count = 0; + + /* + * If phandle is 0, then it is an empty entry with no + * arguments. Skip forward to the next entry. + * */ + phandle = be32_to_cpup(list++); + if ( phandle ) + { + /* + * Find the provider node and parse the #*-cells + * property to determine the argument length. + * + * This is not needed if the cell count is hard-coded + * (i.e. cells_name not set, but cell_count is set), + * except when we're going to return the found node + * below. + */ + if ( cells_name || cur_index == index ) + { + node = dt_find_node_by_phandle(phandle); + if ( !node ) + { + printk(XENLOG_ERR "%s: could not find phandle\n", + np->full_name); + goto err; + } + } + + if ( cells_name ) + { + if ( !dt_property_read_u32(node, cells_name, &count) ) + { + printk("%s: could not get %s for %s\n", + np->full_name, cells_name, node->full_name); + goto err; + } + } + else + count = cell_count; + + /* + * Make sure that the arguments actually fit in the + * remaining property data length + */ + if ( list + count > list_end ) + { + printk(XENLOG_ERR "%s: arguments longer than property\n", + np->full_name); + goto err; + } + } + + /* + * All of the error cases above bail out of the loop, so at + * this point, the parsing is successful. If the requested + * index matches, then fill the out_args structure and return, + * or return -ENOENT for an empty entry. + */ + rc = -ENOENT; + if ( cur_index == index ) + { + if (!phandle) + goto err; + + if ( out_args ) + { + int i; + + WARN_ON(count > MAX_PHANDLE_ARGS); + if (count > MAX_PHANDLE_ARGS) + count = MAX_PHANDLE_ARGS; + out_args->np = node; + out_args->args_count = count; + for ( i = 0; i < count; i++ ) + out_args->args[i] = be32_to_cpup(list++); + } + + /* Found it! return success */ + return 0; + } + + node = NULL; + list += count; + cur_index++; + } + + /* + * Returning result will be one of: + * -ENOENT : index is for empty phandle + * -EINVAL : parsing error on data + * [1..n] : Number of phandle (count mode; when index = -1) + */ + rc = index < 0 ? cur_index : -ENOENT; +err: + return rc; +} + +struct dt_device_node *dt_parse_phandle(const struct dt_device_node *np, + const char *phandle_name, int index) +{ + struct dt_phandle_args args; + + if (index < 0) + return NULL; + + if (__dt_parse_phandle_with_args(np, phandle_name, NULL, 0, + index, &args)) + return NULL; + + return args.np; +} + + +int dt_parse_phandle_with_args(const struct dt_device_node *np, + const char *list_name, + const char *cells_name, int index, + struct dt_phandle_args *out_args) +{ + if ( index < 0 ) + return -EINVAL; + return __dt_parse_phandle_with_args(np, list_name, cells_name, 0, + index, out_args); +} + +int dt_count_phandle_with_args(const struct dt_device_node *np, + const char *list_name, + const char *cells_name) +{ + return __dt_parse_phandle_with_args(np, list_name, cells_name, 0, -1, NULL); +} + +/** + * unflatten_dt_node - Alloc and populate a device_node from the flat tree + * @fdt: The parent device tree blob + * @mem: Memory chunk to use for allocating device nodes and properties + * @p: pointer to node in flat tree + * @dad: Parent struct device_node + * @allnextpp: pointer to ->allnext from last allocated device_node + * @fpsize: Size of the node path up at the current depth. + */ +static unsigned long unflatten_dt_node(const void *fdt, + unsigned long mem, + unsigned long *p, + struct dt_device_node *dad, + struct dt_device_node ***allnextpp, + unsigned long fpsize) +{ + struct dt_device_node *np; + struct dt_property *pp, **prev_pp = NULL; + char *pathp; + u32 tag; + unsigned int l, allocl; + int has_name = 0; + int new_format = 0; + + tag = be32_to_cpup((__be32 *)(*p)); + if ( tag != FDT_BEGIN_NODE ) + { + printk(XENLOG_WARNING "Weird tag at start of node: %x\n", tag); + return mem; + } + *p += 4; + pathp = (char *)*p; + l = allocl = strlen(pathp) + 1; + *p = ROUNDUP(*p + l, 4); + + /* version 0x10 has a more compact unit name here instead of the full + * path. we accumulate the full path size using "fpsize", we'll rebuild + * it later. We detect this because the first character of the name is + * not '/'. + */ + if ( (*pathp) != '/' ) + { + new_format = 1; + if ( fpsize == 0 ) + { + /* root node: special case. fpsize accounts for path + * plus terminating zero. root node only has '/', so + * fpsize should be 2, but we want to avoid the first + * level nodes to have two '/' so we use fpsize 1 here + */ + fpsize = 1; + allocl = 2; + } + else + { + /* account for '/' and path size minus terminal 0 + * already in 'l' + */ + fpsize += l; + allocl = fpsize; + } + } + + np = unflatten_dt_alloc(&mem, sizeof(struct dt_device_node) + allocl, + __alignof__(struct dt_device_node)); + if ( allnextpp ) + { + memset(np, 0, sizeof(*np)); + np->full_name = ((char *)np) + sizeof(struct dt_device_node); + /* By default dom0 owns the device */ + np->used_by = 0; + /* By default the device is not protected */ + np->is_protected = false; + INIT_LIST_HEAD(&np->domain_list); + + if ( new_format ) + { + char *fn = np->full_name; + /* rebuild full path for new format */ + if ( dad && dad->parent ) + { + strlcpy(fn, dad->full_name, allocl); +#ifdef DEBUG_DT + if ( (strlen(fn) + l + 1) != allocl ) + { + dt_dprintk("%s: p: %d, l: %d, a: %d\n", + pathp, (int)strlen(fn), + l, allocl); + } +#endif + fn += strlen(fn); + } + *(fn++) = '/'; + memcpy(fn, pathp, l); + } + else + memcpy(np->full_name, pathp, l); + prev_pp = &np->properties; + **allnextpp = np; + *allnextpp = &np->allnext; + if ( dad != NULL ) + { + np->parent = dad; + /* we temporarily use the next field as `last_child'*/ + if ( dad->next == NULL ) + dad->child = np; + else + dad->next->sibling = np; + dad->next = np; + } + } + /* process properties */ + while ( 1 ) + { + u32 sz, noff; + const char *pname; + + tag = be32_to_cpup((__be32 *)(*p)); + if ( tag == FDT_NOP ) + { + *p += 4; + continue; + } + if ( tag != FDT_PROP ) + break; + *p += 4; + sz = be32_to_cpup((__be32 *)(*p)); + noff = be32_to_cpup((__be32 *)((*p) + 4)); + *p += 8; + if ( fdt_version(fdt) < 0x10 ) + *p = ROUNDUP(*p, sz >= 8 ? 8 : 4); + + pname = fdt_string(fdt, noff); + if ( pname == NULL ) + { + dt_dprintk("Can't find property name in list!\n"); + break; + } + if ( strcmp(pname, "name") == 0 ) + has_name = 1; + l = strlen(pname) + 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property), + __alignof__(struct dt_property)); + if ( allnextpp ) + { + /* We accept flattened tree phandles either in + * ePAPR-style "phandle" properties, or the + * legacy "linux,phandle" properties. If both + * appear and have different values, things + * will get weird. Don't do that. */ + if ( (strcmp(pname, "phandle") == 0) || + (strcmp(pname, "linux,phandle") == 0) ) + { + if ( np->phandle == 0 ) + np->phandle = be32_to_cpup((__be32*)*p); + } + /* And we process the "ibm,phandle" property + * used in pSeries dynamic device tree + * stuff */ + if ( strcmp(pname, "ibm,phandle") == 0 ) + np->phandle = be32_to_cpup((__be32 *)*p); + pp->name = pname; + pp->length = sz; + pp->value = (void *)*p; + *prev_pp = pp; + prev_pp = &pp->next; + } + *p = ROUNDUP((*p) + sz, 4); + } + /* with version 0x10 we may not have the name property, recreate + * it here from the unit name if absent + */ + if ( !has_name ) + { + char *p1 = pathp, *ps = pathp, *pa = NULL; + int sz; + + while ( *p1 ) + { + if ( (*p1) == '@' ) + pa = p1; + if ( (*p1) == '/' ) + ps = p1 + 1; + p1++; + } + if ( pa < ps ) + pa = p1; + sz = (pa - ps) + 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property) + sz, + __alignof__(struct dt_property)); + if ( allnextpp ) + { + pp->name = "name"; + pp->length = sz; + pp->value = pp + 1; + /* + * The device tree creation code assume that the property + * "name" is not a fake. + * To avoid a big divergence with Linux code, only remove + * property link. In this case we will lose a bit of memory + */ +#if 0 + *prev_pp = pp; + prev_pp = &pp->next; +#endif + np->name = pp->value; + memcpy(pp->value, ps, sz - 1); + ((char *)pp->value)[sz - 1] = 0; + dt_dprintk("fixed up name for %s -> %s\n", pathp, + (char *)pp->value); + /* Generic device initialization */ + np->dev.type = DEV_DT; + np->dev.of_node = np; + } + } + if ( allnextpp ) + { + *prev_pp = NULL; + np->name = (np->name) ? : dt_get_property(np, "name", NULL); + np->type = dt_get_property(np, "device_type", NULL); + + if ( !np->name ) + np->name = ""; + if ( !np->type ) + np->type = ""; + } + while ( tag == FDT_BEGIN_NODE || tag == FDT_NOP ) + { + if ( tag == FDT_NOP ) + *p += 4; + else + mem = unflatten_dt_node(fdt, mem, p, np, allnextpp, fpsize); + tag = be32_to_cpup((__be32 *)(*p)); + } + if ( tag != FDT_END_NODE ) + { + printk(XENLOG_WARNING "Weird tag at end of node: %x\n", tag); + return mem; + } + + *p += 4; + return mem; +} + +int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes) +{ + unsigned long start, mem, size; + struct dt_device_node **allnextp = mynodes; + + dt_dprintk(" -> unflatten_device_tree()\n"); + + dt_dprintk("Unflattening device tree:\n"); + dt_dprintk("magic: %#08x\n", fdt_magic(fdt)); + dt_dprintk("size: %#08x\n", fdt_totalsize(fdt)); + dt_dprintk("version: %#08x\n", fdt_version(fdt)); + + /* First pass, scan for size */ + start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); + size = unflatten_dt_node(fdt, 0, &start, NULL, NULL, 0); + if ( !size ) + return -EINVAL; + + size = (size | 3) + 1; + + dt_dprintk(" size is %#lx allocating...\n", size); + + /* Allocate memory for the expanded device tree */ + mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node)); + if ( !mem ) + return -ENOMEM; + + ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeefU); + + dt_dprintk(" unflattening %lx...\n", mem); + + /* Second pass, do actual unflattening */ + start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); + unflatten_dt_node(fdt, mem, &start, NULL, &allnextp, 0); + if ( be32_to_cpup((__be32 *)start) != FDT_END ) + { + printk(XENLOG_ERR "Weird tag at end of tree: %08x\n", + *((u32 *)start)); + xfree((void *)mem); + return -EINVAL; + } + + if ( be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeefU ) + { + printk(XENLOG_ERR "End of tree marker overwritten: %08x\n", + be32_to_cpu(((__be32 *)mem)[size / 4])); + xfree((void *)mem); + return -EINVAL; + } + + *allnextp = NULL; + + dt_dprintk(" <- unflatten_device_tree()\n"); + + return 0; +} + +static void dt_alias_add(struct dt_alias_prop *ap, + struct dt_device_node *np, + int id, const char *stem, int stem_len) +{ + ap->np = np; + ap->id = id; + strlcpy(ap->stem, stem, stem_len + 1); + list_add_tail(&ap->link, &aliases_lookup); + dt_dprintk("adding DT alias:%s: stem=%s id=%d node=%s\n", + ap->alias, ap->stem, ap->id, dt_node_full_name(np)); +} + +/** + * dt_alias_scan - Scan all properties of 'aliases' node + * + * The function scans all the properties of 'aliases' node and populate + * the the global lookup table with the properties. It returns the + * number of alias_prop found, or error code in error case. + */ +static void __init dt_alias_scan(void) +{ + const struct dt_property *pp; + const struct dt_device_node *aliases; + + aliases = dt_find_node_by_path("/aliases"); + if ( !aliases ) + return; + + dt_for_each_property_node( aliases, pp ) + { + const char *start = pp->name; + const char *end = start + strlen(start); + struct dt_device_node *np; + struct dt_alias_prop *ap; + int id, len; + + /* Skip those we do not want to proceed */ + if ( !strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle") ) + continue; + + np = dt_find_node_by_path(pp->value); + if ( !np ) + continue; + + /* walk the alias backwards to extract the id and work out + * the 'stem' string */ + while ( isdigit(*(end-1)) && end > start ) + end--; + len = end - start; + + id = simple_strtoll(end, NULL, 10); + + /* Allocate an alias_prop with enough space for the stem */ + ap = _xmalloc(sizeof(*ap) + len + 1, 4); + if ( !ap ) + continue; + ap->alias = start; + dt_alias_add(ap, np, id, start, len); + } +} + +struct dt_device_node * __init +dt_find_interrupt_controller(const struct dt_device_match *matches) +{ + struct dt_device_node *np = NULL; + + while ( (np = dt_find_matching_node(np, matches)) ) + { + if ( !dt_find_property(np, "interrupt-controller", NULL) ) + continue; + + if ( dt_get_parent(np) ) + break; + } + + return np; +} + +void __init dt_unflatten_host_device_tree(void) +{ + int error = unflatten_device_tree(device_tree_flattened, &dt_host); + + if ( error ) + panic("unflatten_device_tree failed with error %d\n", error); + + dt_alias_scan(); +} + +int dt_get_pci_domain_nr(struct dt_device_node *node) +{ + u32 domain; + int error; + + error = dt_property_read_u32(node, "linux,pci-domain", &domain); + if ( !error ) + return -EINVAL; + + return (u16)domain; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c deleted file mode 100644 index 8d1017a49d..0000000000 --- a/xen/common/device_tree.c +++ /dev/null @@ -1,2253 +0,0 @@ -/* - * Device Tree - * - * Copyright (C) 2012 Citrix Systems, Inc. - * Copyright 2009 Benjamin Herrenschmidt, IBM Corp - * benh@kernel.crashing.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const void *device_tree_flattened; -dt_irq_xlate_func dt_irq_xlate; -/* Host device tree */ -struct dt_device_node *dt_host; -/* Interrupt controller node*/ -const struct dt_device_node *dt_interrupt_controller; -DEFINE_RWLOCK(dt_host_lock); - -/** - * struct dt_alias_prop - Alias property in 'aliases' node - * @link: List node to link the structure in aliases_lookup list - * @alias: Alias property name - * @np: Pointer to device_node that the alias stands for - * @id: Index value from end of alias name - * @stem: Alias string without the index - * - * The structure represents one alias property of 'aliases' node as - * an entry in aliases_lookup list. - */ -struct dt_alias_prop { - struct list_head link; - const char *alias; - struct dt_device_node *np; - int id; - char stem[0]; -}; - -static LIST_HEAD(aliases_lookup); - -#ifdef CONFIG_DEVICE_TREE_DEBUG -static void dt_dump_addr(const char *s, const __be32 *addr, int na) -{ - dt_dprintk("%s", s); - while ( na-- ) - dt_dprintk(" %08x", be32_to_cpu(*(addr++))); - dt_dprintk("\n"); -} -#else -static void dt_dump_addr(const char *s, const __be32 *addr, int na) { } -#endif - -#define DT_BAD_ADDR ((u64)-1) - -/* Max address size we deal with */ -#define DT_MAX_ADDR_CELLS 4 -#define DT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= DT_MAX_ADDR_CELLS) -#define DT_CHECK_COUNTS(na, ns) (DT_CHECK_ADDR_COUNT(na) && (ns) > 0) - -/* Callbacks for bus specific translators */ -struct dt_bus -{ - const char *name; - const char *addresses; - bool (*match)(const struct dt_device_node *node); - void (*count_cells)(const struct dt_device_node *child, - int *addrc, int *sizec); - u64 (*map)(__be32 *addr, const __be32 *range, int na, int ns, int pna); - int (*translate)(__be32 *addr, u64 offset, int na); - unsigned int (*get_flags)(const __be32 *addr); -}; - -void dt_get_range(const __be32 **cellp, const struct dt_device_node *np, - u64 *address, u64 *size) -{ - *address = dt_next_cell(dt_n_addr_cells(np), cellp); - *size = dt_next_cell(dt_n_size_cells(np), cellp); -} - -void dt_set_cell(__be32 **cellp, int size, u64 val) -{ - int cells = size; - - while ( size-- ) - { - (*cellp)[size] = cpu_to_fdt32(val); - val >>= 32; - } - - (*cellp) += cells; -} - -void dt_set_range(__be32 **cellp, const struct dt_device_node *np, - u64 address, u64 size) -{ - dt_set_cell(cellp, dt_n_addr_cells(np), address); - dt_set_cell(cellp, dt_n_size_cells(np), size); -} - -void dt_child_set_range(__be32 **cellp, int addrcells, int sizecells, - u64 address, u64 size) -{ - dt_set_cell(cellp, addrcells, address); - dt_set_cell(cellp, sizecells, size); -} - -static void __init *unflatten_dt_alloc(unsigned long *mem, unsigned long size, - unsigned long align) -{ - void *res; - - *mem = ROUNDUP(*mem, align); - res = (void *)*mem; - *mem += size; - - return res; -} - -/* Find a property with a given name for a given node and return it. */ -const struct dt_property *dt_find_property(const struct dt_device_node *np, - const char *name, u32 *lenp) -{ - const struct dt_property *pp; - - if ( !np ) - return NULL; - - for ( pp = np->properties; pp; pp = pp->next ) - { - if ( dt_prop_cmp(pp->name, name) == 0 ) - { - if ( lenp ) - *lenp = pp->length; - break; - } - } - - return pp; -} - -const void *dt_get_property(const struct dt_device_node *np, - const char *name, u32 *lenp) -{ - const struct dt_property *pp = dt_find_property(np, name, lenp); - - return pp ? pp->value : NULL; -} - -bool dt_property_read_u32(const struct dt_device_node *np, - const char *name, u32 *out_value) -{ - u32 len; - const __be32 *val; - - val = dt_get_property(np, name, &len); - if ( !val || len < sizeof(*out_value) ) - return 0; - - *out_value = be32_to_cpup(val); - - return 1; -} - - -bool dt_property_read_u64(const struct dt_device_node *np, - const char *name, u64 *out_value) -{ - u32 len; - const __be32 *val; - - val = dt_get_property(np, name, &len); - if ( !val || len < sizeof(*out_value) ) - return 0; - - *out_value = dt_read_number(val, 2); - - return 1; -} -int dt_property_read_string(const struct dt_device_node *np, - const char *propname, const char **out_string) -{ - const struct dt_property *pp = dt_find_property(np, propname, NULL); - - if ( !pp ) - return -EINVAL; - if ( !pp->length ) - return -ENODATA; - if ( strnlen(pp->value, pp->length) >= pp->length ) - return -EILSEQ; - - *out_string = pp->value; - - return 0; -} - -/** - * dt_find_property_value_of_size - * - * @np: device node from which the property value is to be read. - * @propname: name of the property to be searched. - * @min: minimum allowed length of property value - * @max: maximum allowed length of property value (0 means unlimited) - * @len: if !=NULL, actual length is written to here - * - * Search for a property in a device node and valid the requested size. - * - * Return: The property value on success, -EINVAL if the property does not - * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the - * property data is too small or too large. - */ -static void *dt_find_property_value_of_size(const struct dt_device_node *np, - const char *propname, u32 min, - u32 max, size_t *len) -{ - const struct dt_property *prop = dt_find_property(np, propname, NULL); - - if ( !prop ) - return ERR_PTR(-EINVAL); - if ( !prop->value ) - return ERR_PTR(-ENODATA); - if ( prop->length < min ) - return ERR_PTR(-EOVERFLOW); - if ( max && prop->length > max ) - return ERR_PTR(-EOVERFLOW); - - if ( len ) - *len = prop->length; - - return prop->value; -} - -int dt_property_read_variable_u32_array(const struct dt_device_node *np, - const char *propname, u32 *out_values, - size_t sz_min, size_t sz_max) -{ - size_t sz, count; - const __be32 *val = dt_find_property_value_of_size(np, propname, - (sz_min * sizeof(*out_values)), - (sz_max * sizeof(*out_values)), - &sz); - - if ( IS_ERR(val) ) - return PTR_ERR(val); - - if ( !sz_max ) - sz = sz_min; - else - sz /= sizeof(*out_values); - - count = sz; - while ( count-- ) - *out_values++ = be32_to_cpup(val++); - - return sz; -} - -int dt_property_match_string(const struct dt_device_node *np, - const char *propname, const char *string) -{ - const struct dt_property *dtprop = dt_find_property(np, propname, NULL); - size_t l; - int i; - const char *p, *end; - - if ( !dtprop ) - return -EINVAL; - if ( !dtprop->value ) - return -ENODATA; - - p = dtprop->value; - end = p + dtprop->length; - - for ( i = 0; p < end; i++, p += l ) - { - l = strnlen(p, end - p) + 1; - if ( p + l > end ) - return -EILSEQ; - if ( strcmp(string, p) == 0 ) - return i; /* Found it; return index */ - } - return -ENODATA; -} - -bool dt_device_is_compatible(const struct dt_device_node *device, - const char *compat) -{ - const char* cp; - u32 cplen, l; - - cp = dt_get_property(device, "compatible", &cplen); - if ( cp == NULL ) - return 0; - while ( cplen > 0 ) - { - if ( dt_compat_cmp(cp, compat) == 0 ) - return 1; - l = strlen(cp) + 1; - cp += l; - cplen -= l; - } - - return 0; -} - -bool dt_machine_is_compatible(const char *compat) -{ - const struct dt_device_node *root; - bool rc = false; - - root = dt_find_node_by_path("/"); - if ( root ) - { - rc = dt_device_is_compatible(root, compat); - } - return rc; -} - -struct dt_device_node *dt_find_node_by_name(struct dt_device_node *from, - const char *name) -{ - struct dt_device_node *np; - struct dt_device_node *dt; - - dt = from ? from->allnext : dt_host; - dt_for_each_device_node(dt, np) - if ( np->name && (dt_node_cmp(np->name, name) == 0) ) - break; - - return np; -} - -struct dt_device_node *dt_find_node_by_type(struct dt_device_node *from, - const char *type) -{ - struct dt_device_node *np; - struct dt_device_node *dt; - - dt = from ? from->allnext : dt_host; - dt_for_each_device_node(dt, np) - if ( np->type && (dt_node_cmp(np->type, type) == 0) ) - break; - - return np; -} - -struct dt_device_node *dt_find_node_by_path_from(struct dt_device_node *from, - const char *path) -{ - struct dt_device_node *np; - - dt_for_each_device_node(from, np) - if ( np->full_name && (dt_node_cmp(np->full_name, path) == 0) ) - break; - - return np; -} - -int dt_find_node_by_gpath(XEN_GUEST_HANDLE(char) u_path, uint32_t u_plen, - struct dt_device_node **node) -{ - char *path; - - path = safe_copy_string_from_guest(u_path, u_plen, PAGE_SIZE); - if ( IS_ERR(path) ) - return PTR_ERR(path); - - *node = dt_find_node_by_path(path); - - xfree(path); - - return (*node == NULL) ? -ESRCH : 0; -} - -struct dt_device_node *dt_find_node_by_alias(const char *alias) -{ - const struct dt_alias_prop *app; - - list_for_each_entry( app, &aliases_lookup, link ) - { - if ( !strcmp(app->alias, alias) ) - return app->np; - } - - return NULL; -} - -const struct dt_device_match * -dt_match_node(const struct dt_device_match *matches, - const struct dt_device_node *node) -{ - if ( !matches ) - return NULL; - - while ( matches->path || matches->type || - matches->compatible || matches->not_available || matches->prop ) - { - bool match = true; - - if ( matches->path ) - match &= dt_node_path_is_equal(node, matches->path); - - if ( matches->type ) - match &= dt_device_type_is_equal(node, matches->type); - - if ( matches->compatible ) - match &= dt_device_is_compatible(node, matches->compatible); - - if ( matches->not_available ) - match &= !dt_device_is_available(node); - - if ( matches->prop ) - match &= dt_find_property(node, matches->prop, NULL) != NULL; - - if ( match ) - return matches; - matches++; - } - - return NULL; -} - -const struct dt_device_node *dt_get_parent(const struct dt_device_node *node) -{ - if ( !node ) - return NULL; - - return node->parent; -} - -struct dt_device_node * -dt_find_compatible_node(struct dt_device_node *from, - const char *type, - const char *compatible) -{ - struct dt_device_node *np; - struct dt_device_node *dt; - - dt = from ? from->allnext : dt_host; - dt_for_each_device_node(dt, np) - { - if ( type - && !(np->type && (dt_node_cmp(np->type, type) == 0)) ) - continue; - if ( dt_device_is_compatible(np, compatible) ) - break; - } - - return np; -} - -struct dt_device_node * -dt_find_matching_node(struct dt_device_node *from, - const struct dt_device_match *matches) -{ - struct dt_device_node *np; - struct dt_device_node *dt; - - dt = from ? from->allnext : dt_host; - dt_for_each_device_node(dt, np) - { - if ( dt_match_node(matches, np) ) - return np; - } - - return NULL; -} - -static int __dt_n_addr_cells(const struct dt_device_node *np, bool parent) -{ - const __be32 *ip; - - do { - if ( np->parent && !parent ) - np = np->parent; - parent = false; - - ip = dt_get_property(np, "#address-cells", NULL); - if ( ip ) - return be32_to_cpup(ip); - } while ( np->parent ); - /* No #address-cells property for the root node */ - return DT_ROOT_NODE_ADDR_CELLS_DEFAULT; -} - -static int __dt_n_size_cells(const struct dt_device_node *np, bool parent) -{ - const __be32 *ip; - - do { - if ( np->parent && !parent ) - np = np->parent; - parent = false; - - ip = dt_get_property(np, "#size-cells", NULL); - if ( ip ) - return be32_to_cpup(ip); - } while ( np->parent ); - /* No #address-cells property for the root node */ - return DT_ROOT_NODE_SIZE_CELLS_DEFAULT; -} - -int dt_n_addr_cells(const struct dt_device_node *np) -{ - return __dt_n_addr_cells(np, false); -} - -int dt_n_size_cells(const struct dt_device_node *np) -{ - return __dt_n_size_cells(np, false); -} - -int dt_child_n_addr_cells(const struct dt_device_node *parent) -{ - return __dt_n_addr_cells(parent, true); -} - -int dt_child_n_size_cells(const struct dt_device_node *parent) -{ - return __dt_n_size_cells(parent, true); -} - -/* - * These are defined in Linux where much of this code comes from, but - * are currently unused outside this file in the context of Xen. - */ -#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ - -#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ -#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ -#define IORESOURCE_MEM 0x00000200 -#define IORESOURCE_REG 0x00000300 /* Register offsets */ -#define IORESOURCE_IRQ 0x00000400 -#define IORESOURCE_DMA 0x00000800 -#define IORESOURCE_BUS 0x00001000 - -#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ -#define IORESOURCE_READONLY 0x00004000 -#define IORESOURCE_CACHEABLE 0x00008000 -#define IORESOURCE_RANGELENGTH 0x00010000 -#define IORESOURCE_SHADOWABLE 0x00020000 - -/* - * Default translator (generic bus) - */ -static bool dt_bus_default_match(const struct dt_device_node *node) -{ - /* Root node doesn't have "ranges" property */ - if ( node->parent == NULL ) - return 1; - - /* The default bus is only used when the "ranges" property exists. - * Otherwise we can't translate the address - */ - return (dt_get_property(node, "ranges", NULL) != NULL); -} - -static void dt_bus_default_count_cells(const struct dt_device_node *dev, - int *addrc, int *sizec) -{ - if ( addrc ) - *addrc = dt_n_addr_cells(dev); - if ( sizec ) - *sizec = dt_n_size_cells(dev); -} - -static u64 dt_bus_default_map(__be32 *addr, const __be32 *range, - int na, int ns, int pna) -{ - u64 cp, s, da; - - cp = dt_read_number(range, na); - s = dt_read_number(range + na + pna, ns); - da = dt_read_number(addr, na); - - dt_dprintk("DT: default map, cp=%llx, s=%llx, da=%llx\n", - (unsigned long long)cp, (unsigned long long)s, - (unsigned long long)da); - - /* - * If the number of address cells is larger than 2 we assume the - * mapping doesn't specify a physical address. Rather, the address - * specifies an identifier that must match exactly. - */ - if ( na > 2 && memcmp(range, addr, na * 4) != 0 ) - return DT_BAD_ADDR; - - if ( da < cp || da >= (cp + s) ) - return DT_BAD_ADDR; - return da - cp; -} - -static int dt_bus_default_translate(__be32 *addr, u64 offset, int na) -{ - u64 a = dt_read_number(addr, na); - - memset(addr, 0, na * 4); - a += offset; - if ( na > 1 ) - addr[na - 2] = cpu_to_be32(a >> 32); - addr[na - 1] = cpu_to_be32(a & 0xffffffffu); - - return 0; -} -static unsigned int dt_bus_default_get_flags(const __be32 *addr) -{ - return IORESOURCE_MEM; -} - -/* - * PCI bus specific translator - */ - -static bool dt_node_is_pci(const struct dt_device_node *np) -{ - bool is_pci = !strcmp(np->name, "pcie") || !strcmp(np->name, "pci"); - - if ( is_pci ) - printk(XENLOG_WARNING "%s: Missing device_type\n", np->full_name); - - return is_pci; -} - -static bool dt_bus_pci_match(const struct dt_device_node *np) -{ - /* - * "pciex" is PCI Express "vci" is for the /chaos bridge on 1st-gen PCI - * powermacs "ht" is hypertransport - * - * If none of the device_type match, and that the node name is - * "pcie" or "pci", accept the device as PCI (with a warning). - */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex") || - !strcmp(np->type, "vci") || !strcmp(np->type, "ht") || - dt_node_is_pci(np); -} - -static void dt_bus_pci_count_cells(const struct dt_device_node *np, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 3; - if (sizec) - *sizec = 2; -} - -static unsigned int dt_bus_pci_get_flags(const __be32 *addr) -{ - unsigned int flags = 0; - u32 w = be32_to_cpup(addr); - - switch((w >> 24) & 0x03) { - case 0x01: - flags |= IORESOURCE_IO; - break; - case 0x02: /* 32 bits */ - case 0x03: /* 64 bits */ - flags |= IORESOURCE_MEM; - break; - } - if (w & 0x40000000) - flags |= IORESOURCE_PREFETCH; - return flags; -} - -static u64 dt_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns, - int pna) -{ - u64 cp, s, da; - unsigned int af, rf; - - af = dt_bus_pci_get_flags(addr); - rf = dt_bus_pci_get_flags(range); - - /* Check address type match */ - if ((af ^ rf) & (IORESOURCE_MEM | IORESOURCE_IO)) - return DT_BAD_ADDR; - - /* Read address values, skipping high cell */ - cp = dt_read_number(range + 1, na - 1); - s = dt_read_number(range + na + pna, ns); - da = dt_read_number(addr + 1, na - 1); - - dt_dprintk("DT: PCI map, cp=%llx, s=%llx, da=%llx\n", - (unsigned long long)cp, (unsigned long long)s, - (unsigned long long)da); - - if (da < cp || da >= (cp + s)) - return DT_BAD_ADDR; - return da - cp; -} - -static int dt_bus_pci_translate(__be32 *addr, u64 offset, int na) -{ - return dt_bus_default_translate(addr + 1, offset, na - 1); -} - -/* - * Array of bus specific translators - */ -static const struct dt_bus dt_busses[] = -{ - /* PCI */ - { - .name = "pci", - .addresses = "assigned-addresses", - .match = dt_bus_pci_match, - .count_cells = dt_bus_pci_count_cells, - .map = dt_bus_pci_map, - .translate = dt_bus_pci_translate, - .get_flags = dt_bus_pci_get_flags, - }, - /* Default */ - { - .name = "default", - .addresses = "reg", - .match = dt_bus_default_match, - .count_cells = dt_bus_default_count_cells, - .map = dt_bus_default_map, - .translate = dt_bus_default_translate, - .get_flags = dt_bus_default_get_flags, - }, -}; - -static const struct dt_bus *dt_match_bus(const struct dt_device_node *np) -{ - int i; - - for ( i = 0; i < ARRAY_SIZE(dt_busses); i++ ) - if ( !dt_busses[i].match || dt_busses[i].match(np) ) - return &dt_busses[i]; - - return NULL; -} - -static const __be32 *dt_get_address(const struct dt_device_node *dev, - unsigned int index, u64 *size, - unsigned int *flags) -{ - const __be32 *prop; - u32 psize; - const struct dt_device_node *parent; - const struct dt_bus *bus; - int onesize, i, na, ns; - - /* Get parent & match bus type */ - parent = dt_get_parent(dev); - if ( parent == NULL ) - return NULL; - - bus = dt_match_bus(parent); - if ( !bus ) - return NULL; - bus->count_cells(dev, &na, &ns); - - if ( !DT_CHECK_ADDR_COUNT(na) ) - return NULL; - - /* Get "reg" or "assigned-addresses" property */ - prop = dt_get_property(dev, bus->addresses, &psize); - if ( prop == NULL ) - return NULL; - psize /= 4; - - onesize = na + ns; - for ( i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++ ) - { - if ( i == index ) - { - if ( size ) - *size = dt_read_number(prop + na, ns); - if ( flags ) - *flags = bus->get_flags(prop); - return prop; - } - } - return NULL; -} - -static int dt_translate_one(const struct dt_device_node *parent, - const struct dt_bus *bus, - const struct dt_bus *pbus, - __be32 *addr, int na, int ns, - int pna, const char *rprop) -{ - const __be32 *ranges; - unsigned int rlen; - int rone; - u64 offset = DT_BAD_ADDR; - - ranges = dt_get_property(parent, rprop, &rlen); - if ( ranges == NULL ) - { - printk(XENLOG_ERR "DT: no ranges; cannot translate\n"); - return 1; - } - if ( rlen == 0 ) - { - offset = dt_read_number(addr, na); - memset(addr, 0, pna * 4); - dt_dprintk("DT: empty ranges; 1:1 translation\n"); - goto finish; - } - - dt_dprintk("DT: walking ranges...\n"); - - /* Now walk through the ranges */ - rlen /= 4; - rone = na + pna + ns; - for ( ; rlen >= rone; rlen -= rone, ranges += rone ) - { - offset = bus->map(addr, ranges, na, ns, pna); - if ( offset != DT_BAD_ADDR ) - break; - } - if ( offset == DT_BAD_ADDR ) - { - dt_dprintk("DT: not found !\n"); - return 1; - } - memcpy(addr, ranges + na, 4 * pna); - -finish: - dt_dump_addr("DT: parent translation for:", addr, pna); - dt_dprintk("DT: with offset: %llx\n", (unsigned long long)offset); - - /* Translate it into parent bus space */ - return pbus->translate(addr, offset, pna); -} - -/* - * Translate an address from the device-tree into a CPU physical address, - * this walks up the tree and applies the various bus mappings on the - * way. - * - * Note: We consider that crossing any level with #size-cells == 0 to mean - * that translation is impossible (that is we are not dealing with a value - * that can be mapped to a cpu physical address). This is not really specified - * that way, but this is traditionally the way IBM at least do things - */ -static u64 __dt_translate_address(const struct dt_device_node *dev, - const __be32 *in_addr, const char *rprop) -{ - const struct dt_device_node *parent = NULL; - const struct dt_bus *bus, *pbus; - __be32 addr[DT_MAX_ADDR_CELLS]; - int na, ns, pna, pns; - u64 result = DT_BAD_ADDR; - - dt_dprintk("DT: ** translation for device %s **\n", dev->full_name); - - /* Get parent & match bus type */ - parent = dt_get_parent(dev); - if ( parent == NULL ) - goto bail; - bus = dt_match_bus(parent); - if ( !bus ) - goto bail; - - /* Count address cells & copy address locally */ - bus->count_cells(dev, &na, &ns); - if ( !DT_CHECK_COUNTS(na, ns) ) - { - printk(XENLOG_ERR "dt_parse: Bad cell count for device %s\n", - dev->full_name); - goto bail; - } - memcpy(addr, in_addr, na * 4); - - dt_dprintk("DT: bus is %s (na=%d, ns=%d) on %s\n", - bus->name, na, ns, parent->full_name); - dt_dump_addr("DT: translating address:", addr, na); - - /* Translate */ - for ( ;; ) - { - /* Switch to parent bus */ - dev = parent; - parent = dt_get_parent(dev); - - /* If root, we have finished */ - if ( parent == NULL ) - { - dt_dprintk("DT: reached root node\n"); - result = dt_read_number(addr, na); - break; - } - - /* Get new parent bus and counts */ - pbus = dt_match_bus(parent); - if ( pbus == NULL ) - { - printk("DT: %s is not a valid bus\n", parent->full_name); - break; - } - pbus->count_cells(dev, &pna, &pns); - if ( !DT_CHECK_COUNTS(pna, pns) ) - { - printk(XENLOG_ERR "dt_parse: Bad cell count for parent %s\n", - dev->full_name); - break; - } - - dt_dprintk("DT: parent bus is %s (na=%d, ns=%d) on %s\n", - pbus->name, pna, pns, parent->full_name); - - /* Apply bus translation */ - if ( dt_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop) ) - break; - - /* Complete the move up one level */ - na = pna; - ns = pns; - bus = pbus; - - dt_dump_addr("DT: one level translation:", addr, na); - } - -bail: - return result; -} - -/* dt_device_address - Translate device tree address and return it */ -int dt_device_get_address(const struct dt_device_node *dev, unsigned int index, - u64 *addr, u64 *size) -{ - const __be32 *addrp; - unsigned int flags; - - addrp = dt_get_address(dev, index, size, &flags); - if ( addrp == NULL ) - return -EINVAL; - - if ( !addr ) - return -EINVAL; - - *addr = __dt_translate_address(dev, addrp, "ranges"); - - if ( *addr == DT_BAD_ADDR ) - return -EINVAL; - - return 0; -} - -int dt_device_get_paddr(const struct dt_device_node *dev, unsigned int index, - paddr_t *addr, paddr_t *size) -{ - uint64_t dt_addr, dt_size; - int ret; - - ret = dt_device_get_address(dev, index, &dt_addr, &dt_size); - if ( ret ) - return ret; - - if ( !addr ) - return -EINVAL; - - if ( dt_addr != (paddr_t)dt_addr ) - { - printk("Error: Physical address 0x%"PRIx64" for node=%s is greater than max width (%zu bytes) supported\n", - dt_addr, dev->name, sizeof(paddr_t)); - return -ERANGE; - } - - *addr = dt_addr; - - if ( size ) - { - if ( dt_size != (paddr_t)dt_size ) - { - printk("Error: Physical size 0x%"PRIx64" for node=%s is greater than max width (%zu bytes) supported\n", - dt_size, dev->name, sizeof(paddr_t)); - return -ERANGE; - } - - *size = dt_size; - } - - return ret; -} - -int dt_for_each_range(const struct dt_device_node *dev, - int (*cb)(const struct dt_device_node *dev, - uint64_t addr, uint64_t length, - void *data), - void *data) -{ - const struct dt_device_node *parent = NULL; - const struct dt_bus *bus, *pbus; - const __be32 *ranges; - __be32 addr[DT_MAX_ADDR_CELLS]; - unsigned int rlen; - int na, ns, pna, pns, rone; - - bus = dt_match_bus(dev); - if ( !bus ) - return 0; /* device is not a bus */ - - parent = dt_get_parent(dev); - if ( parent == NULL ) - return -EINVAL; - - ranges = dt_get_property(dev, "ranges", &rlen); - if ( ranges == NULL ) - { - printk(XENLOG_ERR "DT: no ranges; cannot enumerate %s\n", - dev->full_name); - return -EINVAL; - } - if ( rlen == 0 ) /* Nothing to do */ - return 0; - - bus->count_cells(dev, &na, &ns); - if ( !DT_CHECK_COUNTS(na, ns) ) - { - printk(XENLOG_ERR "dt_parse: Bad cell count for device %s\n", - dev->full_name); - return -EINVAL; - } - - pbus = dt_match_bus(parent); - if ( pbus == NULL ) - { - printk("DT: %s is not a valid bus\n", parent->full_name); - return -EINVAL; - } - - pbus->count_cells(dev, &pna, &pns); - if ( !DT_CHECK_COUNTS(pna, pns) ) - { - printk(XENLOG_ERR "dt_parse: Bad cell count for parent %s\n", - dev->full_name); - return -EINVAL; - } - - /* Now walk through the ranges */ - rlen /= 4; - rone = na + pna + ns; - - dt_dprintk("%s: dev=%s, bus=%s, parent=%s, rlen=%d, rone=%d\n", - __func__, - dt_node_name(dev), bus->name, - dt_node_name(parent), rlen, rone); - - for ( ; rlen >= rone; rlen -= rone, ranges += rone ) - { - uint64_t a, s; - int ret; - - memcpy(addr, ranges + na, 4 * pna); - - a = __dt_translate_address(dev, addr, "ranges"); - s = dt_read_number(ranges + na + pna, ns); - - ret = cb(dev, a, s, data); - if ( ret ) - { - dt_dprintk(" -> callback failed=%d\n", ret); - return ret; - } - - } - - return 0; -} - -/** - * dt_find_node_by_phandle - Find a node given a phandle - * @handle: phandle of the node to find - * - * Returns a node pointer. - */ -struct dt_device_node *dt_find_node_by_phandle(dt_phandle handle) -{ - struct dt_device_node *np; - - dt_for_each_device_node(dt_host, np) - if ( np->phandle == handle ) - break; - - return np; -} - -/** - * dt_irq_find_parent - Given a device node, find its interrupt parent node - * @child: pointer to device node - * - * Returns a pointer to the interrupt parent node, or NULL if the interrupt - * parent could not be determined. - */ -static const struct dt_device_node * -dt_irq_find_parent(const struct dt_device_node *child) -{ - const struct dt_device_node *p; - const __be32 *parp; - - do - { - parp = dt_get_property(child, "interrupt-parent", NULL); - if ( parp == NULL ) - p = dt_get_parent(child); - else - p = dt_find_node_by_phandle(be32_to_cpup(parp)); - child = p; - } while ( p && dt_get_property(p, "#interrupt-cells", NULL) == NULL ); - - return p; -} - -unsigned int dt_number_of_irq(const struct dt_device_node *device) -{ - const struct dt_device_node *p; - const __be32 *intspec, *tmp; - u32 intsize, intlen; - int intnum; - - dt_dprintk("dt_irq_number: dev=%s\n", device->full_name); - - /* Try the new-style interrupts-extended first */ - intnum = dt_count_phandle_with_args(device, "interrupts-extended", - "#interrupt-cells"); - if ( intnum >= 0 ) - { - dt_dprintk(" using 'interrupts-extended' property\n"); - dt_dprintk(" intnum=%d\n", intnum); - return intnum; - } - - /* Get the interrupts property */ - intspec = dt_get_property(device, "interrupts", &intlen); - if ( intspec == NULL ) - return 0; - intlen /= sizeof(*intspec); - - dt_dprintk(" using 'interrupts' property\n"); - dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); - - /* Look for the interrupt parent. */ - p = dt_irq_find_parent(device); - if ( p == NULL ) - return 0; - - /* Get size of interrupt specifier */ - tmp = dt_get_property(p, "#interrupt-cells", NULL); - if ( tmp == NULL ) - return 0; - intsize = be32_to_cpu(*tmp); - - dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); - - return (intlen / intsize); -} - -unsigned int dt_number_of_address(const struct dt_device_node *dev) -{ - const __be32 *prop; - u32 psize; - const struct dt_device_node *parent; - const struct dt_bus *bus; - int onesize, na, ns; - - /* Get parent & match bus type */ - parent = dt_get_parent(dev); - if ( parent == NULL ) - return 0; - - bus = dt_match_bus(parent); - if ( !bus ) - return 0; - bus->count_cells(dev, &na, &ns); - - if ( !DT_CHECK_COUNTS(na, ns) ) - return 0; - - /* Get "reg" or "assigned-addresses" property */ - prop = dt_get_property(dev, bus->addresses, &psize); - if ( prop == NULL ) - return 0; - - psize /= 4; - onesize = na + ns; - - return (psize / onesize); -} - -int dt_for_each_irq_map(const struct dt_device_node *dev, - int (*cb)(const struct dt_device_node *dev, - const struct dt_irq *dt_irq, - void *data), - void *data) -{ - const struct dt_device_node *ipar, *tnode, *old = NULL; - const __be32 *tmp, *imap; - u32 intsize = 1, addrsize, pintsize = 0, paddrsize = 0; - u32 imaplen; - int i, ret; - - struct dt_raw_irq dt_raw_irq; - struct dt_irq dt_irq; - - dt_dprintk("%s: par=%s cb=%p data=%p\n", __func__, - dev->full_name, cb, data); - - ipar = dev; - - /* First get the #interrupt-cells property of the current cursor - * that tells us how to interpret the passed-in intspec. If there - * is none, we are nice and just walk up the tree - */ - do { - tmp = dt_get_property(ipar, "#interrupt-cells", NULL); - if ( tmp != NULL ) - { - intsize = be32_to_cpu(*tmp); - break; - } - tnode = ipar; - ipar = dt_irq_find_parent(ipar); - } while ( ipar ); - if ( ipar == NULL ) - { - dt_dprintk(" -> no parent found !\n"); - goto fail; - } - - dt_dprintk("%s: ipar=%s, size=%d\n", __func__, ipar->full_name, intsize); - - if ( intsize > DT_MAX_IRQ_SPEC ) - { - dt_dprintk(" -> too many irq specifier cells\n"); - goto fail; - } - - /* Look for this #address-cells. We have to implement the old linux - * trick of looking for the parent here as some device-trees rely on it - */ - old = ipar; - do { - tmp = dt_get_property(old, "#address-cells", NULL); - tnode = dt_get_parent(old); - old = tnode; - } while ( old && tmp == NULL ); - - old = NULL; - addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); - - dt_dprintk(" -> addrsize=%d\n", addrsize); - - /* Now look for an interrupt-map */ - imap = dt_get_property(dev, "interrupt-map", &imaplen); - /* No interrupt-map found. Ignore */ - if ( imap == NULL ) - { - dt_dprintk(" -> no map, ignoring\n"); - return 0; - } - imaplen /= sizeof(u32); - - /* Parse interrupt-map */ - while ( imaplen > (addrsize + intsize + 1) ) - { - /* skip child unit address and child interrupt specifier */ - imap += addrsize + intsize; - imaplen -= addrsize + intsize; - - /* Get the interrupt parent */ - ipar = dt_find_node_by_phandle(be32_to_cpup(imap)); - imap++; - --imaplen; - - /* Check if not found */ - if ( ipar == NULL ) - { - dt_dprintk(" -> imap parent not found !\n"); - goto fail; - } - - dt_dprintk(" -> ipar %s\n", dt_node_name(ipar)); - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - tmp = dt_get_property(ipar, "#interrupt-cells", NULL); - if ( tmp == NULL ) - { - dt_dprintk(" -> parent lacks #interrupt-cells!\n"); - goto fail; - } - pintsize = be32_to_cpu(*tmp); - tmp = dt_get_property(ipar, "#address-cells", NULL); - paddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); - - dt_dprintk(" -> pintsize=%d, paddrsize=%d\n", - pintsize, paddrsize); - - if ( pintsize > DT_MAX_IRQ_SPEC ) - { - dt_dprintk(" -> too many irq specifier cells in parent\n"); - goto fail; - } - - /* Check for malformed properties */ - if ( imaplen < (paddrsize + pintsize) ) - goto fail; - - imap += paddrsize; - imaplen -= paddrsize; - - dt_raw_irq.controller = ipar; - dt_raw_irq.size = pintsize; - for ( i = 0; i < pintsize; i++ ) - dt_raw_irq.specifier[i] = dt_read_number(imap + i, 1); - - if ( dt_raw_irq.controller != dt_interrupt_controller ) - { - /* - * We don't map IRQs connected to secondary IRQ controllers as - * these IRQs have no meaning to us until they connect to the - * primary controller. - * - * Secondary IRQ controllers will at some point connect to - * the primary controller (possibly via other IRQ controllers). - * We map the IRQs at that last connection point. - */ - imap += pintsize; - imaplen -= pintsize; - dt_dprintk(" -> Skipped IRQ for secondary IRQ controller\n"); - continue; - } - - ret = dt_irq_translate(&dt_raw_irq, &dt_irq); - if ( ret ) - { - dt_dprintk(" -> failed to translate IRQ: %d\n", ret); - return ret; - } - - ret = cb(dev, &dt_irq, data); - if ( ret ) - { - dt_dprintk(" -> callback failed=%d\n", ret); - return ret; - } - - imap += pintsize; - imaplen -= pintsize; - - dt_dprintk(" -> imaplen=%d\n", imaplen); - } - - return 0; - -fail: - return -EINVAL; -} - -/** - * dt_irq_map_raw - Low level interrupt tree parsing - * @parent: the device interrupt parent - * @intspec: interrupt specifier ("interrupts" property of the device) - * @ointsize: size of the passed in interrupt specifier - * @addr: address specifier (start of "reg" property of the device) - * @oirq: structure dt_raw_irq filled by this function - * - * Returns 0 on success and a negative number on error - * - * This function is a low-level interrupt tree walking function. It - * can be used to do a partial walk with synthesized reg and interrupts - * properties, for example when resolving PCI interrupts when no device - * node exist for the parent. - */ -static int dt_irq_map_raw(const struct dt_device_node *parent, - const __be32 *intspec, u32 ointsize, - const __be32 *addr, - struct dt_raw_irq *oirq) -{ - const struct dt_device_node *ipar, *tnode, *old = NULL, *newpar = NULL; - const __be32 *tmp, *imap, *imask; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - u32 imaplen; - int match, i; - - dt_dprintk("dt_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", - parent->full_name, be32_to_cpup(intspec), - be32_to_cpup(intspec + 1), ointsize); - - ipar = parent; - - /* First get the #interrupt-cells property of the current cursor - * that tells us how to interpret the passed-in intspec. If there - * is none, we are nice and just walk up the tree - */ - do { - tmp = dt_get_property(ipar, "#interrupt-cells", NULL); - if ( tmp != NULL ) - { - intsize = be32_to_cpu(*tmp); - break; - } - tnode = ipar; - ipar = dt_irq_find_parent(ipar); - } while ( ipar ); - if ( ipar == NULL ) - { - dt_dprintk(" -> no parent found !\n"); - goto fail; - } - - dt_dprintk("dt_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize); - - if ( ointsize != intsize ) - return -EINVAL; - - /* Look for this #address-cells. We have to implement the old linux - * trick of looking for the parent here as some device-trees rely on it - */ - old = ipar; - do { - tmp = dt_get_property(old, "#address-cells", NULL); - tnode = dt_get_parent(old); - old = tnode; - } while ( old && tmp == NULL ); - - old = NULL; - addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); - - dt_dprintk(" -> addrsize=%d\n", addrsize); - - /* Now start the actual "proper" walk of the interrupt tree */ - while ( ipar != NULL ) - { - /* Now check if cursor is an interrupt-controller and if it is - * then we are done - */ - if ( dt_get_property(ipar, "interrupt-controller", NULL) != NULL ) - { - dt_dprintk(" -> got it !\n"); - if ( intsize > DT_MAX_IRQ_SPEC ) - { - dt_dprintk(" -> intsize(%u) greater than DT_MAX_IRQ_SPEC(%u)\n", - intsize, DT_MAX_IRQ_SPEC); - goto fail; - } - for ( i = 0; i < intsize; i++ ) - oirq->specifier[i] = dt_read_number(intspec + i, 1); - oirq->size = intsize; - oirq->controller = ipar; - return 0; - } - - /* Now look for an interrupt-map */ - imap = dt_get_property(ipar, "interrupt-map", &imaplen); - /* No interrupt map, check for an interrupt parent */ - if ( imap == NULL ) - { - dt_dprintk(" -> no map, getting parent\n"); - newpar = dt_irq_find_parent(ipar); - goto skiplevel; - } - imaplen /= sizeof(u32); - - /* Look for a mask */ - imask = dt_get_property(ipar, "interrupt-map-mask", NULL); - - /* If we were passed no "reg" property and we attempt to parse - * an interrupt-map, then #address-cells must be 0. - * Fail if it's not. - */ - if ( addr == NULL && addrsize != 0 ) - { - dt_dprintk(" -> no reg passed in when needed !\n"); - goto fail; - } - - /* Parse interrupt-map */ - match = 0; - while ( imaplen > (addrsize + intsize + 1) && !match ) - { - /* Compare specifiers */ - match = 1; - for ( i = 0; i < addrsize && match; ++i ) - { - __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); - match = ((addr[i] ^ imap[i]) & mask) == 0; - } - for ( ; i < (addrsize + intsize) && match; ++i ) - { - __be32 mask = imask ? imask[i] : cpu_to_be32(0xffffffffu); - match = ((intspec[i-addrsize] ^ imap[i]) & mask) == 0; - } - imap += addrsize + intsize; - imaplen -= addrsize + intsize; - - dt_dprintk(" -> match=%d (imaplen=%d)\n", match, imaplen); - - /* Get the interrupt parent */ - newpar = dt_find_node_by_phandle(be32_to_cpup(imap)); - imap++; - --imaplen; - - /* Check if not found */ - if ( newpar == NULL ) - { - dt_dprintk(" -> imap parent not found !\n"); - goto fail; - } - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - tmp = dt_get_property(newpar, "#interrupt-cells", NULL); - if ( tmp == NULL ) - { - dt_dprintk(" -> parent lacks #interrupt-cells!\n"); - goto fail; - } - newintsize = be32_to_cpu(*tmp); - tmp = dt_get_property(newpar, "#address-cells", NULL); - newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); - - dt_dprintk(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if ( imaplen < (newaddrsize + newintsize) ) - goto fail; - - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; - - dt_dprintk(" -> imaplen=%d\n", imaplen); - } - if ( !match ) - goto fail; - - old = newpar; - addrsize = newaddrsize; - intsize = newintsize; - intspec = imap - intsize; - addr = intspec - addrsize; - - skiplevel: - /* Iterate again with new parent */ - dt_dprintk(" -> new parent: %s\n", dt_node_full_name(newpar)); - ipar = newpar; - newpar = NULL; - } -fail: - return -EINVAL; -} - -int dt_device_get_raw_irq(const struct dt_device_node *device, - unsigned int index, - struct dt_raw_irq *out_irq) -{ - const struct dt_device_node *p; - const __be32 *intspec, *tmp, *addr; - u32 intsize, intlen; - int res = -EINVAL; - struct dt_phandle_args args; - int i; - - dt_dprintk("dt_device_get_raw_irq: dev=%s, index=%u\n", - device->full_name, index); - - /* Get the reg property (if any) */ - addr = dt_get_property(device, "reg", NULL); - - /* Try the new-style interrupts-extended first */ - res = dt_parse_phandle_with_args(device, "interrupts-extended", - "#interrupt-cells", index, &args); - if ( !res ) - { - dt_dprintk(" using 'interrupts-extended' property\n"); - dt_dprintk(" intspec=%d intsize=%d\n", args.args[0], args.args_count); - - for ( i = 0; i < args.args_count; i++ ) - args.args[i] = cpu_to_be32(args.args[i]); - - return dt_irq_map_raw(args.np, args.args, args.args_count, - addr, out_irq); - } - - /* Get the interrupts property */ - intspec = dt_get_property(device, "interrupts", &intlen); - if ( intspec == NULL ) - return -EINVAL; - intlen /= sizeof(*intspec); - - dt_dprintk(" using 'interrupts' property\n"); - dt_dprintk(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); - - /* Look for the interrupt parent. */ - p = dt_irq_find_parent(device); - if ( p == NULL ) - return -EINVAL; - - /* Get size of interrupt specifier */ - tmp = dt_get_property(p, "#interrupt-cells", NULL); - if ( tmp == NULL ) - goto out; - intsize = be32_to_cpu(*tmp); - - dt_dprintk(" intsize=%d intlen=%d\n", intsize, intlen); - - /* Check index */ - if ( (index + 1) * intsize > intlen ) - goto out; - - /* Get new specifier and map it */ - res = dt_irq_map_raw(p, intspec + index * intsize, intsize, - addr, out_irq); - if ( res ) - goto out; -out: - return res; -} - -int dt_irq_translate(const struct dt_raw_irq *raw, - struct dt_irq *out_irq) -{ - ASSERT(dt_irq_xlate != NULL); - ASSERT(dt_interrupt_controller != NULL); - - /* - * TODO: Retrieve the right irq_xlate. This is only works for the primary - * interrupt controller. - */ - if ( raw->controller != dt_interrupt_controller ) - return -EINVAL; - - return dt_irq_xlate(raw->specifier, raw->size, - &out_irq->irq, &out_irq->type); -} - -int dt_device_get_irq(const struct dt_device_node *device, unsigned int index, - struct dt_irq *out_irq) -{ - struct dt_raw_irq raw; - int res; - - res = dt_device_get_raw_irq(device, index, &raw); - - if ( res ) - return res; - - return dt_irq_translate(&raw, out_irq); -} - -bool dt_device_is_available(const struct dt_device_node *device) -{ - const char *status; - u32 statlen; - - status = dt_get_property(device, "status", &statlen); - if ( status == NULL ) - return 1; - - if ( statlen > 0 ) - { - if ( !strcmp(status, "okay") || !strcmp(status, "ok") ) - return 1; - } - - return 0; -} - -bool dt_device_for_passthrough(const struct dt_device_node *device) -{ - return (dt_find_property(device, "xen,passthrough", NULL) != NULL); - -} - -static int __dt_parse_phandle_with_args(const struct dt_device_node *np, - const char *list_name, - const char *cells_name, - int cell_count, int index, - struct dt_phandle_args *out_args) -{ - const __be32 *list, *list_end; - int rc = 0, cur_index = 0; - u32 size, count = 0; - struct dt_device_node *node = NULL; - dt_phandle phandle; - - /* Retrieve the phandle list property */ - list = dt_get_property(np, list_name, &size); - if ( !list ) - return -ENOENT; - list_end = list + size / sizeof(*list); - - /* Loop over the phandles until all the requested entry is found */ - while ( list < list_end ) - { - rc = -EINVAL; - count = 0; - - /* - * If phandle is 0, then it is an empty entry with no - * arguments. Skip forward to the next entry. - * */ - phandle = be32_to_cpup(list++); - if ( phandle ) - { - /* - * Find the provider node and parse the #*-cells - * property to determine the argument length. - * - * This is not needed if the cell count is hard-coded - * (i.e. cells_name not set, but cell_count is set), - * except when we're going to return the found node - * below. - */ - if ( cells_name || cur_index == index ) - { - node = dt_find_node_by_phandle(phandle); - if ( !node ) - { - printk(XENLOG_ERR "%s: could not find phandle\n", - np->full_name); - goto err; - } - } - - if ( cells_name ) - { - if ( !dt_property_read_u32(node, cells_name, &count) ) - { - printk("%s: could not get %s for %s\n", - np->full_name, cells_name, node->full_name); - goto err; - } - } - else - count = cell_count; - - /* - * Make sure that the arguments actually fit in the - * remaining property data length - */ - if ( list + count > list_end ) - { - printk(XENLOG_ERR "%s: arguments longer than property\n", - np->full_name); - goto err; - } - } - - /* - * All of the error cases above bail out of the loop, so at - * this point, the parsing is successful. If the requested - * index matches, then fill the out_args structure and return, - * or return -ENOENT for an empty entry. - */ - rc = -ENOENT; - if ( cur_index == index ) - { - if (!phandle) - goto err; - - if ( out_args ) - { - int i; - - WARN_ON(count > MAX_PHANDLE_ARGS); - if (count > MAX_PHANDLE_ARGS) - count = MAX_PHANDLE_ARGS; - out_args->np = node; - out_args->args_count = count; - for ( i = 0; i < count; i++ ) - out_args->args[i] = be32_to_cpup(list++); - } - - /* Found it! return success */ - return 0; - } - - node = NULL; - list += count; - cur_index++; - } - - /* - * Returning result will be one of: - * -ENOENT : index is for empty phandle - * -EINVAL : parsing error on data - * [1..n] : Number of phandle (count mode; when index = -1) - */ - rc = index < 0 ? cur_index : -ENOENT; -err: - return rc; -} - -struct dt_device_node *dt_parse_phandle(const struct dt_device_node *np, - const char *phandle_name, int index) -{ - struct dt_phandle_args args; - - if (index < 0) - return NULL; - - if (__dt_parse_phandle_with_args(np, phandle_name, NULL, 0, - index, &args)) - return NULL; - - return args.np; -} - - -int dt_parse_phandle_with_args(const struct dt_device_node *np, - const char *list_name, - const char *cells_name, int index, - struct dt_phandle_args *out_args) -{ - if ( index < 0 ) - return -EINVAL; - return __dt_parse_phandle_with_args(np, list_name, cells_name, 0, - index, out_args); -} - -int dt_count_phandle_with_args(const struct dt_device_node *np, - const char *list_name, - const char *cells_name) -{ - return __dt_parse_phandle_with_args(np, list_name, cells_name, 0, -1, NULL); -} - -/** - * unflatten_dt_node - Alloc and populate a device_node from the flat tree - * @fdt: The parent device tree blob - * @mem: Memory chunk to use for allocating device nodes and properties - * @p: pointer to node in flat tree - * @dad: Parent struct device_node - * @allnextpp: pointer to ->allnext from last allocated device_node - * @fpsize: Size of the node path up at the current depth. - */ -static unsigned long unflatten_dt_node(const void *fdt, - unsigned long mem, - unsigned long *p, - struct dt_device_node *dad, - struct dt_device_node ***allnextpp, - unsigned long fpsize) -{ - struct dt_device_node *np; - struct dt_property *pp, **prev_pp = NULL; - char *pathp; - u32 tag; - unsigned int l, allocl; - int has_name = 0; - int new_format = 0; - - tag = be32_to_cpup((__be32 *)(*p)); - if ( tag != FDT_BEGIN_NODE ) - { - printk(XENLOG_WARNING "Weird tag at start of node: %x\n", tag); - return mem; - } - *p += 4; - pathp = (char *)*p; - l = allocl = strlen(pathp) + 1; - *p = ROUNDUP(*p + l, 4); - - /* version 0x10 has a more compact unit name here instead of the full - * path. we accumulate the full path size using "fpsize", we'll rebuild - * it later. We detect this because the first character of the name is - * not '/'. - */ - if ( (*pathp) != '/' ) - { - new_format = 1; - if ( fpsize == 0 ) - { - /* root node: special case. fpsize accounts for path - * plus terminating zero. root node only has '/', so - * fpsize should be 2, but we want to avoid the first - * level nodes to have two '/' so we use fpsize 1 here - */ - fpsize = 1; - allocl = 2; - } - else - { - /* account for '/' and path size minus terminal 0 - * already in 'l' - */ - fpsize += l; - allocl = fpsize; - } - } - - np = unflatten_dt_alloc(&mem, sizeof(struct dt_device_node) + allocl, - __alignof__(struct dt_device_node)); - if ( allnextpp ) - { - memset(np, 0, sizeof(*np)); - np->full_name = ((char *)np) + sizeof(struct dt_device_node); - /* By default dom0 owns the device */ - np->used_by = 0; - /* By default the device is not protected */ - np->is_protected = false; - INIT_LIST_HEAD(&np->domain_list); - - if ( new_format ) - { - char *fn = np->full_name; - /* rebuild full path for new format */ - if ( dad && dad->parent ) - { - strlcpy(fn, dad->full_name, allocl); -#ifdef DEBUG_DT - if ( (strlen(fn) + l + 1) != allocl ) - { - dt_dprintk("%s: p: %d, l: %d, a: %d\n", - pathp, (int)strlen(fn), - l, allocl); - } -#endif - fn += strlen(fn); - } - *(fn++) = '/'; - memcpy(fn, pathp, l); - } - else - memcpy(np->full_name, pathp, l); - prev_pp = &np->properties; - **allnextpp = np; - *allnextpp = &np->allnext; - if ( dad != NULL ) - { - np->parent = dad; - /* we temporarily use the next field as `last_child'*/ - if ( dad->next == NULL ) - dad->child = np; - else - dad->next->sibling = np; - dad->next = np; - } - } - /* process properties */ - while ( 1 ) - { - u32 sz, noff; - const char *pname; - - tag = be32_to_cpup((__be32 *)(*p)); - if ( tag == FDT_NOP ) - { - *p += 4; - continue; - } - if ( tag != FDT_PROP ) - break; - *p += 4; - sz = be32_to_cpup((__be32 *)(*p)); - noff = be32_to_cpup((__be32 *)((*p) + 4)); - *p += 8; - if ( fdt_version(fdt) < 0x10 ) - *p = ROUNDUP(*p, sz >= 8 ? 8 : 4); - - pname = fdt_string(fdt, noff); - if ( pname == NULL ) - { - dt_dprintk("Can't find property name in list!\n"); - break; - } - if ( strcmp(pname, "name") == 0 ) - has_name = 1; - l = strlen(pname) + 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property), - __alignof__(struct dt_property)); - if ( allnextpp ) - { - /* We accept flattened tree phandles either in - * ePAPR-style "phandle" properties, or the - * legacy "linux,phandle" properties. If both - * appear and have different values, things - * will get weird. Don't do that. */ - if ( (strcmp(pname, "phandle") == 0) || - (strcmp(pname, "linux,phandle") == 0) ) - { - if ( np->phandle == 0 ) - np->phandle = be32_to_cpup((__be32*)*p); - } - /* And we process the "ibm,phandle" property - * used in pSeries dynamic device tree - * stuff */ - if ( strcmp(pname, "ibm,phandle") == 0 ) - np->phandle = be32_to_cpup((__be32 *)*p); - pp->name = pname; - pp->length = sz; - pp->value = (void *)*p; - *prev_pp = pp; - prev_pp = &pp->next; - } - *p = ROUNDUP((*p) + sz, 4); - } - /* with version 0x10 we may not have the name property, recreate - * it here from the unit name if absent - */ - if ( !has_name ) - { - char *p1 = pathp, *ps = pathp, *pa = NULL; - int sz; - - while ( *p1 ) - { - if ( (*p1) == '@' ) - pa = p1; - if ( (*p1) == '/' ) - ps = p1 + 1; - p1++; - } - if ( pa < ps ) - pa = p1; - sz = (pa - ps) + 1; - pp = unflatten_dt_alloc(&mem, sizeof(struct dt_property) + sz, - __alignof__(struct dt_property)); - if ( allnextpp ) - { - pp->name = "name"; - pp->length = sz; - pp->value = pp + 1; - /* - * The device tree creation code assume that the property - * "name" is not a fake. - * To avoid a big divergence with Linux code, only remove - * property link. In this case we will lose a bit of memory - */ -#if 0 - *prev_pp = pp; - prev_pp = &pp->next; -#endif - np->name = pp->value; - memcpy(pp->value, ps, sz - 1); - ((char *)pp->value)[sz - 1] = 0; - dt_dprintk("fixed up name for %s -> %s\n", pathp, - (char *)pp->value); - /* Generic device initialization */ - np->dev.type = DEV_DT; - np->dev.of_node = np; - } - } - if ( allnextpp ) - { - *prev_pp = NULL; - np->name = (np->name) ? : dt_get_property(np, "name", NULL); - np->type = dt_get_property(np, "device_type", NULL); - - if ( !np->name ) - np->name = ""; - if ( !np->type ) - np->type = ""; - } - while ( tag == FDT_BEGIN_NODE || tag == FDT_NOP ) - { - if ( tag == FDT_NOP ) - *p += 4; - else - mem = unflatten_dt_node(fdt, mem, p, np, allnextpp, fpsize); - tag = be32_to_cpup((__be32 *)(*p)); - } - if ( tag != FDT_END_NODE ) - { - printk(XENLOG_WARNING "Weird tag at end of node: %x\n", tag); - return mem; - } - - *p += 4; - return mem; -} - -int unflatten_device_tree(const void *fdt, struct dt_device_node **mynodes) -{ - unsigned long start, mem, size; - struct dt_device_node **allnextp = mynodes; - - dt_dprintk(" -> unflatten_device_tree()\n"); - - dt_dprintk("Unflattening device tree:\n"); - dt_dprintk("magic: %#08x\n", fdt_magic(fdt)); - dt_dprintk("size: %#08x\n", fdt_totalsize(fdt)); - dt_dprintk("version: %#08x\n", fdt_version(fdt)); - - /* First pass, scan for size */ - start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); - size = unflatten_dt_node(fdt, 0, &start, NULL, NULL, 0); - if ( !size ) - return -EINVAL; - - size = (size | 3) + 1; - - dt_dprintk(" size is %#lx allocating...\n", size); - - /* Allocate memory for the expanded device tree */ - mem = (unsigned long)_xmalloc (size + 4, __alignof__(struct dt_device_node)); - if ( !mem ) - return -ENOMEM; - - ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeefU); - - dt_dprintk(" unflattening %lx...\n", mem); - - /* Second pass, do actual unflattening */ - start = ((unsigned long)fdt) + fdt_off_dt_struct(fdt); - unflatten_dt_node(fdt, mem, &start, NULL, &allnextp, 0); - if ( be32_to_cpup((__be32 *)start) != FDT_END ) - { - printk(XENLOG_ERR "Weird tag at end of tree: %08x\n", - *((u32 *)start)); - xfree((void *)mem); - return -EINVAL; - } - - if ( be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeefU ) - { - printk(XENLOG_ERR "End of tree marker overwritten: %08x\n", - be32_to_cpu(((__be32 *)mem)[size / 4])); - xfree((void *)mem); - return -EINVAL; - } - - *allnextp = NULL; - - dt_dprintk(" <- unflatten_device_tree()\n"); - - return 0; -} - -static void dt_alias_add(struct dt_alias_prop *ap, - struct dt_device_node *np, - int id, const char *stem, int stem_len) -{ - ap->np = np; - ap->id = id; - strlcpy(ap->stem, stem, stem_len + 1); - list_add_tail(&ap->link, &aliases_lookup); - dt_dprintk("adding DT alias:%s: stem=%s id=%d node=%s\n", - ap->alias, ap->stem, ap->id, dt_node_full_name(np)); -} - -/** - * dt_alias_scan - Scan all properties of 'aliases' node - * - * The function scans all the properties of 'aliases' node and populate - * the the global lookup table with the properties. It returns the - * number of alias_prop found, or error code in error case. - */ -static void __init dt_alias_scan(void) -{ - const struct dt_property *pp; - const struct dt_device_node *aliases; - - aliases = dt_find_node_by_path("/aliases"); - if ( !aliases ) - return; - - dt_for_each_property_node( aliases, pp ) - { - const char *start = pp->name; - const char *end = start + strlen(start); - struct dt_device_node *np; - struct dt_alias_prop *ap; - int id, len; - - /* Skip those we do not want to proceed */ - if ( !strcmp(pp->name, "name") || - !strcmp(pp->name, "phandle") || - !strcmp(pp->name, "linux,phandle") ) - continue; - - np = dt_find_node_by_path(pp->value); - if ( !np ) - continue; - - /* walk the alias backwards to extract the id and work out - * the 'stem' string */ - while ( isdigit(*(end-1)) && end > start ) - end--; - len = end - start; - - id = simple_strtoll(end, NULL, 10); - - /* Allocate an alias_prop with enough space for the stem */ - ap = _xmalloc(sizeof(*ap) + len + 1, 4); - if ( !ap ) - continue; - ap->alias = start; - dt_alias_add(ap, np, id, start, len); - } -} - -struct dt_device_node * __init -dt_find_interrupt_controller(const struct dt_device_match *matches) -{ - struct dt_device_node *np = NULL; - - while ( (np = dt_find_matching_node(np, matches)) ) - { - if ( !dt_find_property(np, "interrupt-controller", NULL) ) - continue; - - if ( dt_get_parent(np) ) - break; - } - - return np; -} - -void __init dt_unflatten_host_device_tree(void) -{ - int error = unflatten_device_tree(device_tree_flattened, &dt_host); - - if ( error ) - panic("unflatten_device_tree failed with error %d\n", error); - - dt_alias_scan(); -} - -int dt_get_pci_domain_nr(struct dt_device_node *node) -{ - u32 domain; - int error; - - error = dt_property_read_u32(node, "linux,pci-domain", &domain); - if ( !error ) - return -EINVAL; - - return (u16)domain; -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/xen/include/xen/bootfdt.h b/xen/include/xen/bootfdt.h new file mode 100644 index 0000000000..c39428d5f5 --- /dev/null +++ b/xen/include/xen/bootfdt.h @@ -0,0 +1,195 @@ +#ifndef XEN_BOOTFDT_H +#define XEN_BOOTFDT_H + +#include +#include +#include + +#define MIN_FDT_ALIGN 8 + +#define NR_MEM_BANKS 256 +#define NR_SHMEM_BANKS 32 + +#define MAX_MODULES 32 /* Current maximum useful modules */ + +typedef enum { + BOOTMOD_XEN, + BOOTMOD_FDT, + BOOTMOD_KERNEL, + BOOTMOD_RAMDISK, + BOOTMOD_XSM, + BOOTMOD_GUEST_DTB, + BOOTMOD_UNKNOWN +} bootmodule_kind; + +enum membank_type { + /* + * The MEMBANK_DEFAULT type refers to either reserved memory for the + * device/firmware (when the bank is in 'reserved_mem') or any RAM (when + * the bank is in 'mem'). + */ + MEMBANK_DEFAULT, + /* + * The MEMBANK_STATIC_DOMAIN type is used to indicate whether the memory + * bank is bound to a static Xen domain. It is only valid when the bank + * is in reserved_mem. + */ + MEMBANK_STATIC_DOMAIN, + /* + * The MEMBANK_STATIC_HEAP type is used to indicate whether the memory + * bank is reserved as static heap. It is only valid when the bank is + * in reserved_mem. + */ + MEMBANK_STATIC_HEAP, + /* + * The MEMBANK_FDT_RESVMEM type is used to indicate whether the memory + * bank is from the FDT reserve map. + */ + MEMBANK_FDT_RESVMEM, +}; + +/* Indicates the maximum number of characters(\0 included) for shm_id */ +#define MAX_SHM_ID_LENGTH 16 + +struct shmem_membank_extra { + char shm_id[MAX_SHM_ID_LENGTH]; + unsigned int nr_shm_borrowers; +}; + +struct membank { + paddr_t start; + paddr_t size; + union { + enum membank_type type; +#ifdef CONFIG_STATIC_SHM + struct shmem_membank_extra *shmem_extra; +#endif + }; +}; + +struct membanks { + __struct_group(membanks_hdr, common, , + unsigned int nr_banks; + unsigned int max_banks; + ); + struct membank bank[]; +}; + +struct meminfo { + struct membanks_hdr common; + struct membank bank[NR_MEM_BANKS]; +}; + +struct shared_meminfo { + struct membanks_hdr common; + struct membank bank[NR_SHMEM_BANKS]; + struct shmem_membank_extra extra[NR_SHMEM_BANKS]; +}; + +/* + * The domU flag is set for kernels and ramdisks of "xen,domain" nodes. + * The purpose of the domU flag is to avoid getting confused in + * kernel_probe, where we try to guess which is the dom0 kernel and + * initrd to be compatible with all versions of the multiboot spec. + */ +#define BOOTMOD_MAX_CMDLINE 1024 +struct bootmodule { + bootmodule_kind kind; + bool domU; + paddr_t start; + paddr_t size; +}; + +/* DT_MAX_NAME is the node name max length according the DT spec */ +#define DT_MAX_NAME 41 +struct bootcmdline { + bootmodule_kind kind; + bool domU; + paddr_t start; + char dt_name[DT_MAX_NAME]; + char cmdline[BOOTMOD_MAX_CMDLINE]; +}; + +struct bootmodules { + int nr_mods; + struct bootmodule module[MAX_MODULES]; +}; + +struct bootcmdlines { + unsigned int nr_mods; + struct bootcmdline cmdline[MAX_MODULES]; +}; + +struct bootinfo { + struct meminfo mem; + /* The reserved regions are only used when booting using Device-Tree */ + struct meminfo reserved_mem; + struct bootmodules modules; + struct bootcmdlines cmdlines; +#ifdef CONFIG_ACPI + struct meminfo acpi; +#endif +#ifdef CONFIG_STATIC_SHM + struct shared_meminfo shmem; +#endif + bool static_heap; +}; + +#ifdef CONFIG_ACPI +#define BOOTINFO_ACPI_INIT .acpi.common.max_banks = NR_MEM_BANKS, +#else +#define BOOTINFO_ACPI_INIT +#endif + +#ifdef CONFIG_STATIC_SHM +#define BOOTINFO_SHMEM_INIT .shmem.common.max_banks = NR_SHMEM_BANKS, +#else +#define BOOTINFO_SHMEM_INIT +#endif + +#define BOOTINFO_INIT \ +{ \ + .mem.common.max_banks = NR_MEM_BANKS, \ + .reserved_mem.common.max_banks = NR_MEM_BANKS, \ + BOOTINFO_ACPI_INIT \ + BOOTINFO_SHMEM_INIT \ +} + +extern struct bootinfo bootinfo; + +void populate_boot_allocator(void); + +size_t boot_fdt_info(const void *fdt, paddr_t paddr); + +const char *boot_fdt_cmdline(const void *fdt); + +static inline struct membanks *bootinfo_get_reserved_mem(void) +{ + return container_of(&bootinfo.reserved_mem.common, struct membanks, common); +} + +static inline struct membanks *bootinfo_get_mem(void) +{ + return container_of(&bootinfo.mem.common, struct membanks, common); +} + +#ifdef CONFIG_ACPI +static inline struct membanks *bootinfo_get_acpi(void) +{ + return container_of(&bootinfo.acpi.common, struct membanks, common); +} +#endif + +#ifdef CONFIG_STATIC_SHM +static inline struct membanks *bootinfo_get_shmem(void) +{ + return container_of(&bootinfo.shmem.common, struct membanks, common); +} + +static inline struct shmem_membank_extra *bootinfo_get_shmem_extra(void) +{ + return bootinfo.shmem.extra; +} +#endif + +#endif /* XEN_BOOTFDT_H */ From patchwork Wed Jul 24 15:31:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741098 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2D60EC3DA63 for ; Wed, 24 Jul 2024 15:32:02 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764291.1174727 (Exim 4.92) (envelope-from ) id 1sWdy4-00082A-Ld; Wed, 24 Jul 2024 15:31:52 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764291.1174727; Wed, 24 Jul 2024 15:31:52 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy4-00081t-Hw; Wed, 24 Jul 2024 15:31:52 +0000 Received: by outflank-mailman (input) for mailman id 764291; Wed, 24 Jul 2024 15:31:50 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy2-0005Od-K7 for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:50 +0000 Received: from mail-lj1-x22d.google.com (mail-lj1-x22d.google.com [2a00:1450:4864:20::22d]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id d7411ca8-49d1-11ef-8776-851b0ebba9a2; Wed, 24 Jul 2024 17:31:48 +0200 (CEST) Received: by mail-lj1-x22d.google.com with SMTP id 38308e7fff4ca-2ef2d96164aso34909601fa.3 for ; Wed, 24 Jul 2024 08:31:48 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:46 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d7411ca8-49d1-11ef-8776-851b0ebba9a2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835107; x=1722439907; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=j+g7AFrErQBUChRVsS1091nTh6+2szALNipR3Qr/jkI=; b=gDcrlGmrzuyHFvlO3pK5FAMaQGZXT1vz/Z1ei4hswvZlffG+8/rhDZKC4m0YzyuJni 5KYtIZm+2tfm0XQU8rXkZ+FFMrmEy3EP6QRwN7jNU5xcWto7MDTX4wKVtQSbOZF1rP49 HFhRRQwTNntjpufRLnClY0BBs/g4aTF/hBnrZV9JA6FCAsBht9EpRakaUjdcMSxTife9 /aTXiycaiO6TQwxhPRV0n7Hlp+/Q+9RKde2ceeUkjN28IPbJMJj5OvI1hm+ak3c3tnCa EupZCdZ2G9DdyX8gNLqmBIVeJO77R5GhVSPMJ1kmOx8DQHByV88KpqCbItuZDXntEyaw 3SrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835107; x=1722439907; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=j+g7AFrErQBUChRVsS1091nTh6+2szALNipR3Qr/jkI=; b=i7JKviX9yWggVyaVu9PlFRchHWcBY62FCFrao0nTK0W7wyVOLccOGl6xmMA2PSUDbr pvMiyHavr2I06WOj+bq7pcOBDjmL3MaBpGgasXb31nNS7Y8Y3Page2aSXDgw1pHheiQ0 6uBiYNe2CS9Hr1kYpXWDY5/rCWz78da1tmgjQ0B7k79j3aLcGkj+MZ+EEPAHPXbpdeiW mbZF5BZtNmptwrDJByOaXE7+QTQW46Zwj+i0nGcHnLfzhSEH6W2ttHFhaIIa/cfEpyqp fkFFFEsB7iz1w3UEKHq8I+ZRYC5ZCMdKzIBv3Qb20AFa+1Q3XIPLP2En81Rh20T42QHr 1q4g== X-Gm-Message-State: AOJu0YzggYqivdk1vWmAlISRTyMHFImfi7+GJhsle8t50dSroTRCoWJO BKNbcOJoy2tpaK2EpGVU3KVY9kpHbj+X4QXAc/Wp5aXdHJVgyxCNOd2sG+U+ X-Google-Smtp-Source: AGHT+IE/KIxrEqf9xFYS3i280KwwlmR0jeeBEOZDciJj5UgYbYzhc3CbNvJqtR+0RrLe9WyGZcrtng== X-Received: by 2002:a05:651c:d0:b0:2ee:7a3e:4721 with SMTP id 38308e7fff4ca-2f039d56b7dmr1366041fa.39.1721835106818; Wed, 24 Jul 2024 08:31:46 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Shawn Anastasio , Stefano Stabellini , Julien Grall , Bertrand Marquis , Michal Orzel , Volodymyr Babchuk , "Daniel P. Smith" , Oleksii Kurochko Subject: [PATCH v7 2/9] xen/common: Move Arm's bootfdt.c to common Date: Wed, 24 Jul 2024 17:31:33 +0200 Message-ID: <35558886445c39c0f570632d355b42bb764dbdc4.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 From: Shawn Anastasio Move Arm's bootfdt.c to xen/common so that it can be used by other device tree architectures like PPC and RISCV. Suggested-by: Julien Grall Signed-off-by: Shawn Anastasio Acked-by: Julien Grall Signed-off-by: Oleksii Kurochko --- Changes in V7: - Nothing changed. Only rebase. --- Changes in V6: - update the version of the patch to v6. --- Changes in V5: - add xen/include/xen/bootfdt.h to MAINTAINERS file. - drop message "Early device tree parsing and". - After rebase on top of the current staging the following changes were done: - init bootinfo variable in with BOOTINFO_INIT; - update the code of dt_unreserved_regions(): CONFIG_STATIC_SHM related changes and getting of reserved_mem bootinfo_get_shmem() ?? - update the code of meminfo_overlap_check(): add check ( INVALID_PADDR == bank_start ) to if case. - update the code of check_reserved_regions_overlap(): CONFIG_STATIC_SHM related changes. - struct bootinfo was updated ( CONFIG_STATIC_SHM changes ) - add shared_meminfo ( because of CONFIG_STATIC_SHM ) - struct struct membanks was update with __struct group so is neeeded to be included in bootfdt.h - move BOOTINFO_ACPI_INIT, BOOTINFO_SHMEM_INIT, BOOTINFO_INIT to generic bootfdt.h - bootinfo_get_reserved_mem(), bootinfo_get_mem(), bootinfo_get_acpi(), bootinfo_get_shmem() and bootinfo_get_shmem_extra() were moved to xen/bootfdt.h - s/arm32/CONFIG_SEPARATE_XENHEAP/ - add inclusion of because there are function in which are using container_of(). --- Changes in v4: - create new xen/include/bootinfo.h rather than relying on arch's asm/setup.h to provide required definitions for bootinfo.c - build bootinfo.c as .init.o - clean up and sort bootinfo.c's #includes - use CONFIG_SEPARATE_XENHEAP rather than CONFIG_ARM_32 to guard xenheap-specific behavior of populate_boot_allocator - (MAINTAINERS) include all of common/device-tree rather than just bootinfo.c --- xen/arch/arm/Makefile | 1 - xen/arch/arm/bootfdt.c | 622 ------------------------------ xen/arch/arm/include/asm/setup.h | 13 - xen/common/device-tree/Makefile | 1 + xen/common/device-tree/bootfdt.c | 635 +++++++++++++++++++++++++++++++ xen/include/xen/bootfdt.h | 14 + 6 files changed, 650 insertions(+), 636 deletions(-) delete mode 100644 xen/arch/arm/bootfdt.c create mode 100644 xen/common/device-tree/bootfdt.c diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 45dc29ea53..da9c979dc4 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_HAS_VPCI) += vpci.o obj-$(CONFIG_HAS_ALTERNATIVE) += alternative.o -obj-y += bootfdt.init.o obj-y += cpuerrata.o obj-y += cpufeature.o obj-y += decode.o diff --git a/xen/arch/arm/bootfdt.c b/xen/arch/arm/bootfdt.c deleted file mode 100644 index 6e060111d9..0000000000 --- a/xen/arch/arm/bootfdt.c +++ /dev/null @@ -1,622 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Early Device Tree - * - * Copyright (C) 2012-2014 Citrix Systems, Inc. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void __init __maybe_unused build_assertions(void) -{ - /* - * Check that no padding is between struct membanks "bank" flexible array - * member and struct meminfo "bank" member - */ - BUILD_BUG_ON((offsetof(struct membanks, bank) != - offsetof(struct meminfo, bank))); - /* Ensure "struct membanks" is 8-byte aligned */ - BUILD_BUG_ON(alignof(struct membanks) != 8); -} - -static bool __init device_tree_node_is_available(const void *fdt, int node) -{ - const char *status; - int len; - - status = fdt_getprop(fdt, node, "status", &len); - if ( !status ) - return true; - - if ( len > 0 ) - { - if ( !strcmp(status, "ok") || !strcmp(status, "okay") ) - return true; - } - - return false; -} - -static bool __init device_tree_node_matches(const void *fdt, int node, - const char *match) -{ - const char *name; - size_t match_len; - - name = fdt_get_name(fdt, node, NULL); - match_len = strlen(match); - - /* Match both "match" and "match@..." patterns but not - "match-foo". */ - return strncmp(name, match, match_len) == 0 - && (name[match_len] == '@' || name[match_len] == '\0'); -} - -static bool __init device_tree_node_compatible(const void *fdt, int node, - const char *match) -{ - int len, l; - const void *prop; - - prop = fdt_getprop(fdt, node, "compatible", &len); - if ( prop == NULL ) - return false; - - while ( len > 0 ) { - if ( !dt_compat_cmp(prop, match) ) - return true; - l = strlen(prop) + 1; - prop += l; - len -= l; - } - - return false; -} - -void __init device_tree_get_reg(const __be32 **cell, uint32_t address_cells, - uint32_t size_cells, paddr_t *start, - paddr_t *size) -{ - uint64_t dt_start, dt_size; - - /* - * dt_next_cell will return uint64_t whereas paddr_t may not be 64-bit. - * Thus, there is an implicit cast from uint64_t to paddr_t. - */ - dt_start = dt_next_cell(address_cells, cell); - dt_size = dt_next_cell(size_cells, cell); - - if ( dt_start != (paddr_t)dt_start ) - { - printk("Physical address greater than max width supported\n"); - WARN(); - } - - if ( dt_size != (paddr_t)dt_size ) - { - printk("Physical size greater than max width supported\n"); - WARN(); - } - - /* - * Xen will truncate the address/size if it is greater than the maximum - * supported width and it will give an appropriate warning. - */ - *start = dt_start; - *size = dt_size; -} - -static int __init device_tree_get_meminfo(const void *fdt, int node, - const char *prop_name, - u32 address_cells, u32 size_cells, - struct membanks *mem, - enum membank_type type) -{ - const struct fdt_property *prop; - unsigned int i, banks; - const __be32 *cell; - u32 reg_cells = address_cells + size_cells; - paddr_t start, size; - - if ( !device_tree_node_is_available(fdt, node) ) - return 0; - - if ( address_cells < 1 || size_cells < 1 ) - { - printk("fdt: property `%s': invalid #address-cells or #size-cells", - prop_name); - return -EINVAL; - } - - prop = fdt_get_property(fdt, node, prop_name, NULL); - if ( !prop ) - return -ENOENT; - - cell = (const __be32 *)prop->data; - banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32)); - - for ( i = 0; i < banks && mem->nr_banks < mem->max_banks; i++ ) - { - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - if ( mem == bootinfo_get_reserved_mem() && - check_reserved_regions_overlap(start, size) ) - return -EINVAL; - /* Some DT may describe empty bank, ignore them */ - if ( !size ) - continue; - mem->bank[mem->nr_banks].start = start; - mem->bank[mem->nr_banks].size = size; - mem->bank[mem->nr_banks].type = type; - mem->nr_banks++; - } - - if ( i < banks ) - { - printk("Warning: Max number of supported memory regions reached.\n"); - return -ENOSPC; - } - - return 0; -} - -u32 __init device_tree_get_u32(const void *fdt, int node, - const char *prop_name, u32 dflt) -{ - const struct fdt_property *prop; - - prop = fdt_get_property(fdt, node, prop_name, NULL); - if ( !prop || prop->len < sizeof(u32) ) - return dflt; - - return fdt32_to_cpu(*(uint32_t*)prop->data); -} - -/** - * device_tree_for_each_node - iterate over all device tree sub-nodes - * @fdt: flat device tree. - * @node: parent node to start the search from - * @func: function to call for each sub-node. - * @data: data to pass to @func. - * - * Any nodes nested at DEVICE_TREE_MAX_DEPTH or deeper are ignored. - * - * Returns 0 if all nodes were iterated over successfully. If @func - * returns a value different from 0, that value is returned immediately. - */ -int __init device_tree_for_each_node(const void *fdt, int node, - device_tree_node_func func, - void *data) -{ - /* - * We only care about relative depth increments, assume depth of - * node is 0 for simplicity. - */ - int depth = 0; - const int first_node = node; - u32 address_cells[DEVICE_TREE_MAX_DEPTH]; - u32 size_cells[DEVICE_TREE_MAX_DEPTH]; - int ret; - - do { - const char *name = fdt_get_name(fdt, node, NULL); - u32 as, ss; - - if ( depth >= DEVICE_TREE_MAX_DEPTH ) - { - printk("Warning: device tree node `%s' is nested too deep\n", - name); - continue; - } - - as = depth > 0 ? address_cells[depth-1] : DT_ROOT_NODE_ADDR_CELLS_DEFAULT; - ss = depth > 0 ? size_cells[depth-1] : DT_ROOT_NODE_SIZE_CELLS_DEFAULT; - - address_cells[depth] = device_tree_get_u32(fdt, node, - "#address-cells", as); - size_cells[depth] = device_tree_get_u32(fdt, node, - "#size-cells", ss); - - /* skip the first node */ - if ( node != first_node ) - { - ret = func(fdt, node, name, depth, as, ss, data); - if ( ret != 0 ) - return ret; - } - - node = fdt_next_node(fdt, node, &depth); - } while ( node >= 0 && depth > 0 ); - - return 0; -} - -static int __init process_memory_node(const void *fdt, int node, - const char *name, int depth, - u32 address_cells, u32 size_cells, - struct membanks *mem) -{ - return device_tree_get_meminfo(fdt, node, "reg", address_cells, size_cells, - mem, MEMBANK_DEFAULT); -} - -static int __init process_reserved_memory_node(const void *fdt, int node, - const char *name, int depth, - u32 address_cells, - u32 size_cells, - void *data) -{ - int rc = process_memory_node(fdt, node, name, depth, address_cells, - size_cells, data); - - if ( rc == -ENOSPC ) - panic("Max number of supported reserved-memory regions reached.\n"); - else if ( rc != -ENOENT ) - return rc; - return 0; -} - -static int __init process_reserved_memory(const void *fdt, int node, - const char *name, int depth, - u32 address_cells, u32 size_cells) -{ - return device_tree_for_each_node(fdt, node, - process_reserved_memory_node, - bootinfo_get_reserved_mem()); -} - -static void __init process_multiboot_node(const void *fdt, int node, - const char *name, - u32 address_cells, u32 size_cells) -{ - static int __initdata kind_guess = 0; - const struct fdt_property *prop; - const __be32 *cell; - bootmodule_kind kind; - paddr_t start, size; - int len; - /* sizeof("/chosen/") + DT_MAX_NAME + '/' + DT_MAX_NAME + '/0' => 92 */ - char path[92]; - int parent_node, ret; - bool domU; - - parent_node = fdt_parent_offset(fdt, node); - ASSERT(parent_node >= 0); - - /* Check that the node is under "/chosen" (first 7 chars of path) */ - ret = fdt_get_path(fdt, node, path, sizeof (path)); - if ( ret != 0 || strncmp(path, "/chosen", 7) ) - return; - - prop = fdt_get_property(fdt, node, "reg", &len); - if ( !prop ) - panic("node %s missing `reg' property\n", name); - - if ( len < dt_cells_to_size(address_cells + size_cells) ) - panic("fdt: node `%s': `reg` property length is too short\n", - name); - - cell = (const __be32 *)prop->data; - device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); - - if ( fdt_node_check_compatible(fdt, node, "xen,linux-zimage") == 0 || - fdt_node_check_compatible(fdt, node, "multiboot,kernel") == 0 ) - kind = BOOTMOD_KERNEL; - else if ( fdt_node_check_compatible(fdt, node, "xen,linux-initrd") == 0 || - fdt_node_check_compatible(fdt, node, "multiboot,ramdisk") == 0 ) - kind = BOOTMOD_RAMDISK; - else if ( fdt_node_check_compatible(fdt, node, "xen,xsm-policy") == 0 ) - kind = BOOTMOD_XSM; - else if ( fdt_node_check_compatible(fdt, node, "multiboot,device-tree") == 0 ) - kind = BOOTMOD_GUEST_DTB; - else - kind = BOOTMOD_UNKNOWN; - - /** - * Guess the kind of these first two unknowns respectively: - * (1) The first unknown must be kernel. - * (2) Detect the XSM Magic from the 2nd unknown: - * a. If it's XSM, set the kind as XSM, and that also means we - * won't load ramdisk; - * b. if it's not XSM, set the kind as ramdisk. - * So if user want to load ramdisk, it must be the 2nd unknown. - * We also detect the XSM Magic for the following unknowns, - * then set its kind according to the return value of has_xsm_magic. - */ - if ( kind == BOOTMOD_UNKNOWN ) - { - switch ( kind_guess++ ) - { - case 0: kind = BOOTMOD_KERNEL; break; - case 1: kind = BOOTMOD_RAMDISK; break; - default: break; - } - if ( kind_guess > 1 && has_xsm_magic(start) ) - kind = BOOTMOD_XSM; - } - - domU = fdt_node_check_compatible(fdt, parent_node, "xen,domain") == 0; - add_boot_module(kind, start, size, domU); - - prop = fdt_get_property(fdt, node, "bootargs", &len); - if ( !prop ) - return; - add_boot_cmdline(fdt_get_name(fdt, parent_node, &len), prop->data, - kind, start, domU); -} - -static int __init process_chosen_node(const void *fdt, int node, - const char *name, - u32 address_cells, u32 size_cells) -{ - const struct fdt_property *prop; - paddr_t start, end; - int len; - - if ( fdt_get_property(fdt, node, "xen,static-heap", NULL) ) - { - int rc; - - printk("Checking for static heap in /chosen\n"); - - rc = device_tree_get_meminfo(fdt, node, "xen,static-heap", - address_cells, size_cells, - bootinfo_get_reserved_mem(), - MEMBANK_STATIC_HEAP); - if ( rc ) - return rc; - - bootinfo.static_heap = true; - } - - printk("Checking for initrd in /chosen\n"); - - prop = fdt_get_property(fdt, node, "linux,initrd-start", &len); - if ( !prop ) - /* No initrd present. */ - return 0; - if ( len != sizeof(u32) && len != sizeof(u64) ) - { - printk("linux,initrd-start property has invalid length %d\n", len); - return -EINVAL; - } - start = dt_read_paddr((const void *)&prop->data, dt_size_to_cells(len)); - - prop = fdt_get_property(fdt, node, "linux,initrd-end", &len); - if ( !prop ) - { - printk("linux,initrd-end not present but -start was\n"); - return -EINVAL; - } - if ( len != sizeof(u32) && len != sizeof(u64) ) - { - printk("linux,initrd-end property has invalid length %d\n", len); - return -EINVAL; - } - end = dt_read_paddr((const void *)&prop->data, dt_size_to_cells(len)); - - if ( start >= end ) - { - printk("linux,initrd limits invalid: %"PRIpaddr" >= %"PRIpaddr"\n", - start, end); - return -EINVAL; - } - - printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end); - - add_boot_module(BOOTMOD_RAMDISK, start, end-start, false); - - return 0; -} - -static int __init process_domain_node(const void *fdt, int node, - const char *name, - u32 address_cells, u32 size_cells) -{ - const struct fdt_property *prop; - - printk("Checking for \"xen,static-mem\" in domain node\n"); - - prop = fdt_get_property(fdt, node, "xen,static-mem", NULL); - if ( !prop ) - /* No "xen,static-mem" present. */ - return 0; - - return device_tree_get_meminfo(fdt, node, "xen,static-mem", address_cells, - size_cells, bootinfo_get_reserved_mem(), - MEMBANK_STATIC_DOMAIN); -} - -static int __init early_scan_node(const void *fdt, - int node, const char *name, int depth, - u32 address_cells, u32 size_cells, - void *data) -{ - int rc = 0; - - /* - * If Xen has been booted via UEFI, the memory banks are - * populated. So we should skip the parsing. - */ - if ( !efi_enabled(EFI_BOOT) && - device_tree_node_matches(fdt, node, "memory") ) - rc = process_memory_node(fdt, node, name, depth, - address_cells, size_cells, bootinfo_get_mem()); - else if ( depth == 1 && !dt_node_cmp(name, "reserved-memory") ) - rc = process_reserved_memory(fdt, node, name, depth, - address_cells, size_cells); - else if ( depth <= 3 && (device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) || - device_tree_node_compatible(fdt, node, "multiboot,module" ))) - process_multiboot_node(fdt, node, name, address_cells, size_cells); - else if ( depth == 1 && device_tree_node_matches(fdt, node, "chosen") ) - rc = process_chosen_node(fdt, node, name, address_cells, size_cells); - else if ( depth == 2 && device_tree_node_compatible(fdt, node, "xen,domain") ) - rc = process_domain_node(fdt, node, name, address_cells, size_cells); - else if ( depth <= 3 && device_tree_node_compatible(fdt, node, "xen,domain-shared-memory-v1") ) - rc = process_shm_node(fdt, node, address_cells, size_cells); - - if ( rc < 0 ) - printk("fdt: node `%s': parsing failed\n", name); - return rc; -} - -static void __init early_print_info(void) -{ - const struct membanks *mi = bootinfo_get_mem(); - const struct membanks *mem_resv = bootinfo_get_reserved_mem(); - struct bootmodules *mods = &bootinfo.modules; - struct bootcmdlines *cmds = &bootinfo.cmdlines; - unsigned int i; - - for ( i = 0; i < mi->nr_banks; i++ ) - printk("RAM: %"PRIpaddr" - %"PRIpaddr"\n", - mi->bank[i].start, - mi->bank[i].start + mi->bank[i].size - 1); - printk("\n"); - for ( i = 0 ; i < mods->nr_mods; i++ ) - printk("MODULE[%d]: %"PRIpaddr" - %"PRIpaddr" %-12s\n", - i, - mods->module[i].start, - mods->module[i].start + mods->module[i].size, - boot_module_kind_as_string(mods->module[i].kind)); - - for ( i = 0; i < mem_resv->nr_banks; i++ ) - { - printk(" RESVD[%u]: %"PRIpaddr" - %"PRIpaddr"\n", i, - mem_resv->bank[i].start, - mem_resv->bank[i].start + mem_resv->bank[i].size - 1); - } - early_print_info_shmem(); - printk("\n"); - for ( i = 0 ; i < cmds->nr_mods; i++ ) - printk("CMDLINE[%"PRIpaddr"]:%s %s\n", cmds->cmdline[i].start, - cmds->cmdline[i].dt_name, - &cmds->cmdline[i].cmdline[0]); - printk("\n"); -} - -/* This function assumes that memory regions are not overlapped */ -static int __init cmp_memory_node(const void *key, const void *elem) -{ - const struct membank *handler0 = key; - const struct membank *handler1 = elem; - - if ( handler0->start < handler1->start ) - return -1; - - if ( handler0->start >= (handler1->start + handler1->size) ) - return 1; - - return 0; -} - -static void __init swap_memory_node(void *_a, void *_b, size_t size) -{ - struct membank *a = _a, *b = _b; - - SWAP(*a, *b); -} - -/** - * boot_fdt_info - initialize bootinfo from a DTB - * @fdt: flattened device tree binary - * - * Returns the size of the DTB. - */ -size_t __init boot_fdt_info(const void *fdt, paddr_t paddr) -{ - struct membanks *reserved_mem = bootinfo_get_reserved_mem(); - struct membanks *mem = bootinfo_get_mem(); - unsigned int i; - int nr_rsvd; - int ret; - - ret = fdt_check_header(fdt); - if ( ret < 0 ) - panic("No valid device tree\n"); - - add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false); - - nr_rsvd = fdt_num_mem_rsv(fdt); - if ( nr_rsvd < 0 ) - panic("Parsing FDT memory reserve map failed (%d)\n", nr_rsvd); - - for ( i = 0; i < nr_rsvd; i++ ) - { - struct membank *bank; - paddr_t s, sz; - - if ( fdt_get_mem_rsv_paddr(device_tree_flattened, i, &s, &sz) < 0 ) - continue; - - if ( reserved_mem->nr_banks < reserved_mem->max_banks ) - { - bank = &reserved_mem->bank[reserved_mem->nr_banks]; - bank->start = s; - bank->size = sz; - bank->type = MEMBANK_FDT_RESVMEM; - reserved_mem->nr_banks++; - } - else - panic("Cannot allocate reserved memory bank\n"); - } - - ret = device_tree_for_each_node(fdt, 0, early_scan_node, NULL); - if ( ret ) - panic("Early FDT parsing failed (%d)\n", ret); - - /* - * On Arm64 setup_directmap_mappings() expects to be called with the lowest - * bank in memory first. There is no requirement that the DT will provide - * the banks sorted in ascending order. So sort them through. - */ - sort(mem->bank, mem->nr_banks, sizeof(struct membank), - cmp_memory_node, swap_memory_node); - - early_print_info(); - - return fdt_totalsize(fdt); -} - -const __init char *boot_fdt_cmdline(const void *fdt) -{ - int node; - const struct fdt_property *prop; - - node = fdt_path_offset(fdt, "/chosen"); - if ( node < 0 ) - return NULL; - - prop = fdt_get_property(fdt, node, "xen,xen-bootargs", NULL); - if ( prop == NULL ) - { - struct bootcmdline *dom0_cmdline = - boot_cmdline_find_by_kind(BOOTMOD_KERNEL); - - if (fdt_get_property(fdt, node, "xen,dom0-bootargs", NULL) || - ( dom0_cmdline && dom0_cmdline->cmdline[0] ) ) - prop = fdt_get_property(fdt, node, "bootargs", NULL); - } - if ( prop == NULL ) - return NULL; - - return prop->data; -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index 1748be29e5..64c227d171 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -41,19 +41,6 @@ void fw_unreserved_regions(paddr_t s, paddr_t e, void (*cb)(paddr_t ps, paddr_t pe), unsigned int first); -bool check_reserved_regions_overlap(paddr_t region_start, paddr_t region_size); - -struct bootmodule *add_boot_module(bootmodule_kind kind, - paddr_t start, paddr_t size, bool domU); -struct bootmodule *boot_module_find_by_kind(bootmodule_kind kind); -struct bootmodule * boot_module_find_by_addr_and_kind(bootmodule_kind kind, - paddr_t start); -void add_boot_cmdline(const char *name, const char *cmdline, - bootmodule_kind kind, paddr_t start, bool domU); -struct bootcmdline *boot_cmdline_find_by_kind(bootmodule_kind kind); -struct bootcmdline * boot_cmdline_find_by_name(const char *name); -const char *boot_module_kind_as_string(bootmodule_kind kind); - void init_pdx(void); void setup_mm(void); diff --git a/xen/common/device-tree/Makefile b/xen/common/device-tree/Makefile index da892dd55d..22a053f184 100644 --- a/xen/common/device-tree/Makefile +++ b/xen/common/device-tree/Makefile @@ -1,2 +1,3 @@ +obj-y += bootfdt.init.o obj-y += bootinfo.init.o obj-y += device_tree.o diff --git a/xen/common/device-tree/bootfdt.c b/xen/common/device-tree/bootfdt.c new file mode 100644 index 0000000000..748b5f7c69 --- /dev/null +++ b/xen/common/device-tree/bootfdt.c @@ -0,0 +1,635 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Early Device Tree + * + * Copyright (C) 2012-2014 Citrix Systems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_STATIC_SHM +#include +#endif + +static void __init __maybe_unused build_assertions(void) +{ + /* + * Check that no padding is between struct membanks "bank" flexible array + * member and struct meminfo "bank" member + */ + BUILD_BUG_ON((offsetof(struct membanks, bank) != + offsetof(struct meminfo, bank))); + /* Ensure "struct membanks" is 8-byte aligned */ + BUILD_BUG_ON(alignof(struct membanks) != 8); +} + +static bool __init device_tree_node_is_available(const void *fdt, int node) +{ + const char *status; + int len; + + status = fdt_getprop(fdt, node, "status", &len); + if ( !status ) + return true; + + if ( len > 0 ) + { + if ( !strcmp(status, "ok") || !strcmp(status, "okay") ) + return true; + } + + return false; +} + +static bool __init device_tree_node_matches(const void *fdt, int node, + const char *match) +{ + const char *name; + size_t match_len; + + name = fdt_get_name(fdt, node, NULL); + match_len = strlen(match); + + /* Match both "match" and "match@..." patterns but not + "match-foo". */ + return strncmp(name, match, match_len) == 0 + && (name[match_len] == '@' || name[match_len] == '\0'); +} + +static bool __init device_tree_node_compatible(const void *fdt, int node, + const char *match) +{ + int len, l; + const void *prop; + + prop = fdt_getprop(fdt, node, "compatible", &len); + if ( prop == NULL ) + return false; + + while ( len > 0 ) { + if ( !dt_compat_cmp(prop, match) ) + return true; + l = strlen(prop) + 1; + prop += l; + len -= l; + } + + return false; +} + +void __init device_tree_get_reg(const __be32 **cell, uint32_t address_cells, + uint32_t size_cells, paddr_t *start, + paddr_t *size) +{ + uint64_t dt_start, dt_size; + + /* + * dt_next_cell will return uint64_t whereas paddr_t may not be 64-bit. + * Thus, there is an implicit cast from uint64_t to paddr_t. + */ + dt_start = dt_next_cell(address_cells, cell); + dt_size = dt_next_cell(size_cells, cell); + + if ( dt_start != (paddr_t)dt_start ) + { + printk("Physical address greater than max width supported\n"); + WARN(); + } + + if ( dt_size != (paddr_t)dt_size ) + { + printk("Physical size greater than max width supported\n"); + WARN(); + } + + /* + * Xen will truncate the address/size if it is greater than the maximum + * supported width and it will give an appropriate warning. + */ + *start = dt_start; + *size = dt_size; +} + +static int __init device_tree_get_meminfo(const void *fdt, int node, + const char *prop_name, + u32 address_cells, u32 size_cells, + struct membanks *mem, + enum membank_type type) +{ + const struct fdt_property *prop; + unsigned int i, banks; + const __be32 *cell; + u32 reg_cells = address_cells + size_cells; + paddr_t start, size; + + if ( !device_tree_node_is_available(fdt, node) ) + return 0; + + if ( address_cells < 1 || size_cells < 1 ) + { + printk("fdt: property `%s': invalid #address-cells or #size-cells", + prop_name); + return -EINVAL; + } + + prop = fdt_get_property(fdt, node, prop_name, NULL); + if ( !prop ) + return -ENOENT; + + cell = (const __be32 *)prop->data; + banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32)); + + for ( i = 0; i < banks && mem->nr_banks < mem->max_banks; i++ ) + { + device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); + if ( mem == bootinfo_get_reserved_mem() && + check_reserved_regions_overlap(start, size) ) + return -EINVAL; + /* Some DT may describe empty bank, ignore them */ + if ( !size ) + continue; + mem->bank[mem->nr_banks].start = start; + mem->bank[mem->nr_banks].size = size; + mem->bank[mem->nr_banks].type = type; + mem->nr_banks++; + } + + if ( i < banks ) + { + printk("Warning: Max number of supported memory regions reached.\n"); + return -ENOSPC; + } + + return 0; +} + +u32 __init device_tree_get_u32(const void *fdt, int node, + const char *prop_name, u32 dflt) +{ + const struct fdt_property *prop; + + prop = fdt_get_property(fdt, node, prop_name, NULL); + if ( !prop || prop->len < sizeof(u32) ) + return dflt; + + return fdt32_to_cpu(*(uint32_t*)prop->data); +} + +/** + * device_tree_for_each_node - iterate over all device tree sub-nodes + * @fdt: flat device tree. + * @node: parent node to start the search from + * @func: function to call for each sub-node. + * @data: data to pass to @func. + * + * Any nodes nested at DEVICE_TREE_MAX_DEPTH or deeper are ignored. + * + * Returns 0 if all nodes were iterated over successfully. If @func + * returns a value different from 0, that value is returned immediately. + */ +int __init device_tree_for_each_node(const void *fdt, int node, + device_tree_node_func func, + void *data) +{ + /* + * We only care about relative depth increments, assume depth of + * node is 0 for simplicity. + */ + int depth = 0; + const int first_node = node; + u32 address_cells[DEVICE_TREE_MAX_DEPTH]; + u32 size_cells[DEVICE_TREE_MAX_DEPTH]; + int ret; + + do { + const char *name = fdt_get_name(fdt, node, NULL); + u32 as, ss; + + if ( depth >= DEVICE_TREE_MAX_DEPTH ) + { + printk("Warning: device tree node `%s' is nested too deep\n", + name); + continue; + } + + as = depth > 0 ? address_cells[depth-1] : DT_ROOT_NODE_ADDR_CELLS_DEFAULT; + ss = depth > 0 ? size_cells[depth-1] : DT_ROOT_NODE_SIZE_CELLS_DEFAULT; + + address_cells[depth] = device_tree_get_u32(fdt, node, + "#address-cells", as); + size_cells[depth] = device_tree_get_u32(fdt, node, + "#size-cells", ss); + + /* skip the first node */ + if ( node != first_node ) + { + ret = func(fdt, node, name, depth, as, ss, data); + if ( ret != 0 ) + return ret; + } + + node = fdt_next_node(fdt, node, &depth); + } while ( node >= 0 && depth > 0 ); + + return 0; +} + +static int __init process_memory_node(const void *fdt, int node, + const char *name, int depth, + u32 address_cells, u32 size_cells, + struct membanks *mem) +{ + return device_tree_get_meminfo(fdt, node, "reg", address_cells, size_cells, + mem, MEMBANK_DEFAULT); +} + +static int __init process_reserved_memory_node(const void *fdt, int node, + const char *name, int depth, + u32 address_cells, + u32 size_cells, + void *data) +{ + int rc = process_memory_node(fdt, node, name, depth, address_cells, + size_cells, data); + + if ( rc == -ENOSPC ) + panic("Max number of supported reserved-memory regions reached.\n"); + else if ( rc != -ENOENT ) + return rc; + return 0; +} + +static int __init process_reserved_memory(const void *fdt, int node, + const char *name, int depth, + u32 address_cells, u32 size_cells) +{ + return device_tree_for_each_node(fdt, node, + process_reserved_memory_node, + bootinfo_get_reserved_mem()); +} + +static void __init process_multiboot_node(const void *fdt, int node, + const char *name, + u32 address_cells, u32 size_cells) +{ + static int __initdata kind_guess = 0; + const struct fdt_property *prop; + const __be32 *cell; + bootmodule_kind kind; + paddr_t start, size; + int len; + /* sizeof("/chosen/") + DT_MAX_NAME + '/' + DT_MAX_NAME + '/0' => 92 */ + char path[92]; + int parent_node, ret; + bool domU; + + parent_node = fdt_parent_offset(fdt, node); + ASSERT(parent_node >= 0); + + /* Check that the node is under "/chosen" (first 7 chars of path) */ + ret = fdt_get_path(fdt, node, path, sizeof (path)); + if ( ret != 0 || strncmp(path, "/chosen", 7) ) + return; + + prop = fdt_get_property(fdt, node, "reg", &len); + if ( !prop ) + panic("node %s missing `reg' property\n", name); + + if ( len < dt_cells_to_size(address_cells + size_cells) ) + panic("fdt: node `%s': `reg` property length is too short\n", + name); + + cell = (const __be32 *)prop->data; + device_tree_get_reg(&cell, address_cells, size_cells, &start, &size); + + if ( fdt_node_check_compatible(fdt, node, "xen,linux-zimage") == 0 || + fdt_node_check_compatible(fdt, node, "multiboot,kernel") == 0 ) + kind = BOOTMOD_KERNEL; + else if ( fdt_node_check_compatible(fdt, node, "xen,linux-initrd") == 0 || + fdt_node_check_compatible(fdt, node, "multiboot,ramdisk") == 0 ) + kind = BOOTMOD_RAMDISK; + else if ( fdt_node_check_compatible(fdt, node, "xen,xsm-policy") == 0 ) + kind = BOOTMOD_XSM; + else if ( fdt_node_check_compatible(fdt, node, "multiboot,device-tree") == 0 ) + kind = BOOTMOD_GUEST_DTB; + else + kind = BOOTMOD_UNKNOWN; + + /** + * Guess the kind of these first two unknowns respectively: + * (1) The first unknown must be kernel. + * (2) Detect the XSM Magic from the 2nd unknown: + * a. If it's XSM, set the kind as XSM, and that also means we + * won't load ramdisk; + * b. if it's not XSM, set the kind as ramdisk. + * So if user want to load ramdisk, it must be the 2nd unknown. + * We also detect the XSM Magic for the following unknowns, + * then set its kind according to the return value of has_xsm_magic. + */ + if ( kind == BOOTMOD_UNKNOWN ) + { + switch ( kind_guess++ ) + { + case 0: kind = BOOTMOD_KERNEL; break; + case 1: kind = BOOTMOD_RAMDISK; break; + default: break; + } + if ( kind_guess > 1 && has_xsm_magic(start) ) + kind = BOOTMOD_XSM; + } + + domU = fdt_node_check_compatible(fdt, parent_node, "xen,domain") == 0; + add_boot_module(kind, start, size, domU); + + prop = fdt_get_property(fdt, node, "bootargs", &len); + if ( !prop ) + return; + add_boot_cmdline(fdt_get_name(fdt, parent_node, &len), prop->data, + kind, start, domU); +} + +static int __init process_chosen_node(const void *fdt, int node, + const char *name, + u32 address_cells, u32 size_cells) +{ + const struct fdt_property *prop; + paddr_t start, end; + int len; + + if ( fdt_get_property(fdt, node, "xen,static-heap", NULL) ) + { + int rc; + + printk("Checking for static heap in /chosen\n"); + + rc = device_tree_get_meminfo(fdt, node, "xen,static-heap", + address_cells, size_cells, + bootinfo_get_reserved_mem(), + MEMBANK_STATIC_HEAP); + if ( rc ) + return rc; + + bootinfo.static_heap = true; + } + + printk("Checking for initrd in /chosen\n"); + + prop = fdt_get_property(fdt, node, "linux,initrd-start", &len); + if ( !prop ) + /* No initrd present. */ + return 0; + if ( len != sizeof(u32) && len != sizeof(u64) ) + { + printk("linux,initrd-start property has invalid length %d\n", len); + return -EINVAL; + } + start = dt_read_paddr((const void *)&prop->data, dt_size_to_cells(len)); + + prop = fdt_get_property(fdt, node, "linux,initrd-end", &len); + if ( !prop ) + { + printk("linux,initrd-end not present but -start was\n"); + return -EINVAL; + } + if ( len != sizeof(u32) && len != sizeof(u64) ) + { + printk("linux,initrd-end property has invalid length %d\n", len); + return -EINVAL; + } + end = dt_read_paddr((const void *)&prop->data, dt_size_to_cells(len)); + + if ( start >= end ) + { + printk("linux,initrd limits invalid: %"PRIpaddr" >= %"PRIpaddr"\n", + start, end); + return -EINVAL; + } + + printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end); + + add_boot_module(BOOTMOD_RAMDISK, start, end-start, false); + + return 0; +} + +static int __init process_domain_node(const void *fdt, int node, + const char *name, + u32 address_cells, u32 size_cells) +{ + const struct fdt_property *prop; + + printk("Checking for \"xen,static-mem\" in domain node\n"); + + prop = fdt_get_property(fdt, node, "xen,static-mem", NULL); + if ( !prop ) + /* No "xen,static-mem" present. */ + return 0; + + return device_tree_get_meminfo(fdt, node, "xen,static-mem", address_cells, + size_cells, bootinfo_get_reserved_mem(), + MEMBANK_STATIC_DOMAIN); +} + +#ifndef CONFIG_STATIC_SHM +static inline int process_shm_node(const void *fdt, int node, + uint32_t address_cells, uint32_t size_cells) +{ + printk("CONFIG_STATIC_SHM must be enabled for parsing static shared" + " memory nodes\n"); + return -EINVAL; +} +#endif + +static int __init early_scan_node(const void *fdt, + int node, const char *name, int depth, + u32 address_cells, u32 size_cells, + void *data) +{ + int rc = 0; + + /* + * If Xen has been booted via UEFI, the memory banks are + * populated. So we should skip the parsing. + */ + if ( !efi_enabled(EFI_BOOT) && + device_tree_node_matches(fdt, node, "memory") ) + rc = process_memory_node(fdt, node, name, depth, + address_cells, size_cells, bootinfo_get_mem()); + else if ( depth == 1 && !dt_node_cmp(name, "reserved-memory") ) + rc = process_reserved_memory(fdt, node, name, depth, + address_cells, size_cells); + else if ( depth <= 3 && (device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) || + device_tree_node_compatible(fdt, node, "multiboot,module" ))) + process_multiboot_node(fdt, node, name, address_cells, size_cells); + else if ( depth == 1 && device_tree_node_matches(fdt, node, "chosen") ) + rc = process_chosen_node(fdt, node, name, address_cells, size_cells); + else if ( depth == 2 && device_tree_node_compatible(fdt, node, "xen,domain") ) + rc = process_domain_node(fdt, node, name, address_cells, size_cells); + else if ( depth <= 3 && device_tree_node_compatible(fdt, node, "xen,domain-shared-memory-v1") ) + rc = process_shm_node(fdt, node, address_cells, size_cells); + + if ( rc < 0 ) + printk("fdt: node `%s': parsing failed\n", name); + return rc; +} + +static void __init early_print_info(void) +{ + const struct membanks *mi = bootinfo_get_mem(); + const struct membanks *mem_resv = bootinfo_get_reserved_mem(); + struct bootmodules *mods = &bootinfo.modules; + struct bootcmdlines *cmds = &bootinfo.cmdlines; + unsigned int i; + + for ( i = 0; i < mi->nr_banks; i++ ) + printk("RAM: %"PRIpaddr" - %"PRIpaddr"\n", + mi->bank[i].start, + mi->bank[i].start + mi->bank[i].size - 1); + printk("\n"); + for ( i = 0 ; i < mods->nr_mods; i++ ) + printk("MODULE[%d]: %"PRIpaddr" - %"PRIpaddr" %-12s\n", + i, + mods->module[i].start, + mods->module[i].start + mods->module[i].size, + boot_module_kind_as_string(mods->module[i].kind)); + + for ( i = 0; i < mem_resv->nr_banks; i++ ) + { + printk(" RESVD[%u]: %"PRIpaddr" - %"PRIpaddr"\n", i, + mem_resv->bank[i].start, + mem_resv->bank[i].start + mem_resv->bank[i].size - 1); + } +#ifdef CONFIG_STATIC_SHM + early_print_info_shmem(); +#endif + printk("\n"); + for ( i = 0 ; i < cmds->nr_mods; i++ ) + printk("CMDLINE[%"PRIpaddr"]:%s %s\n", cmds->cmdline[i].start, + cmds->cmdline[i].dt_name, + &cmds->cmdline[i].cmdline[0]); + printk("\n"); +} + +/* This function assumes that memory regions are not overlapped */ +static int __init cmp_memory_node(const void *key, const void *elem) +{ + const struct membank *handler0 = key; + const struct membank *handler1 = elem; + + if ( handler0->start < handler1->start ) + return -1; + + if ( handler0->start >= (handler1->start + handler1->size) ) + return 1; + + return 0; +} + +static void __init swap_memory_node(void *_a, void *_b, size_t size) +{ + struct membank *a = _a, *b = _b; + + SWAP(*a, *b); +} + +/** + * boot_fdt_info - initialize bootinfo from a DTB + * @fdt: flattened device tree binary + * + * Returns the size of the DTB. + */ +size_t __init boot_fdt_info(const void *fdt, paddr_t paddr) +{ + struct membanks *reserved_mem = bootinfo_get_reserved_mem(); + struct membanks *mem = bootinfo_get_mem(); + unsigned int i; + int nr_rsvd; + int ret; + + ret = fdt_check_header(fdt); + if ( ret < 0 ) + panic("No valid device tree\n"); + + add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false); + + nr_rsvd = fdt_num_mem_rsv(fdt); + if ( nr_rsvd < 0 ) + panic("Parsing FDT memory reserve map failed (%d)\n", nr_rsvd); + + for ( i = 0; i < nr_rsvd; i++ ) + { + struct membank *bank; + paddr_t s, sz; + + if ( fdt_get_mem_rsv_paddr(device_tree_flattened, i, &s, &sz) < 0 ) + continue; + + if ( reserved_mem->nr_banks < reserved_mem->max_banks ) + { + bank = &reserved_mem->bank[reserved_mem->nr_banks]; + bank->start = s; + bank->size = sz; + bank->type = MEMBANK_FDT_RESVMEM; + reserved_mem->nr_banks++; + } + else + panic("Cannot allocate reserved memory bank\n"); + } + + ret = device_tree_for_each_node(fdt, 0, early_scan_node, NULL); + if ( ret ) + panic("Early FDT parsing failed (%d)\n", ret); + + /* + * On Arm64 setup_directmap_mappings() expects to be called with the lowest + * bank in memory first. There is no requirement that the DT will provide + * the banks sorted in ascending order. So sort them through. + */ + sort(mem->bank, mem->nr_banks, sizeof(struct membank), + cmp_memory_node, swap_memory_node); + + early_print_info(); + + return fdt_totalsize(fdt); +} + +const __init char *boot_fdt_cmdline(const void *fdt) +{ + int node; + const struct fdt_property *prop; + + node = fdt_path_offset(fdt, "/chosen"); + if ( node < 0 ) + return NULL; + + prop = fdt_get_property(fdt, node, "xen,xen-bootargs", NULL); + if ( prop == NULL ) + { + struct bootcmdline *dom0_cmdline = + boot_cmdline_find_by_kind(BOOTMOD_KERNEL); + + if (fdt_get_property(fdt, node, "xen,dom0-bootargs", NULL) || + ( dom0_cmdline && dom0_cmdline->cmdline[0] ) ) + prop = fdt_get_property(fdt, node, "bootargs", NULL); + } + if ( prop == NULL ) + return NULL; + + return prop->data; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/bootfdt.h b/xen/include/xen/bootfdt.h index c39428d5f5..16fa05f38f 100644 --- a/xen/include/xen/bootfdt.h +++ b/xen/include/xen/bootfdt.h @@ -157,6 +157,20 @@ struct bootinfo { extern struct bootinfo bootinfo; +bool check_reserved_regions_overlap(paddr_t region_start, + paddr_t region_size); + +struct bootmodule *add_boot_module(bootmodule_kind kind, + paddr_t start, paddr_t size, bool domU); +struct bootmodule *boot_module_find_by_kind(bootmodule_kind kind); +struct bootmodule * boot_module_find_by_addr_and_kind(bootmodule_kind kind, + paddr_t start); +void add_boot_cmdline(const char *name, const char *cmdline, + bootmodule_kind kind, paddr_t start, bool domU); +struct bootcmdline *boot_cmdline_find_by_kind(bootmodule_kind kind); +struct bootcmdline * boot_cmdline_find_by_name(const char *name); +const char *boot_module_kind_as_string(bootmodule_kind kind); + void populate_boot_allocator(void); size_t boot_fdt_info(const void *fdt, paddr_t paddr); From patchwork Wed Jul 24 15:31:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741096 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5A678C3DA63 for ; Wed, 24 Jul 2024 15:31:59 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764290.1174717 (Exim 4.92) (envelope-from ) id 1sWdy2-0007in-Dz; Wed, 24 Jul 2024 15:31:50 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764290.1174717; Wed, 24 Jul 2024 15:31:50 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy2-0007ig-AP; Wed, 24 Jul 2024 15:31:50 +0000 Received: by outflank-mailman (input) for mailman id 764290; Wed, 24 Jul 2024 15:31:48 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy0-0005Oj-Sc for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:48 +0000 Received: from mail-lj1-x230.google.com (mail-lj1-x230.google.com [2a00:1450:4864:20::230]) by se1-gles-sth1.inumbo.com (Halon) with ESMTPS id d7640d8e-49d1-11ef-bbfe-fd08da9f4363; Wed, 24 Jul 2024 17:31:48 +0200 (CEST) Received: by mail-lj1-x230.google.com with SMTP id 38308e7fff4ca-2ef2fbf1d14so10882981fa.1 for ; Wed, 24 Jul 2024 08:31:48 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:47 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d7640d8e-49d1-11ef-bbfe-fd08da9f4363 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835108; x=1722439908; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=CrU347Bqi/Mu5IIuJa5WjXX+En829gmjRRoMWLS+Csc=; b=ddiHMFdWMSCTVkB6zrwBLO0pHNn4qr00CnXV3VEJKfToXhQr336fQ2ec5PbDrz5pN9 R8W9n0hmHBUcev7oWOeXptDnHOWHlkw1njkG+85wKhmt+u/yyLlzbGuagFV0b0RFlswn RQqszwu/MJxlYXKKjQTufDyFIrvH5k8IQidikbVvINTruESh0i5oSJC7bWU49mSmwNsz s+BBCVvia3jd8TkR9EjrKXXmzZN4H+Z4zammH9ghYMpI5Xh9KJBSELZXNbvYvfXXrUpp Tiu+0EFVn0gjK8gZXL9a0eaUV4dtqOJjQcxl1SSPUQ7xuGFLXaqs3nXl1rqg2xVLgvWL qTWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835108; x=1722439908; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CrU347Bqi/Mu5IIuJa5WjXX+En829gmjRRoMWLS+Csc=; b=H4oQyWtZ2maJPXu37rZBfgJyRC0Xgf405XGfatThrD+o93XbPUsovz2SfwNmbmUtJT sdamzgpqdMLr6LZ08+GjO1n9LTp3Y9jJs5dUp81zagGybDtm92czZGRpDGElHGBpZyvv ope95HM1uEVdgVXncU7rbPTVSuU5qPxFhUsqVIW6DxXEzSJ6L6LKg0x+RBnRdzG8Auxc ob5XOtYXuBc6iFoi2El1jt9SprSPnEH9RbMwUASlT+vYd6zqj4yvreAsjXnHZn/oLYjR h/iHv3S0qS4gO5r0TyDfWah38JSkNG0P8K+DKmE7tvI6ejdSWBpARB5ULF7pn51Qm2Hx Trmw== X-Gm-Message-State: AOJu0YypdaKcD2+3zAS5XZnXjIYwF41CGDmtNefcABN2caoseIncg2A0 VyBJMWqfT+G4wimWuObu17jScUz88Nx9085uV11ZAcbqerp71XqxGsncV8vn X-Google-Smtp-Source: AGHT+IHmrh5MngxfCF1/++20FMQDwLx/3y8MXaraZVYqWCKJmI7Rg1+nFzCxgcikvq+dniNrPZDDtA== X-Received: by 2002:a2e:9c8c:0:b0:2ef:2b70:5372 with SMTP id 38308e7fff4ca-2f032cd28a9mr6620981fa.12.1721835107655; Wed, 24 Jul 2024 08:31:47 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 3/9] xen/riscv: enable CONFIG_HAS_DEVICE_TREE Date: Wed, 24 Jul 2024 17:31:34 +0200 Message-ID: X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Enable build of generic functionality for working with device tree for RISC-V. Also, a collection of functions for parsing memory map and other boot information from a device tree are available now. Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- Changes in V3: - add Acked-by: Jan Beulich - update the commit message --- Changes in V2: - move 'select HAS_DEVICE_TREE' to CONFIG_RISCV. --- Changes in V1: - new patch --- xen/arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/xen/arch/riscv/Kconfig b/xen/arch/riscv/Kconfig index f531e96657..259eea8d3b 100644 --- a/xen/arch/riscv/Kconfig +++ b/xen/arch/riscv/Kconfig @@ -2,6 +2,7 @@ config RISCV def_bool y select FUNCTION_ALIGNMENT_16B select GENERIC_BUG_FRAME + select HAS_DEVICE_TREE config RISCV_64 def_bool y From patchwork Wed Jul 24 15:31:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741100 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E99C6C52CD8 for ; Wed, 24 Jul 2024 15:32:03 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764293.1174737 (Exim 4.92) (envelope-from ) id 1sWdy5-0008Ap-K9; Wed, 24 Jul 2024 15:31:53 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764293.1174737; Wed, 24 Jul 2024 15:31:53 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy5-00087U-AN; Wed, 24 Jul 2024 15:31:53 +0000 Received: by outflank-mailman (input) for mailman id 764293; Wed, 24 Jul 2024 15:31:51 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy3-0005Od-KJ for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:51 +0000 Received: from mail-lj1-x231.google.com (mail-lj1-x231.google.com [2a00:1450:4864:20::231]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id d81d47e1-49d1-11ef-8776-851b0ebba9a2; Wed, 24 Jul 2024 17:31:49 +0200 (CEST) Received: by mail-lj1-x231.google.com with SMTP id 38308e7fff4ca-2eefe705510so76141901fa.1 for ; Wed, 24 Jul 2024 08:31:49 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:48 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d81d47e1-49d1-11ef-8776-851b0ebba9a2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835109; x=1722439909; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=J5JfDcjOdJ3XraQIsEMUmJEphHgWVY7ZxFBh+8Ru7ag=; b=iRvZPcocC+w/FdK0I8LwGhnr8ofeVKtLyOTcCuXfJnEXHtG5Yo38dkUMp7R2bFPa+y DliwlVuexEAb4pkrNEtC6ObRmcPUE10pbRj5iFWm1PCsEZVoD669aKaJw44YI/YM4nAU sHWLGF0GweaSPPgTQ965oGmSyKECwBVckoiIPJe5uXMwqjaqNsnbQ32xn3Ur7DJRylXa MnmVeP8y8JZHuCD3mRB3FzKWCrvYAqWfJPyN8vdGp/LCYy9xk8xgJTCBTcBW6odHeXEh 9R6UQ/LbwYovpDbZ463Xj247uf9RvwRyn9AOa+zP4RB0nGLlvRhmZXWz50iKZyX0RAlp GXtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835109; x=1722439909; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=J5JfDcjOdJ3XraQIsEMUmJEphHgWVY7ZxFBh+8Ru7ag=; b=FtHtxfwZ2pbJULL9tb0FBnfW8wPDiZ8ZItKaKhX4n31cOUuODanJrMky16T/aXuQdG ZJcb4Yd/oGF5iaNyElqx2H93UIWQzBkQKxptUp4DtfN6RXcCClRNG4RWh5Y52o0rrrB7 U8nL2mDKo+Osv9MgKbHEN+5oDXZJXPkbWqgr0VLx4RvRMY1YgnPgoqpIbQgmgtrix3mN 4q6EM/8+teqHjiyOH3z6RlkXEmAcTScqyVKMQRjTW9+FUnoVVVpZcYgwqo0bhrGEGW5e E6aZean+gu8Xb9ckI8gcGm59oeAKuClZYy+uK5xDOBCz00L4q0M/p6pterUfmTAvhHli MCow== X-Gm-Message-State: AOJu0YydUWrxeHeBcPGmHaEsmEEKiYbX6DXoOPpYMlDqnj/3T8vxAWQh gxbIs6CdRGc5K3vLf1tCzl85u+vjfG4LoeO9QNNUYTBq/n2eSDmGZGxSF2tb X-Google-Smtp-Source: AGHT+IFiBgT3XwFsReTDHlEwUN/Jqa4gn0ffbAC+D7oyHWup6bKLbWzzuzfNPdDOsXX1Le1M97rjNw== X-Received: by 2002:a2e:9dc2:0:b0:2ef:23ec:9356 with SMTP id 38308e7fff4ca-2f039c4e0cdmr1827781fa.8.1721835108566; Wed, 24 Jul 2024 08:31:48 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 4/9] xen/riscv: setup fixmap mapping Date: Wed, 24 Jul 2024 17:31:35 +0200 Message-ID: <04576976b82b97442f645b83b3d62475d144af8e.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Introduce a function to set up fixmap mappings and L0 page table for fixmap. Additionally, defines were introduced in riscv/config.h to calculate the FIXMAP_BASE address. This involved introducing BOOT_FDT_VIRT_{START, SIZE} and XEN_VIRT_SIZE, XEN_VIRT_END. Also, the check of Xen size was updated in the riscv/lds.S script to use XEN_VIRT_SIZE instead of a hardcoded constant. Signed-off-by: Oleksii Kurochko --- Changes in V3: - s/XEN_SIZE/XEN_VIRT_SIZE - drop usage of XEN_VIRT_END. - sort newly introduced defines in config.h by address - code style fixes - drop runtime check of that pte is valid as it was checked in L1 page table finding cycle by BUG_ON(). - update implementation of write_pte() with FENCE rw, rw. - add BUILD_BUG_ON() to check that amount of entries aren't bigger then entries in page table. - drop set_fixmap, clear_fixmap declarations as they aren't used and defined now - update the commit message. - s/__ASM_FIXMAP_H/ASM_FIXMAP_H - add SPDX-License-Identifier: GPL-2.0 --- xen/arch/riscv/include/asm/config.h | 8 +++++ xen/arch/riscv/include/asm/fixmap.h | 44 +++++++++++++++++++++++++++ xen/arch/riscv/include/asm/mm.h | 2 ++ xen/arch/riscv/include/asm/page.h | 9 ++++++ xen/arch/riscv/mm.c | 47 +++++++++++++++++++++++++++++ xen/arch/riscv/setup.c | 2 ++ xen/arch/riscv/xen.lds.S | 2 +- 7 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 xen/arch/riscv/include/asm/fixmap.h diff --git a/xen/arch/riscv/include/asm/config.h b/xen/arch/riscv/include/asm/config.h index 50583aafdc..f517684cbb 100644 --- a/xen/arch/riscv/include/asm/config.h +++ b/xen/arch/riscv/include/asm/config.h @@ -66,6 +66,14 @@ #define SLOTN_ENTRY_BITS (HYP_PT_ROOT_LEVEL * VPN_BITS + PAGE_SHIFT) #define SLOTN(slot) (_AT(vaddr_t, slot) << SLOTN_ENTRY_BITS) +#define XEN_VIRT_SIZE MB(2) + +#define BOOT_FDT_VIRT_START (XEN_VIRT_START + XEN_VIRT_SIZE) +#define BOOT_FDT_VIRT_SIZE MB(4) + +#define FIXMAP_BASE (BOOT_FDT_VIRT_START + BOOT_FDT_VIRT_SIZE) +#define FIXMAP_ADDR(n) (FIXMAP_BASE + (n) * PAGE_SIZE) + #if RV_STAGE1_MODE == SATP_MODE_SV39 #define XEN_VIRT_START 0xFFFFFFFFC0000000 #elif RV_STAGE1_MODE == SATP_MODE_SV48 diff --git a/xen/arch/riscv/include/asm/fixmap.h b/xen/arch/riscv/include/asm/fixmap.h new file mode 100644 index 0000000000..d3f5ee4944 --- /dev/null +++ b/xen/arch/riscv/include/asm/fixmap.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * fixmap.h: compile-time virtual memory allocation + */ +#ifndef ASM_FIXMAP_H +#define ASM_FIXMAP_H + +#include +#include +#include + +#include + +/* Fixmap slots */ +#define FIX_PMAP_BEGIN (0) /* Start of PMAP */ +#define FIX_PMAP_END (FIX_PMAP_BEGIN + NUM_FIX_PMAP - 1) /* End of PMAP */ +#define FIX_MISC (FIX_PMAP_END + 1) /* Ephemeral mappings of hardware */ + +#define FIX_LAST FIX_MISC + +#define FIXADDR_START FIXMAP_ADDR(0) +#define FIXADDR_TOP FIXMAP_ADDR(FIX_LAST) + +#ifndef __ASSEMBLY__ + +/* + * Direct access to xen_fixmap[] should only happen when {set, + * clear}_fixmap() is unusable (e.g. where we would end up to + * recursively call the helpers). + */ +extern pte_t xen_fixmap[]; + +#define fix_to_virt(slot) ((void *)FIXMAP_ADDR(slot)) + +static inline unsigned int virt_to_fix(vaddr_t vaddr) +{ + BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START); + + return ((vaddr - FIXADDR_START) >> PAGE_SHIFT); +} + +#endif /* __ASSEMBLY__ */ + +#endif /* ASM_FIXMAP_H */ diff --git a/xen/arch/riscv/include/asm/mm.h b/xen/arch/riscv/include/asm/mm.h index 25af9e1aaa..a0bdc2bc3a 100644 --- a/xen/arch/riscv/include/asm/mm.h +++ b/xen/arch/riscv/include/asm/mm.h @@ -255,4 +255,6 @@ static inline unsigned int arch_get_dma_bitsize(void) return 32; /* TODO */ } +void setup_fixmap_mappings(void); + #endif /* _ASM_RISCV_MM_H */ diff --git a/xen/arch/riscv/include/asm/page.h b/xen/arch/riscv/include/asm/page.h index c831e16417..0cc2f37cf8 100644 --- a/xen/arch/riscv/include/asm/page.h +++ b/xen/arch/riscv/include/asm/page.h @@ -11,6 +11,7 @@ #include #include +#include #define VPN_MASK (PAGETABLE_ENTRIES - 1UL) @@ -81,6 +82,14 @@ static inline void flush_page_to_ram(unsigned long mfn, bool sync_icache) BUG_ON("unimplemented"); } +/* Write a pagetable entry. */ +static inline void write_pte(pte_t *p, pte_t pte) +{ + RISCV_FENCE(rw, rw); + *p = pte; + RISCV_FENCE(rw, rw); +} + #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PAGE_H */ diff --git a/xen/arch/riscv/mm.c b/xen/arch/riscv/mm.c index 7d09e781bf..35724505ec 100644 --- a/xen/arch/riscv/mm.c +++ b/xen/arch/riscv/mm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,9 @@ stage1_pgtbl_root[PAGETABLE_ENTRIES]; pte_t __section(".bss.page_aligned") __aligned(PAGE_SIZE) stage1_pgtbl_nonroot[PGTBL_INITIAL_COUNT * PAGETABLE_ENTRIES]; +pte_t __section(".bss.page_aligned") __aligned(PAGE_SIZE) +xen_fixmap[PAGETABLE_ENTRIES]; + #define HANDLE_PGTBL(curr_lvl_num) \ index = pt_index(curr_lvl_num, page_addr); \ if ( pte_is_valid(pgtbl[index]) ) \ @@ -191,6 +195,49 @@ static bool __init check_pgtbl_mode_support(struct mmu_desc *mmu_desc, return is_mode_supported; } +void __init setup_fixmap_mappings(void) +{ + pte_t *pte, tmp; + unsigned int i; + + BUILD_BUG_ON(FIX_LAST >= PAGETABLE_ENTRIES); + + pte = &stage1_pgtbl_root[pt_index(HYP_PT_ROOT_LEVEL, FIXMAP_ADDR(0))]; + + /* + * In RISC-V page table levels are enumerated from Lx to L0 where + * x is the highest page table level for currect MMU mode ( for example, + * for Sv39 has 3 page tables so the x = 2 (L2 -> L1 -> L0) ). + * + * In this cycle we want to find L1 page table because as L0 page table + * xen_fixmap[] will be used. + * + * i is defined ( HYP_PT_ROOT_LEVEL - 1 ) becuase pte for L2 ( in + * case of Sv39 ) has been recieved above. + */ + for ( i = HYP_PT_ROOT_LEVEL - 1; i != 0; i-- ) + { + BUG_ON(!pte_is_valid(*pte)); + + pte = (pte_t *)LOAD_TO_LINK(pte_to_paddr(*pte)); + pte = &pte[pt_index(i, FIXMAP_ADDR(0))]; + } + + BUG_ON(pte_is_valid(*pte)); + + tmp = paddr_to_pte(LINK_TO_LOAD((unsigned long)&xen_fixmap), PTE_TABLE); + write_pte(pte, tmp); + + sfence_vma(); + + printk("(XEN) fixmap is mapped\n"); + + /* + * We only need the zeroeth table allocated, but not the PTEs set, because + * set_fixmap() will set them on the fly. + */ +} + /* * setup_initial_pagetables: * diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 4defad68f4..13f0e8c77d 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -46,6 +46,8 @@ void __init noreturn start_xen(unsigned long bootcpu_id, test_macros_from_bug_h(); #endif + setup_fixmap_mappings(); + printk("All set up\n"); for ( ;; ) diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S index 070b19d915..7a683f6065 100644 --- a/xen/arch/riscv/xen.lds.S +++ b/xen/arch/riscv/xen.lds.S @@ -181,6 +181,6 @@ ASSERT(!SIZEOF(.got.plt), ".got.plt non-empty") * Changing the size of Xen binary can require an update of * PGTBL_INITIAL_COUNT. */ -ASSERT(_end - _start <= MB(2), "Xen too large for early-boot assumptions") +ASSERT(_end - _start <= XEN_VIRT_SIZE, "Xen too large for early-boot assumptions") ASSERT(_ident_end - _ident_start <= IDENT_AREA_SIZE, "identity region is too big"); From patchwork Wed Jul 24 15:31:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741097 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 79C26C3DA61 for ; Wed, 24 Jul 2024 15:32:02 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764295.1174754 (Exim 4.92) (envelope-from ) id 1sWdy7-0000EF-5x; Wed, 24 Jul 2024 15:31:55 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764295.1174754; Wed, 24 Jul 2024 15:31:55 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy6-0000DS-SY; Wed, 24 Jul 2024 15:31:54 +0000 Received: by outflank-mailman (input) for mailman id 764295; Wed, 24 Jul 2024 15:31:52 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy4-0005Od-Kd for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:52 +0000 Received: from mail-lj1-x231.google.com (mail-lj1-x231.google.com [2a00:1450:4864:20::231]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id d9092283-49d1-11ef-8776-851b0ebba9a2; Wed, 24 Jul 2024 17:31:51 +0200 (CEST) Received: by mail-lj1-x231.google.com with SMTP id 38308e7fff4ca-2ef2ed59200so44649161fa.3 for ; Wed, 24 Jul 2024 08:31:51 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:49 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d9092283-49d1-11ef-8776-851b0ebba9a2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835110; x=1722439910; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=g6XL7EOUU66EeSUXZG2jsVtfUvFtJf8Harf/p3UUhw0=; b=cvQF3i/3PM9dsQDE5rDJVaDnQzLFpWK1gorApVKjVCHl8QI1h80sw8evsms0XM7ymz dnXA7vRdpCEKs53EbLEtl8nWgJzA/+4T2ScdNfG3Bm1t3GLlwtWnJ6p9co2/MaMXdRuK BzdQ6D5yTuTqwnxmTMdCgtL2RJN4Q59BEKewa0R/XDucFJgTVxFWF/XakeNRXinA0fwt NPfl1F9sayXzq1rTBJuD2Q4pd6B9tDOE5N7r+nH6wDmIHTd0v8QUaMtjTO71+VyTvH75 KsBPHnMrJuLdqYvuMblf+6Mi4HwzjGevY5Zknwx5XfZQhVy3iIJ72ZDNQjPJG/U9/aib ZMSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835110; x=1722439910; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g6XL7EOUU66EeSUXZG2jsVtfUvFtJf8Harf/p3UUhw0=; b=u2rLISDuIXyzdjDsR8pHhkD9KHpRPgEzM+cjcEHhkdnENklM8KENPoQUj/0p07q+uN 5292/JsZGtyvIKWRcTZ7xOxt7xK3EWXMiGyDwYoS22gmLsWkNCvEcuA3M2XaZcg9kmPe 17BL9f9K29zIEEvlP3EuxKK9WAXpC8KTKoD8uFsBLJphZLrQQ2RMtbOkqM16LVZ/ElRX G7bZzZgDGWbicGS5ZfE7QaKoQrDGQfbQGR5pizn/rmrcJX3i8nBj37YYpBbyUyBRgv7v w7MxhAiWl9bJn80XD1PuV/eT59K8JkjEd5exECHwnzZFbZk3Dubbtv4bDWsg5bz86uSk Ma2A== X-Gm-Message-State: AOJu0YzFRkBxx0za/oVgfYpXl70spYuxUDA3Q/Ypz/r3ckbdy6CwyJ7I b/a+7ELDwcQhdDCruZSNQovuB97Tsn9RnyppkEL4JCs/at9tgzbrzDzXWVaI X-Google-Smtp-Source: AGHT+IH8HTEyrNUZ4pjWIiLtjbSoRHXD+2hsZ8NBtqOV5432Io/rqG/Z2LvelVQf/3ru/RpQkdb7VQ== X-Received: by 2002:a2e:b8ce:0:b0:2ef:21e5:1f01 with SMTP id 38308e7fff4ca-2f039cdff01mr1878401fa.20.1721835109259; Wed, 24 Jul 2024 08:31:49 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 5/9] xen/riscv: introduce asm/pmap.h header Date: Wed, 24 Jul 2024 17:31:36 +0200 Message-ID: <11b5487659a9c76793e520c108cd92c6c84b908d.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Introduces arch_pmap_{un}map functions and select HAS_PMAP for CONFIG_RISCV. Additionaly it was necessary to introduce functions: - mfn_from_xen_entry - mfn_to_pte Also flush_xen_tlb_range_va_local() and flush_xen_tlb_one_local() are introduced and use in arch_pmap_unmap(). Signed-off-by: Oleksii Kurochko --- Changes in V3: - rename argument of function mfn_to_xen_entry(..., attr -> flags ). - update the code of mfn_to_xen_entry() to use flags argument. - add blank in mfn_from_pte() in return line. - introduce flush_xen_tlb_range_va_local() and use it inside arch_pmap_{un}map(). - s/__ASM_PMAP_H__/ASM_PMAP_H - add SPDX-License-Identifier: GPL-2.0 --- xen/arch/riscv/Kconfig | 1 + xen/arch/riscv/include/asm/flushtlb.h | 22 ++++++++++++++++++ xen/arch/riscv/include/asm/page.h | 2 ++ xen/arch/riscv/include/asm/pmap.h | 33 +++++++++++++++++++++++++++ xen/arch/riscv/mm.c | 15 ++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 xen/arch/riscv/include/asm/pmap.h diff --git a/xen/arch/riscv/Kconfig b/xen/arch/riscv/Kconfig index 259eea8d3b..0112aa8778 100644 --- a/xen/arch/riscv/Kconfig +++ b/xen/arch/riscv/Kconfig @@ -3,6 +3,7 @@ config RISCV select FUNCTION_ALIGNMENT_16B select GENERIC_BUG_FRAME select HAS_DEVICE_TREE + select HAS_PMAP config RISCV_64 def_bool y diff --git a/xen/arch/riscv/include/asm/flushtlb.h b/xen/arch/riscv/include/asm/flushtlb.h index 7ce32bea0b..cf66e90773 100644 --- a/xen/arch/riscv/include/asm/flushtlb.h +++ b/xen/arch/riscv/include/asm/flushtlb.h @@ -5,6 +5,28 @@ #include #include +/* Flush TLB of local processor for address va. */ +static inline void flush_xen_tlb_one_local(vaddr_t va) +{ + asm volatile ( "sfence.vma %0" :: "r" (va) : "memory" ); +} + +/* + * Flush a range of VA's hypervisor mappings from the TLB of the local + * processor. + */ +static inline void flush_xen_tlb_range_va_local(vaddr_t va, + unsigned long size) +{ + vaddr_t end = va + size; + + while ( va < end ) + { + flush_xen_tlb_one_local(va); + va += PAGE_SIZE; + } +} + /* * Filter the given set of CPUs, removing those that definitely flushed their * TLB since @page_timestamp. diff --git a/xen/arch/riscv/include/asm/page.h b/xen/arch/riscv/include/asm/page.h index 0cc2f37cf8..2308cefb0a 100644 --- a/xen/arch/riscv/include/asm/page.h +++ b/xen/arch/riscv/include/asm/page.h @@ -52,6 +52,8 @@ typedef struct { #endif } pte_t; +pte_t mfn_to_xen_entry(mfn_t mfn, unsigned int access_bits); + static inline pte_t paddr_to_pte(paddr_t paddr, unsigned int permissions) { diff --git a/xen/arch/riscv/include/asm/pmap.h b/xen/arch/riscv/include/asm/pmap.h new file mode 100644 index 0000000000..068d0794b1 --- /dev/null +++ b/xen/arch/riscv/include/asm/pmap.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ASM_PMAP_H +#define ASM_PMAP_H + +#include +#include +#include + +#include +#include +#include + +static inline void arch_pmap_map(unsigned int slot, mfn_t mfn) +{ + pte_t *entry = &xen_fixmap[slot]; + pte_t pte; + + ASSERT(!pte_is_valid(*entry)); + + pte = mfn_to_xen_entry(mfn, PAGE_HYPERVISOR_RW); + write_pte(entry, pte); +} + +static inline void arch_pmap_unmap(unsigned int slot) +{ + pte_t pte = {}; + + write_pte(&xen_fixmap[slot], pte); + + flush_xen_tlb_range_va_local(FIXMAP_ADDR(slot), PAGE_SIZE); +} + +#endif /* ASM_PMAP_H */ diff --git a/xen/arch/riscv/mm.c b/xen/arch/riscv/mm.c index 35724505ec..959b6fc63e 100644 --- a/xen/arch/riscv/mm.c +++ b/xen/arch/riscv/mm.c @@ -382,3 +382,18 @@ int map_pages_to_xen(unsigned long virt, BUG_ON("unimplemented"); return -1; } + +static inline pte_t mfn_from_pte(mfn_t mfn) +{ + unsigned long pte = mfn_x(mfn) << PTE_PPN_SHIFT; + return (pte_t){ .pte = pte }; +} + +inline pte_t mfn_to_xen_entry(mfn_t mfn, unsigned int access_bits) +{ + pte_t pte = mfn_from_pte(mfn); + + pte.pte |= access_bits; + + return pte; +} From patchwork Wed Jul 24 15:31:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741099 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id DA500C3DA7F for ; Wed, 24 Jul 2024 15:32:03 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764294.1174749 (Exim 4.92) (envelope-from ) id 1sWdy6-00008v-NA; Wed, 24 Jul 2024 15:31:54 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764294.1174749; Wed, 24 Jul 2024 15:31:54 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy6-00006s-Gj; Wed, 24 Jul 2024 15:31:54 +0000 Received: by outflank-mailman (input) for mailman id 764294; Wed, 24 Jul 2024 15:31:52 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy4-0005Oj-HW for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:52 +0000 Received: from mail-lj1-x22c.google.com (mail-lj1-x22c.google.com [2a00:1450:4864:20::22c]) by se1-gles-sth1.inumbo.com (Halon) with ESMTPS id d98969a1-49d1-11ef-bbfe-fd08da9f4363; Wed, 24 Jul 2024 17:31:51 +0200 (CEST) Received: by mail-lj1-x22c.google.com with SMTP id 38308e7fff4ca-2ef2cce8c08so41848491fa.0 for ; Wed, 24 Jul 2024 08:31:52 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:50 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d98969a1-49d1-11ef-bbfe-fd08da9f4363 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835111; x=1722439911; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=PXOE9CRQc9546f2NMHjGZ/14SaZEN5RgTjEp/XheDio=; b=k49/vwWu8fhBmZqpE1IS9yn5z2UFmmfZFhWBw48/M9Hlx2tWiNtLxgrqC8XLt/NW08 dGNjXrPZUocCpPNSirQeY0o/H7I9eJkn2OjDQcJ4ruZGmrgvzdAWBjHFybITO/ZJfHph uUnkU7ipv4mGQMhP8X+86UXxeCp+ecVXPgi7SKUDnbWrFqoWfQjnL2fcMwXnd9HRQdV6 XmFtC97xrPhlZ5OqJxqF+xp1IbRcn8mu+mw7QaaqSYBhJsp+P4BY2d6oJeNamK2eyVfA tRIvqlaDnxWVRAseB2hfSAK0aIKpc1XpgEwg9BgMc321y6U6ujoN13R96TQJHOo3PpmU EUPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835111; x=1722439911; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=PXOE9CRQc9546f2NMHjGZ/14SaZEN5RgTjEp/XheDio=; b=iCrfoVzN0zKTihxKzsEqqMsUgPwnUkgzIsNOYaUIyL6KbMvHRsko6V6GaG7560GL9u C1AdML1eCYsfjWmLy2NX/kQVBPhdFoF9OSQa3eD2oCmqvWDg0iIfmOtc2jS+NAGyOLst KpfdUYlPYM+Yjhxs2FHnjHr1tKWdtUuK1944TFxhM/DdfWq1gBrLF66BfqT2HyV6WFzQ wWqYQ7SSSVVEyKRfvbzeg9iWVleNFgIsLV1nqg1iKXICY4dyyP8sO7+XtdUS33V60QvE +X9Hr4iTbUKtEYVRS9nC2QbM4U+BvVApf+Hd3KaV03bZ2ecDwzWilVLTihbH8S4LiDno TLBA== X-Gm-Message-State: AOJu0YxkJF3TqD0b9UIyo0dknHvxMq8yZv08aa6rO7joV6BivtLjduIv z5tJ6g0bapZxEUgeh+xBnQ5RS9a22Xr30QBFrlTvU+f7TaRvu7lUZoOz3tc6 X-Google-Smtp-Source: AGHT+IFsU2+Od5rLfvrYUmDoF5dSsSMCuxFKBOlJMYc7fMR+ZRmqqk7JEQO5hNH2cIv6nBgSU6DcFA== X-Received: by 2002:a2e:9651:0:b0:2ef:1edc:3b6a with SMTP id 38308e7fff4ca-2f039ea7c63mr1224821fa.21.1721835111186; Wed, 24 Jul 2024 08:31:51 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 6/9] xen/riscv: introduce functionality to work with cpu info Date: Wed, 24 Jul 2024 17:31:37 +0200 Message-ID: <4ea9005d4209e24df9b30a7b3c282276084a3cf1.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 struct pcpu_info is introduced to store pcpu related information. At the moment, it is only processor_id but in the fututre it will be guest cpu information and some temporary variables which will be used during save/restore of vcpu registers. set_processor_id() and get_processor_id() are introduced to set and get processor id which is stored in pcpu_info. __cpuid_to_hartid_map[NR_CPUS] is introduced to store the mapping between Xen logical CPU and hartid ( physical CPU id ) and auxiliary macros cpuid_to_hartid_map() for convience access to __cpuid_to_hartid_map[]. smp_processor_id() is defined properly as it is enough to information to define it now instead of seting it to "dummy" 0. Also, tp registers is initialized to point to pcpu_info[0]; set_processor_id is set to 0 as Xen is running on logical cpu 0 and save physical CPU id for current logical CPU id in __cpuid_to_hartid_map[]. Signed-off-by: Oleksii Kurochko --- Changes in V3: - new patch. --- xen/arch/riscv/Makefile | 2 ++ xen/arch/riscv/include/asm/processor.h | 35 ++++++++++++++++++++++++-- xen/arch/riscv/include/asm/smp.h | 10 ++++++++ xen/arch/riscv/setup.c | 13 ++++++++++ xen/arch/riscv/smp.c | 4 +++ xen/arch/riscv/smpboot.c | 12 +++++++++ 6 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 xen/arch/riscv/smp.c create mode 100644 xen/arch/riscv/smpboot.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index 81b77b13d6..334fd24547 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -4,6 +4,8 @@ obj-y += mm.o obj-$(CONFIG_RISCV_64) += riscv64/ obj-y += sbi.o obj-y += setup.o +obj-y += smp.o +obj-y += smpboot.o obj-y += stubs.o obj-y += traps.o obj-y += vm_event.o diff --git a/xen/arch/riscv/include/asm/processor.h b/xen/arch/riscv/include/asm/processor.h index 3ae164c265..4c3e45fd17 100644 --- a/xen/arch/riscv/include/asm/processor.h +++ b/xen/arch/riscv/include/asm/processor.h @@ -12,8 +12,39 @@ #ifndef __ASSEMBLY__ -/* TODO: need to be implemeted */ -#define smp_processor_id() 0 +#include +#include + +register struct pcpu_info *tp asm ("tp"); + +struct pcpu_info { + unsigned int processor_id; +}; + +/* tp points to one of these */ +extern struct pcpu_info pcpu_info[NR_CPUS]; + +#define get_processor_id() (tp->processor_id) +#define set_processor_id(id) do { \ + tp->processor_id = id; \ +} while(0) + +static inline unsigned int smp_processor_id(void) +{ + unsigned int id; + + id = get_processor_id(); + + /* + * Technically the hartid can be greater than what a uint can hold. + * If such a system were to exist, we will need to change + * the smp_processor_id() API to be unsigned long instead of + * unsigned int. + */ + BUG_ON(id > UINT_MAX); + + return id; +} /* On stack VCPU state */ struct cpu_user_regs diff --git a/xen/arch/riscv/include/asm/smp.h b/xen/arch/riscv/include/asm/smp.h index b1ea91b1eb..3fff27a8a5 100644 --- a/xen/arch/riscv/include/asm/smp.h +++ b/xen/arch/riscv/include/asm/smp.h @@ -5,6 +5,8 @@ #include #include +#define INVALID_HARTID UINT_MAX + DECLARE_PER_CPU(cpumask_var_t, cpu_sibling_mask); DECLARE_PER_CPU(cpumask_var_t, cpu_core_mask); @@ -14,6 +16,14 @@ DECLARE_PER_CPU(cpumask_var_t, cpu_core_mask); */ #define park_offline_cpus false +void smp_setup_processor_id(unsigned long boot_cpu_hartid); + +/* + * Mapping between linux logical cpu index and hartid. + */ +extern unsigned long __cpuid_to_hartid_map[NR_CPUS]; +#define cpuid_to_hartid_map(cpu) __cpuid_to_hartid_map[cpu] + #endif /* diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 13f0e8c77d..37b234360c 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -40,6 +40,19 @@ void __init noreturn start_xen(unsigned long bootcpu_id, { remove_identity_mapping(); + /* + * tp register contains an address of physical cpu information. + * So write physical CPU info of boot cpu to tp register + * It will be used later by get_processor_id() to get process_id ( look at + * ): + * #define get_processor_id() (tp->processor_id) + */ + asm volatile ("mv tp, %0" : : "r"((unsigned long)&pcpu_info[0])); + + set_processor_id(0); + + smp_setup_processor_id(bootcpu_id); + trap_init(); #ifdef CONFIG_SELF_TESTS diff --git a/xen/arch/riscv/smp.c b/xen/arch/riscv/smp.c new file mode 100644 index 0000000000..006a062ad7 --- /dev/null +++ b/xen/arch/riscv/smp.c @@ -0,0 +1,4 @@ +#include + +/* tp points to one of these per cpu */ +struct pcpu_info pcpu_info[NR_CPUS]; \ No newline at end of file diff --git a/xen/arch/riscv/smpboot.c b/xen/arch/riscv/smpboot.c new file mode 100644 index 0000000000..a90401ffd4 --- /dev/null +++ b/xen/arch/riscv/smpboot.c @@ -0,0 +1,12 @@ +#include +#include +#include + +unsigned long __cpuid_to_hartid_map[NR_CPUS] __ro_after_init = { + [0 ... NR_CPUS-1] = INVALID_HARTID +}; + +void __init smp_setup_processor_id(unsigned long boot_cpu_hartid) +{ + cpuid_to_hartid_map(0) = boot_cpu_hartid; +} From patchwork Wed Jul 24 15:31:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741106 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 57892C3DA63 for ; Wed, 24 Jul 2024 15:36:23 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764342.1174787 (Exim 4.92) (envelope-from ) id 1sWe2K-00058c-4m; Wed, 24 Jul 2024 15:36:16 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764342.1174787; Wed, 24 Jul 2024 15:36:16 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWe2K-00058V-1R; Wed, 24 Jul 2024 15:36:16 +0000 Received: by outflank-mailman (input) for mailman id 764342; Wed, 24 Jul 2024 15:36:15 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy7-0005Od-LS for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:55 +0000 Received: from mail-lj1-x233.google.com (mail-lj1-x233.google.com [2a00:1450:4864:20::233]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id da30c9c0-49d1-11ef-8776-851b0ebba9a2; Wed, 24 Jul 2024 17:31:53 +0200 (CEST) Received: by mail-lj1-x233.google.com with SMTP id 38308e7fff4ca-2ef2d96164aso34910601fa.3 for ; Wed, 24 Jul 2024 08:31:53 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:51 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: da30c9c0-49d1-11ef-8776-851b0ebba9a2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835112; x=1722439912; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=q/CS7Jv3CQg3aYb6VjDr+kgbj+LlISONX9Xzf/VXrcE=; b=isNN5wGfEzeZZBDCZ9UPN64CIA8ouZHSl88/ejQk5xorhIqKLi7Hu1/JoMb5Htmu5m Xe2I8R+OnNFq9O4RFLmrRZ5AVyzkL9z4h9ZJKSqBaiEia70zl/X/7ikBvzklpfR8kpQL iL0n/Hp+fVh37lxxeQhmC9jE1ESgIjvVXyPL20hAxcLxDkygYtzcHnHZRlw+0x8Gf7+N tPwoASJ+5pn7tMlP1v283Hplklr1OMHTDbn4htaDpCoPRjUJKw72w350jGjjYC0lqPls +24usiP3A7a4iWmiHc20wj/jHIAzrNC66LbyVq2MqJGhospmD0YHrLOW2ytzto1cOrDC l9oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835112; x=1722439912; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=q/CS7Jv3CQg3aYb6VjDr+kgbj+LlISONX9Xzf/VXrcE=; b=SC1eNCQdO5UyIdgcNmYuZUPVbkE6z7tZycwoBPIOZdoxCETx+i/OkLAyThd831RIG6 c+lHW2jNu3cIKt7D8VEMRJUsyhwDuSS+Zc9zckktaw5eTKFhcW/xehkFFiv7g/Zz74Lm aXD81eKNAO9tZV8qeX0zGyDHWf/t9jZsVgC6Wm3qRxH/vVWkGzT97uHv7QygDwmnMuuf sG/q+0eD6cDAiKYXee9CnLJCI0EAsUB3w12ySWroXzcXkhu7wwBncxPwMWDzbDUHweye UlcDb4WR+dfIcpnhpSlJkAH0nAlmZAw19kFRO3kKc1FbJ2wOT3dRuKeof0bz1uTuOFXA POgw== X-Gm-Message-State: AOJu0YxVvmpUOVxknsuDF0AL70U4K2BeRmaSjaevln2Ap7rNNjnyzHpW +S56Bdv74pdKppuFcsSvRSNWiE7zJ6FwB3qY4n5SN8NmenqrTGdDAaT+5cAX X-Google-Smtp-Source: AGHT+IEyjEPjgLD8s7dHrkVR9Q0dDIrWRBSTtqdVISR/d2JPuqHEq2zU5N7KJ813KWl1O7q1C3Yopg== X-Received: by 2002:a2e:9d17:0:b0:2ef:2f77:e577 with SMTP id 38308e7fff4ca-2f039cac58dmr1412581fa.27.1721835112186; Wed, 24 Jul 2024 08:31:52 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 7/9] xen/riscv: introduce and init SBI RFENCE extension Date: Wed, 24 Jul 2024 17:31:38 +0200 Message-ID: X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Introduces functions to work with SBI RFENCE extenstion which will be used to do various version of fences for remote ( not local ) CPU. Except that sbi_init() function and auxiliary functions and macros definitions are introduced to proper initialization and checking availability of SBI extenstions. At the moment, it is only done for RFENCE. sbi_remote_sfence_vma() is introduced to send SFENCE_VMA to a set of target HARTs and based on that flush_xen_tlb_range_va() will be implemented. Signed-off-by: Oleksii Kurochko --- Changes in V3: - new patch. --- xen/arch/riscv/include/asm/sbi.h | 57 +++++++ xen/arch/riscv/sbi.c | 256 +++++++++++++++++++++++++++++++ xen/arch/riscv/setup.c | 3 + 3 files changed, 316 insertions(+) diff --git a/xen/arch/riscv/include/asm/sbi.h b/xen/arch/riscv/include/asm/sbi.h index 0e6820a4ed..0985fbb1aa 100644 --- a/xen/arch/riscv/include/asm/sbi.h +++ b/xen/arch/riscv/include/asm/sbi.h @@ -14,6 +14,38 @@ #define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1 +#define SBI_EXT_BASE 0x10 +#define SBI_EXT_RFENCE 0x52464E43 + +/* SBI function IDs for BASE extension */ +#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 +#define SBI_EXT_BASE_GET_IMP_ID 0x1 +#define SBI_EXT_BASE_GET_IMP_VERSION 0x2 +#define SBI_EXT_BASE_PROBE_EXT 0x3 + +/* SBI function IDs for RFENCE extension */ +#define SBI_EXT_RFENCE_REMOTE_FENCE_I 0x0 +#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA 0x1 +#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID 0x2 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA 0x3 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID 0x4 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA 0x5 +#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID 0x6 + +#define SBI_SPEC_VERSION_MAJOR_SHIFT 24 +#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f +#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff + +/* SBI return error codes */ +#define SBI_SUCCESS 0 +#define SBI_ERR_FAILURE -1 +#define SBI_ERR_NOT_SUPPORTED -2 +#define SBI_ERR_INVALID_PARAM -3 +#define SBI_ERR_DENIED -4 +#define SBI_ERR_INVALID_ADDRESS -5 + +#define SBI_SPEC_VERSION_DEFAULT 0x1 + struct sbiret { long error; long value; @@ -31,4 +63,29 @@ struct sbiret sbi_ecall(unsigned long ext, unsigned long fid, */ void sbi_console_putchar(int ch); +/* + * Check underlying SBI implementation has RFENCE + * + * @return 1 for supported AND 0 for not-supported + */ +int sbi_has_rfence(void); + +/* + * Send SFENCE_VMA to a set of target HARTs. + * + * @param hart_mask mask representing set of target HARTs + * @param start virtual address start + * @param size virtual address size + */ +void sbi_remote_sfence_vma(const unsigned long *hart_mask, + unsigned long start, + unsigned long size); + +/* + * Initialize SBI library + * + * @return 0 on success, otherwise negative errno on failure + */ +int sbi_init(void); + #endif /* __ASM_RISCV_SBI_H__ */ diff --git a/xen/arch/riscv/sbi.c b/xen/arch/riscv/sbi.c index 0ae166c861..04d878d1e2 100644 --- a/xen/arch/riscv/sbi.c +++ b/xen/arch/riscv/sbi.c @@ -10,8 +10,18 @@ * Copyright (c) 2021-2023 Vates SAS. */ +#include +#include +#include +#include +#include + +#include #include +static unsigned long sbi_spec_version = SBI_SPEC_VERSION_DEFAULT; +static unsigned long sbi_fw_id, sbi_fw_version; + struct sbiret sbi_ecall(unsigned long ext, unsigned long fid, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, @@ -38,7 +48,253 @@ struct sbiret sbi_ecall(unsigned long ext, unsigned long fid, return ret; } +static int sbi_err_map_xen_errno(int err) +{ + switch ( err ) + { + case SBI_SUCCESS: + return 0; + case SBI_ERR_DENIED: + return -EACCES; + case SBI_ERR_INVALID_PARAM: + return -EINVAL; + case SBI_ERR_INVALID_ADDRESS: + return -EFAULT; + case SBI_ERR_NOT_SUPPORTED: + case SBI_ERR_FAILURE: + default: + return -EOPNOTSUPP; + }; +} + void sbi_console_putchar(int ch) { sbi_ecall(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, ch, 0, 0, 0, 0, 0); } + +static unsigned long sbi_major_version(void) +{ + return (sbi_spec_version >> SBI_SPEC_VERSION_MAJOR_SHIFT) & + SBI_SPEC_VERSION_MAJOR_MASK; +} + +static unsigned long sbi_minor_version(void) +{ + return sbi_spec_version & SBI_SPEC_VERSION_MINOR_MASK; +} + +static long sbi_ext_base_func(long fid) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_BASE, fid, 0, 0, 0, 0, 0, 0); + if (!ret.error) + return ret.value; + else + return ret.error; +} + +static void sbi_cpumask_to_hartmask(const struct cpumask *cmask, + struct cpumask *hmask) +{ + u32 cpu; + unsigned long hart = INVALID_HARTID; + + if (!cmask || !hmask) + return; + + cpumask_clear(hmask); + for_each_cpu(cpu, cmask) + { + if ( CONFIG_NR_CPUS <= cpu ) + { + printk(XENLOG_ERR "SBI: invalid hart=%lu for cpu=%d\n", + hart, cpu); + continue; + } + + hart = cpuid_to_hartid_map(pcpu_info[cpu].processor_id); + cpumask_set_cpu(hart, hmask); + } +} + +static int __sbi_rfence_v02_real(unsigned long fid, + unsigned long hmask, unsigned long hbase, + unsigned long start, unsigned long size, + unsigned long arg4) +{ + struct sbiret ret = {0}; + int result = 0; + + switch ( fid ) + { + case SBI_EXT_RFENCE_REMOTE_FENCE_I: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + 0, 0, 0, 0); + break; + case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, 0, 0); + break; + case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, arg4, 0); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, 0, 0); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, arg4, 0); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, 0, 0); + break; + case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID: + ret = sbi_ecall(SBI_EXT_RFENCE, fid, hmask, hbase, + start, size, arg4, 0); + break; + + default: + printk("%s: unknown function ID [%lu]\n", + __func__, fid); + result = -EINVAL; + break; + }; + + if ( ret.error ) + { + result = sbi_err_map_xen_errno(ret.error); + printk("%s: hbase=%lu hmask=0x%lx failed (error %d)\n", + __func__, hbase, hmask, result); + } + + return result; +} + +static int __sbi_rfence_v02(unsigned long fid, + const unsigned long *hart_mask, + unsigned long start, unsigned long size, + unsigned long arg4, unsigned long arg5) +{ + struct cpumask tmask; + unsigned long hart, hmask, hbase; + int result; + + if (!hart_mask) + { + sbi_cpumask_to_hartmask(&cpu_online_map, &tmask); + hart_mask = cpumask_bits(&tmask); + } + + hmask = hbase = 0; + for_each_set_bit(hart, hart_mask, CONFIG_NR_CPUS) + { + if (hmask && ((hbase + BITS_PER_LONG) <= hart)) + { + result = __sbi_rfence_v02_real(fid, hmask, hbase, + start, size, arg4); + if (result) + return result; + hmask = hbase = 0; + } + + if (!hmask) + hbase = hart; + + hmask |= 1UL << (hart - hbase); + } + + if (hmask) + { + result = __sbi_rfence_v02_real(fid, hmask, hbase, + start, size, arg4); + if (result) + return result; + } + + return 0; +} + +static int (*__sbi_rfence)(unsigned long fid, + const unsigned long *hart_mask, + unsigned long start, unsigned long size, + unsigned long arg4, unsigned long arg5) = NULL; + +void sbi_remote_sfence_vma(const unsigned long *hart_mask, + unsigned long start, + unsigned long size) +{ + __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA, + hart_mask, start, size, 0, 0); +} + +#define sbi_get_spec_version() \ + sbi_ext_base_func(SBI_EXT_BASE_GET_SPEC_VERSION) + +#define sbi_get_firmware_id() \ + sbi_ext_base_func(SBI_EXT_BASE_GET_IMP_ID) + +#define sbi_get_firmware_version() \ + sbi_ext_base_func(SBI_EXT_BASE_GET_IMP_VERSION) + +int sbi_probe_extension(long extid) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid, + 0, 0, 0, 0, 0); + if (!ret.error && ret.value) + return ret.value; + + return -EOPNOTSUPP; +} + +static int sbi_spec_is_0_1(void) +{ + return (sbi_spec_version == SBI_SPEC_VERSION_DEFAULT) ? 1 : 0; +} + +int sbi_has_rfence(void) +{ + return __sbi_rfence ? 1 : 0; +} + +int __init sbi_init(void) +{ + int ret; + + ret = sbi_get_spec_version(); + if (ret > 0) + sbi_spec_version = ret; + + printk("SBI specification v%lu.%lu detected\n", + sbi_major_version(), sbi_minor_version()); + + if ( !sbi_spec_is_0_1() ) + { + sbi_fw_id = sbi_get_firmware_id(); + sbi_fw_version = sbi_get_firmware_version(); + + printk("SBI implementation ID=0x%lx Version=0x%lx\n", + sbi_fw_id, sbi_fw_version); + + if ( sbi_probe_extension(SBI_EXT_RFENCE) > 0 ) + { + __sbi_rfence = __sbi_rfence_v02; + printk("SBI v0.2 RFENCE extension detected\n"); + } + } else { + BUG_ON("Ooops. SBI spec veriosn 0.1 detected. Need to add support"); + } + + if ( !sbi_has_rfence() ) + { + BUG_ON("At the moment flush_xen_tlb_range_va() uses SBI rfence to " + "flush TLB for all CPUS!"); + } + + return 0; +} diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 37b234360c..497e273081 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -8,6 +8,7 @@ #include #include +#include #include void arch_get_xen_caps(xen_capabilities_info_t *info) @@ -55,6 +56,8 @@ void __init noreturn start_xen(unsigned long bootcpu_id, trap_init(); + sbi_init(); + #ifdef CONFIG_SELF_TESTS test_macros_from_bug_h(); #endif From patchwork Wed Jul 24 15:31:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741107 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 73EEFC3DA63 for ; Wed, 24 Jul 2024 15:36:29 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764353.1174796 (Exim 4.92) (envelope-from ) id 1sWe2Q-0005lv-CB; Wed, 24 Jul 2024 15:36:22 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764353.1174796; Wed, 24 Jul 2024 15:36:22 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWe2Q-0005li-91; Wed, 24 Jul 2024 15:36:22 +0000 Received: by outflank-mailman (input) for mailman id 764353; Wed, 24 Jul 2024 15:36:21 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy8-0005Od-Lf for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:56 +0000 Received: from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com [2a00:1450:4864:20::22a]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id dadb5699-49d1-11ef-8776-851b0ebba9a2; Wed, 24 Jul 2024 17:31:54 +0200 (CEST) Received: by mail-lj1-x22a.google.com with SMTP id 38308e7fff4ca-2f032cb782dso9068661fa.3 for ; Wed, 24 Jul 2024 08:31:54 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:52 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: dadb5699-49d1-11ef-8776-851b0ebba9a2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835113; x=1722439913; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=lI5HWGurPenfum8/x5dM5na/gvlX/NTHbcJKhmKmsaw=; b=fjysHqPwsWHfHhBAuFL4TqYpd/nf0wUFQoL6qTmMgV/+cCWqVCBZ0k6vCJ9bpWREDt lIBNaTUTDx3e+MC+kEiI0GVk3sMfCxIbnzTioUsr+gY/cbyagTAW+tqLrouQ25wGpysR 705W8Ua/iCO24Y+JYEiHzYECaVVQ/AANbjR8IjLgrzDADPn/W5R3AYWXc88cD4BrVCsC a101FCKTxM4XlmJLZy+XqXscW6jCaJHic7o15ey4XwdPZgE+enY/2jW1gOkl3btGodk9 nGqFDPUfzO/MjMEW8FPpyWmMHVenNkKueexvtXlK/Q4u4fwRnxcNxp9DFovX+A2lHZ+n Uukw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835113; x=1722439913; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lI5HWGurPenfum8/x5dM5na/gvlX/NTHbcJKhmKmsaw=; b=cl+79nlLPUXeXYImlTKKSNBQtzpQj3uDBIRO6lxoIOw+SGJBVIuKmzuQIC4NnUSfIy Z3DfiWfqXkfRZWvr4f3HfHBt88bmi4Dxy00h8NOoHezra6BEEvbR7hoG5WOC/l2y2a6P r9EOOT39AhLE5ZK6j+MbictQbjArZf7nXmqTW1Av2L5cPOQ79TCbzW5Rk10X1h327J6f 8C1WPrlTfj8kt/WOJQm5ClUcDDs6qAUw6cK9DITDyWPq7PKsjQUT+a8qVRp3gxzgE6pk qvVbyqSAe2p3kd7ZSobAhtCAM3QYafpYr/FSx0miKg8G0YBG1NOsXlzDaGXKe1MrPZUj um1Q== X-Gm-Message-State: AOJu0YzO7PodCjE+VbOd1f6jTrPL0JFm6CkIhX+5erOHJjsYu5C243Cd 9cTuXmEdYjfsdBhz72rTDD1wBQc/7e/8vyHC5V74nwKM/plSOnXTJyMZTIRM X-Google-Smtp-Source: AGHT+IFP73Maz9Gt5qpz141xPSg5oqeenO+HdR2JthQ4dCu0M1ouI1mY9DQ70NdFpK9qTDh6tCawmg== X-Received: by 2002:a2e:9f0f:0:b0:2f0:1fb6:344b with SMTP id 38308e7fff4ca-2f039f3b8d9mr1028491fa.47.1721835113139; Wed, 24 Jul 2024 08:31:53 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 8/9] xen/riscv: page table handling Date: Wed, 24 Jul 2024 17:31:39 +0200 Message-ID: <595c7b6736d6f718bafc7a677fb13881584ce4dc.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Introduces an implementation of map_pages_to_xen() which requires multiple functions to work with page tables/entries: - xen_pt_update() - xen_pt_mapping_level() - xen_pt_update_entry() - xen_pt_next_level() - xen_pt_check_entry() During the mentioned above function it is necessary to create Xen tables or map/unmap them. For that it were introduced the following functions: - create_xen_table() - xen_map_table() - xen_unmap_table() Also it was introduced various internal macros for convience started with _PAGE_* which almost duplicate the bits in PTE except _PAGE_BLOCK which actually doesn't present in PTE which indicates that page larger then 4k is needed. RISC-V doesn't have a specific bit in PTE and it detect if it is a superpage or not only by using pte.x and pte.r. From the RISC-V spec: ``` ... 4. Otherwise, the PTE is valid. If pte.r = 1 or pte.x = 1, go to step 5. Otherwise, this PTE is a pointer to the next level of the page table. ... . 5. A leaf PTE has been found. ... ... ``` Except that it was introduced flush_xen_tlb_range_va() for TLB flushing accross CPUs when PTE for requested mapping was properly updated. Signed-off-by: Oleksii Kurochko --- Changes in V3: - new patch. ( Technically it is reworked version of the generic approach which I tried to suggest in the previous version ) --- xen/arch/riscv/Kconfig | 1 + xen/arch/riscv/Makefile | 1 + xen/arch/riscv/include/asm/flushtlb.h | 15 + xen/arch/riscv/include/asm/mm.h | 2 + xen/arch/riscv/include/asm/page-bits.h | 36 +++ xen/arch/riscv/include/asm/page.h | 73 ++++- xen/arch/riscv/mm.c | 9 - xen/arch/riscv/pt.c | 410 +++++++++++++++++++++++++ 8 files changed, 537 insertions(+), 10 deletions(-) create mode 100644 xen/arch/riscv/pt.c diff --git a/xen/arch/riscv/Kconfig b/xen/arch/riscv/Kconfig index 0112aa8778..9827a12d34 100644 --- a/xen/arch/riscv/Kconfig +++ b/xen/arch/riscv/Kconfig @@ -2,6 +2,7 @@ config RISCV def_bool y select FUNCTION_ALIGNMENT_16B select GENERIC_BUG_FRAME + select GENERIC_PT select HAS_DEVICE_TREE select HAS_PMAP diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index 334fd24547..d058ea4e95 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += entry.o obj-y += mm.o +obj-y += pt.o obj-$(CONFIG_RISCV_64) += riscv64/ obj-y += sbi.o obj-y += setup.o diff --git a/xen/arch/riscv/include/asm/flushtlb.h b/xen/arch/riscv/include/asm/flushtlb.h index cf66e90773..90c65b153f 100644 --- a/xen/arch/riscv/include/asm/flushtlb.h +++ b/xen/arch/riscv/include/asm/flushtlb.h @@ -5,6 +5,8 @@ #include #include +#include + /* Flush TLB of local processor for address va. */ static inline void flush_xen_tlb_one_local(vaddr_t va) { @@ -27,6 +29,19 @@ static inline void flush_xen_tlb_range_va_local(vaddr_t va, } } +/* + * Flush a range of VA's hypervisor mappings from the TLB of all + * processors in the inner-shareable domain. + */ +static inline void flush_xen_tlb_range_va(vaddr_t va, + unsigned long size) +{ + if ( sbi_has_rfence() ) + sbi_remote_sfence_vma(NULL, va, size); + else + BUG_ON("IPI support is need for remote TLB flush"); +} + /* * Filter the given set of CPUs, removing those that definitely flushed their * TLB since @page_timestamp. diff --git a/xen/arch/riscv/include/asm/mm.h b/xen/arch/riscv/include/asm/mm.h index a0bdc2bc3a..a7550e77a7 100644 --- a/xen/arch/riscv/include/asm/mm.h +++ b/xen/arch/riscv/include/asm/mm.h @@ -42,6 +42,8 @@ static inline void *maddr_to_virt(paddr_t ma) #define virt_to_mfn(va) __virt_to_mfn(va) #define mfn_to_virt(mfn) __mfn_to_virt(mfn) +#define pte_get_mfn(pte) maddr_to_mfn(pte_to_paddr(pte)) + struct page_info { /* Each frame can be threaded onto a doubly-linked list. */ diff --git a/xen/arch/riscv/include/asm/page-bits.h b/xen/arch/riscv/include/asm/page-bits.h index 8f1f474371..8e40a607d8 100644 --- a/xen/arch/riscv/include/asm/page-bits.h +++ b/xen/arch/riscv/include/asm/page-bits.h @@ -3,6 +3,42 @@ #ifndef __RISCV_PAGE_BITS_H__ #define __RISCV_PAGE_BITS_H__ +/* + * PTE format: + * | XLEN-1 10 | 9 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * PFN reserved for SW D A G U X W R V + */ + +#define _PAGE_PRESENT BIT(0, UL) +#define _PAGE_READ BIT(1, UL) /* Readable */ +#define _PAGE_WRITE BIT(2, UL) /* Writable */ +#define _PAGE_EXEC BIT(3, UL) /* Executable */ +#define _PAGE_USER BIT(4, UL) /* User */ +#define _PAGE_GLOBAL BIT(5, UL) /* Global */ +#define _PAGE_ACCESSED BIT(6, UL) /* Set by hardware on any access */ +#define _PAGE_DIRTY BIT(7, UL) /* Set by hardware on any write */ +#define _PAGE_SOFT BIT(8, UL) /* Reserved for software */ + +/* + * There is no such bits in PTE format for RISC-V. + * + * _PAGE_BLOCK was introduced to have ability to tell that superpage + * should be allocated. + */ +#define _PAGE_BLOCK BIT(9, UL) + +#define _PAGE_W_BIT 2 +#define _PAGE_XN_BIT 3 +#define _PAGE_RO_BIT 1 + +/* TODO: move to somewhere generic part/header ? */ +#define _PAGE_XN (1U << _PAGE_XN_BIT) +#define _PAGE_RO (1U << _PAGE_RO_BIT) +#define _PAGE_W (1U << _PAGE_W_BIT) +#define PAGE_XN_MASK(x) (((x) >> _PAGE_XN_BIT) & 0x1U) +#define PAGE_RO_MASK(x) (((x) >> _PAGE_RO_BIT) & 0x1U) +#define PAGE_W_MASK(x) (((x) >> _PAGE_W_BIT) & 0x1U) + #define PAGE_SHIFT 12 /* 4 KiB Pages */ #define PADDR_BITS 56 /* 44-bit PPN */ diff --git a/xen/arch/riscv/include/asm/page.h b/xen/arch/riscv/include/asm/page.h index 2308cefb0a..0da52b03a3 100644 --- a/xen/arch/riscv/include/asm/page.h +++ b/xen/arch/riscv/include/asm/page.h @@ -34,6 +34,7 @@ #define PTE_LEAF_DEFAULT (PTE_VALID | PTE_READABLE | PTE_WRITABLE) #define PTE_TABLE (PTE_VALID) +#define PAGE_HYPERVISOR_RO (PTE_VALID | PTE_READABLE) #define PAGE_HYPERVISOR_RW (PTE_VALID | PTE_READABLE | PTE_WRITABLE) #define PAGE_HYPERVISOR PAGE_HYPERVISOR_RW @@ -43,13 +44,68 @@ #define pt_index(lvl, va) (pt_linear_offset((lvl), (va)) & VPN_MASK) -/* Page Table entry */ +#define FIRST_SIZE (XEN_PT_LEVEL_SIZE(2)) + +#define TABLE_OFFSET(offs) (_AT(unsigned int, offs) & ((_AC(1, U) << PAGETABLE_ORDER) - 1)) + +#if RV_STAGE1_MODE > SATP_MODE_SV48 +#error "need to to update DECLARE_OFFSETS macros" +#else + +#define l0_table_offset(va) TABLE_OFFSET(pt_linear_offset(0, va)) +#define l1_table_offset(va) TABLE_OFFSET(pt_linear_offset(1, va)) +#define l2_table_offset(va) TABLE_OFFSET(pt_linear_offset(2, va)) +#define l3_table_offset(va) TABLE_OFFSET(pt_linear_offset(3, va)) + +/* Generate an array @var containing the offset for each level from @addr */ +#define DECLARE_OFFSETS(var, addr) \ + const unsigned int var[4] = { \ + l0_table_offset(addr), \ + l1_table_offset(addr), \ + l2_table_offset(addr), \ + l3_table_offset(addr) \ + } + +#endif + typedef struct { + unsigned long v:1; + unsigned long r:1; + unsigned long w:1; + unsigned long x:1; + unsigned long u:1; + unsigned long g:1; + unsigned long a:1; + unsigned long d:1; + unsigned long rsw:2; +#if RV_STAGE1_MODE == SATP_MODE_SV39 + unsigned long ppn0:9; + unsigned long ppn1:9; + unsigned long ppn2:26; + unsigned long rsw2:7; + unsigned long pbmt:2; + unsigned long n:1; +#elif RV_STAGE1_MODE == SATP_MODE_SV48 + unsigned long ppn0:9; + unsigned long ppn1:9; + unsigned long ppn2:9; + unsigned long ppn3:17; + unsigned long rsw2:7; + unsigned long pbmt:2; + unsigned long n:1; +#else +#error "Add proper bits for SATP_MODE" +#endif +} pt_t; + +/* Page Table entry */ +typedef union { #ifdef CONFIG_RISCV_64 uint64_t pte; #else uint32_t pte; #endif +pt_t bits; } pte_t; pte_t mfn_to_xen_entry(mfn_t mfn, unsigned int access_bits); @@ -70,6 +126,21 @@ static inline bool pte_is_valid(pte_t p) return p.pte & PTE_VALID; } +inline bool pte_is_table(const pte_t p, unsigned int level) +{ + (void) level; + + return (((p.pte) & (PTE_VALID + | PTE_READABLE + | PTE_WRITABLE + | PTE_EXECUTABLE)) == PTE_VALID); +} + +static inline bool pte_is_mapping(const pte_t pte, unsigned int level) +{ + return !pte_is_table(pte, level); +} + static inline void invalidate_icache(void) { BUG_ON("unimplemented"); diff --git a/xen/arch/riscv/mm.c b/xen/arch/riscv/mm.c index 959b6fc63e..ecb0f15fa8 100644 --- a/xen/arch/riscv/mm.c +++ b/xen/arch/riscv/mm.c @@ -374,15 +374,6 @@ int destroy_xen_mappings(unsigned long s, unsigned long e) return -1; } -int map_pages_to_xen(unsigned long virt, - mfn_t mfn, - unsigned long nr_mfns, - unsigned int flags) -{ - BUG_ON("unimplemented"); - return -1; -} - static inline pte_t mfn_from_pte(mfn_t mfn) { unsigned long pte = mfn_x(mfn) << PTE_PPN_SHIFT; diff --git a/xen/arch/riscv/pt.c b/xen/arch/riscv/pt.c new file mode 100644 index 0000000000..e057bc4e98 --- /dev/null +++ b/xen/arch/riscv/pt.c @@ -0,0 +1,410 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static inline const mfn_t get_root_page(void) +{ + unsigned long root_maddr = csr_read(CSR_SATP) << PAGE_SHIFT; + + return maddr_to_mfn(root_maddr); +} + +static inline void set_pte_permissions(pte_t *pte, unsigned int flags) +{ + pte->bits.r = PAGE_RO_MASK(flags); + pte->bits.x = ~PAGE_XN_MASK(flags); + pte->bits.w = PAGE_W_MASK(flags); + + pte->pte |= PTE_ACCESSED | PTE_DIRTY; +} + +/* Sanity check of the entry */ +static bool xen_pt_check_entry(pte_t entry, mfn_t mfn, unsigned int level, + unsigned int flags) +{ + /* Sanity check when modifying an entry. */ + if ( mfn_eq(mfn, INVALID_MFN) ) + { + /* We don't allow modifying an invalid entry. */ + if ( !pte_is_valid(entry) ) + { + printk("Modifying invalid entry is not allowed.\n"); + return false; + } + + /* We don't allow modifying a table entry */ + if ( !pte_is_mapping(entry, level) ) + { + printk("Modifying a table entry is not allowed.\n"); + return false; + } + } + /* Sanity check when inserting a mapping */ + else if ( flags & _PAGE_PRESENT ) + { + /* We should be here with a valid MFN. */ + ASSERT(!mfn_eq(mfn, INVALID_MFN)); + + /* + * We don't allow replacing any valid entry. + * + * Note that the function xen_pt_update() relies on this + * assumption and will skip the TLB flush. The function will need + * to be updated if the check is relaxed. + */ + if ( pte_is_valid(entry) ) + { + if ( pte_is_mapping(entry, level) ) + printk("Changing MFN for a valid entry is not allowed (%#"PRI_mfn" -> %#"PRI_mfn").\n", + mfn_x(pte_get_mfn(entry)), mfn_x(mfn)); + else + printk("Trying to replace a table with a mapping.\n"); + return false; + } + } + /* Sanity check when removing a mapping. */ + else if ( (flags & _PAGE_PRESENT) == 0 ) + { + /* We should be here with an invalid MFN. */ + ASSERT(mfn_eq(mfn, INVALID_MFN)); + + /* We don't allow removing a table */ + if ( pte_is_table(entry, level) ) + { + printk("Removing a table is not allowed.\n"); + return false; + } + } + /* No check so far. */ + else + { + /* We should be here with an invalid MFN */ + ASSERT(mfn_eq(mfn, INVALID_MFN)); + } + + return true; +} + +static pte_t *xen_map_table(mfn_t mfn) +{ + /* + * During early boot, map_domain_page() may be unusable. Use the + * PMAP to map temporarily a page-table. + */ + if ( system_state == SYS_STATE_early_boot ) + return pmap_map(mfn); + + return map_domain_page(mfn); +} + +static void xen_unmap_table(const pte_t *table) +{ + /* + * During early boot, xen_map_table() will not use map_domain_page() + * but the PMAP. + */ + if ( system_state == SYS_STATE_early_boot ) + pmap_unmap(table); + else + unmap_domain_page(table); +} + +static int create_xen_table(pte_t *entry) +{ + mfn_t mfn; + void *p; + pte_t pte; + + if ( system_state != SYS_STATE_early_boot ) + { + struct page_info *pg = alloc_domheap_page(NULL, 0); + + if ( pg == NULL ) + return -ENOMEM; + + mfn = page_to_mfn(pg); + } + else + mfn = alloc_boot_pages(1, 1); + + p = xen_map_table(mfn); + clear_page(p); + xen_unmap_table(p); + + pte = mfn_to_xen_entry(mfn, PTE_TABLE); + write_pte(entry, pte); + + return 0; +} + +#define XEN_TABLE_MAP_FAILED 0 +#define XEN_TABLE_SUPER_PAGE 1 +#define XEN_TABLE_NORMAL_PAGE 2 + +/* + * Take the currently mapped table, find the corresponding entry, + * and map the next table, if available. + * + * The read_only parameters indicates whether intermediate tables should + * be allocated when not present. + * + * Return values: + * XEN_TABLE_MAP_FAILED: Either read_only was set and the entry + * was empty, or allocating a new page failed. + * XEN_TABLE_NORMAL_PAGE: next level mapped normally + * XEN_TABLE_SUPER_PAGE: The next entry points to a superpage. + */ +static int xen_pt_next_level(bool read_only, unsigned int level, + pte_t **table, unsigned int offset) +{ + pte_t *entry; + int ret; + mfn_t mfn; + + entry = *table + offset; + + if ( !pte_is_valid(*entry) ) + { + if ( read_only ) + return XEN_TABLE_MAP_FAILED; + + ret = create_xen_table(entry); + if ( ret ) + return XEN_TABLE_MAP_FAILED; + } + + if ( pte_is_mapping(*entry, level) ) + { + return XEN_TABLE_SUPER_PAGE; + } + + mfn = pte_get_mfn(*entry); + + xen_unmap_table(*table); + *table = xen_map_table(mfn); + + return XEN_TABLE_NORMAL_PAGE; +} + +/* Update an entry at the level @target. */ +static int xen_pt_update_entry(mfn_t root, unsigned long virt, + mfn_t mfn, unsigned int arch_target, + unsigned int flags) +{ + int rc; + unsigned int level = HYP_PT_ROOT_LEVEL; + unsigned int arch_level = level; + unsigned int target = arch_target; + pte_t *table; + /* + * The intermediate page tables are read-only when the MFN is not valid + * This means we either modify permissions or remove an entry. + */ + bool read_only = mfn_eq(mfn, INVALID_MFN); + pte_t pte, *entry; + + /* convenience aliases */ + DECLARE_OFFSETS(offsets, (paddr_t)virt); + + table = xen_map_table(root); + for ( ; level > target; level--, arch_level = level ) + { + rc = xen_pt_next_level(read_only, arch_level, &table, offsets[arch_level]); + if ( rc == XEN_TABLE_MAP_FAILED ) + { + /* + * We are here because xen_pt_next_level has failed to map + * the intermediate page table (e.g the table does not exist + * and the pt is read-only). It is a valid case when + * removing a mapping as it may not exist in the page table. + * In this case, just ignore it. + */ + if ( flags & _PAGE_PRESENT ) + { + printk("%s: Unable to map level %u\n", __func__, arch_level); + rc = -ENOENT; + goto out; + } + else + { + rc = 0; + goto out; + } + } + else if ( rc != XEN_TABLE_NORMAL_PAGE ) { + break; + } + } + + if ( arch_level != arch_target ) + { + printk("%s: Shattering superpage is not supported\n", __func__); + rc = -EOPNOTSUPP; + goto out; + } + + entry = table + offsets[arch_level]; + + rc = -EINVAL; + if ( !xen_pt_check_entry(*entry, mfn, arch_level, flags) ) + goto out; + + /* We are removing the page */ + if ( !(flags & _PAGE_PRESENT) ) + memset(&pte, 0x00, sizeof(pte)); + else + { + /* We are inserting a mapping => Create new pte. */ + if ( !mfn_eq(mfn, INVALID_MFN) ) + { + pte = mfn_to_xen_entry(mfn, PTE_VALID); + } + else /* We are updating the permission => Copy the current pte. */ + pte = *entry; + + set_pte_permissions(&pte, flags); + } + + write_pte(entry, pte); + + rc = 0; + +out: + xen_unmap_table(table); + + return rc; +} + +static DEFINE_SPINLOCK(xen_pt_lock); + +/* Return the level where mapping should be done */ +static int xen_pt_mapping_level(unsigned long vfn, mfn_t mfn, unsigned long nr, + unsigned int flags) +{ + unsigned int level = 0; + unsigned long mask; + int i; + + /* + * Don't take into account the MFN when removing mapping (i.e + * MFN_INVALID) to calculate the correct target order. + * + * `vfn` and `mfn` must be both superpage aligned. + * They are or-ed together and then checked against the size of + * each level. + * + * `left` is not included and checked separately to allow + * superpage mapping even if it is not properly aligned (the + * user may have asked to map 2MB + 4k). + */ + mask = !mfn_eq(mfn, INVALID_MFN) ? mfn_x(mfn) : 0; + mask |= vfn; + + /* + * Always use level 0 ( 4k mapping ) mapping unless the caller request + * block mapping. + */ + if ( likely(!(flags & _PAGE_BLOCK)) ) + return level; + + for ( i = HYP_PT_ROOT_LEVEL; i > 0; i-- ) + { + if ( !(mask & (BIT(XEN_PT_LEVEL_ORDER(i), UL) - 1)) && + (nr >= BIT(XEN_PT_LEVEL_ORDER(i), UL)) ) + { + level = i; + break; + } + } + + return level; +} + +static int xen_pt_update(unsigned long virt, + mfn_t mfn, + /* const on purpose as it is used for TLB flush */ + const unsigned long nr_mfns, + unsigned int flags) +{ + int rc = 0; + unsigned long vfn = virt >> PAGE_SHIFT; + unsigned long left = nr_mfns; + + const mfn_t root = get_root_page(); + + /* + * It is bad idea to have mapping both writeable and + * executable. + * When modifying/creating mapping (i.e _PAGE_PRESENT is set), + * prevent any update if this happen. + */ + if ( (flags & _PAGE_PRESENT) && !PAGE_RO_MASK(flags) && + !PAGE_XN_MASK(flags) ) + { + printk("Mappings should not be both Writeable and Executable.\n"); + return -EINVAL; + } + + if ( !IS_ALIGNED(virt, PAGE_SIZE) ) + { + printk("The virtual address is not aligned to the page-size.\n"); + return -EINVAL; + } + + spin_lock(&xen_pt_lock); + + while ( left ) + { + unsigned int order, level; + + level = xen_pt_mapping_level(vfn, mfn, left, flags); + order = XEN_PT_LEVEL_ORDER(level); + + ASSERT(left >= BIT(order, UL)); + + rc = xen_pt_update_entry(root, vfn << PAGE_SHIFT, mfn, level, + flags); + if ( rc ) + break; + + vfn += 1U << order; + if ( !mfn_eq(mfn, INVALID_MFN) ) + mfn = mfn_add(mfn, 1U << order); + + left -= (1U << order); + + if ( rc ) + break; + } + + /* + * The TLBs flush can be safely skipped when a mapping is inserted + * as we don't allow mapping replacement (see xen_pt_check_entry()). + * + * For all the other cases, the TLBs will be flushed unconditionally + * even if the mapping has failed. This is because we may have + * partially modified the PT. This will prevent any unexpected + * behavior afterwards. + */ + if ( !((flags & _PAGE_PRESENT) && !mfn_eq(mfn, INVALID_MFN)) ) + flush_xen_tlb_range_va(virt, PAGE_SIZE * nr_mfns); + + spin_unlock(&xen_pt_lock); + + return rc; +} + +int map_pages_to_xen(unsigned long virt, + mfn_t mfn, + unsigned long nr_mfns, + unsigned int flags) +{ + return xen_pt_update(virt, mfn, nr_mfns, flags); +} From patchwork Wed Jul 24 15:31:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksii Kurochko X-Patchwork-Id: 13741101 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id B3552C3DA7E for ; Wed, 24 Jul 2024 15:32:03 +0000 (UTC) Received: from list by lists.xenproject.org with outflank-mailman.764298.1174777 (Exim 4.92) (envelope-from ) id 1sWdy9-000152-El; Wed, 24 Jul 2024 15:31:57 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version Received: by outflank-mailman (output) from mailman id 764298.1174777; Wed, 24 Jul 2024 15:31:57 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy9-000148-AB; Wed, 24 Jul 2024 15:31:57 +0000 Received: by outflank-mailman (input) for mailman id 764298; Wed, 24 Jul 2024 15:31:55 +0000 Received: from se1-gles-sth1-in.inumbo.com ([159.253.27.254] helo=se1-gles-sth1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1sWdy7-0005Oj-33 for xen-devel@lists.xenproject.org; Wed, 24 Jul 2024 15:31:55 +0000 Received: from mail-lj1-x232.google.com (mail-lj1-x232.google.com [2a00:1450:4864:20::232]) by se1-gles-sth1.inumbo.com (Halon) with ESMTPS id db1383a3-49d1-11ef-bbfe-fd08da9f4363; Wed, 24 Jul 2024 17:31:54 +0200 (CEST) Received: by mail-lj1-x232.google.com with SMTP id 38308e7fff4ca-2ef1c12ae23so49923951fa.0 for ; Wed, 24 Jul 2024 08:31:54 -0700 (PDT) Received: from fedora.. ([94.75.70.14]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-2ef2b568858sm13237311fa.35.2024.07.24.08.31.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 08:31:53 -0700 (PDT) X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: db1383a3-49d1-11ef-bbfe-fd08da9f4363 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721835114; x=1722439914; darn=lists.xenproject.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=A07ZG27CUr7oIHQ1UgxonLp6epcHXS0L6tvykTUJZJg=; b=AnJUY6WFojC+AmQGEI+GUc3vSX26bDaFMnW+7bIPxXsGBWcTsdlR/HU1EQKeqzakZ8 PTapD2Gf+0fm9mMWD+yb8iLbZS+IUgz6wowA4Cb/XvW6Dbq7IQFVfISU96FAj9yhAXBC rTh+TTuFuqDjTJeSImVqCj07wL0J8HFCY9YQ6kfQt5xe9etn603BmYANOSrmyxkBAAZY jOYlahjSuovUY9wp3Uwv4tAjnSCIW4Yya0jSSKXK7lSTg5Y+/cv+1rxF8Geg47rhFANc aW3Sk1WU0dOQBokBNU8xgs/3bEiRUcepvFWUKmoaYhUu3Mr+BUq5ktxoXqwmbs8D/Sgc eHDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721835114; x=1722439914; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=A07ZG27CUr7oIHQ1UgxonLp6epcHXS0L6tvykTUJZJg=; b=rSoJGIwB1iOK1HYIYtp/ZLqOkMZzAImsbhGWPV4uQ2fKic7BbI/zZ83MOVX+D0ahmF z06z01EfY86OXiCI9UZw+D3JaY/nupQSeFQrZNDPRIv4GLi9M8Go1BRNuZtg7Mh5ByHG zGqw1QBRx22fg89/BDtVhTHhezBgw6Us7iZ9oeJFzPcLfwhK7XthCFzJ4DavaI04OVuz VDhmI3U4stPvkDJ8RkLLGgOSWBioKxILAzaHS7r6FWw+PYicKtQ94h0jq2S096Xs92WR cLuIJS42dgJ7UC1uA5vd0hTfw7yiJxCH3RhwCPcRXTEs1v4oj5BZ8XsiBVxS1SV2/Nvj SrMQ== X-Gm-Message-State: AOJu0Yy64/qZWZaVDOveNvEDuBNj2/HBMNiH/0PqwXqtkFYHj70Wk3yT 5wHRDVZxFVaGDDFXw1j17KVi6OGXSQ1iGx2w5t31d9z/j7cXfhr1A6zyOWNd X-Google-Smtp-Source: AGHT+IHKr9lRaxusHvpeu6vtWMcM0TrPaCtck08FGRlrObEi34V5vGEu0lFgr4d2GB0xW+qdGgCU8w== X-Received: by 2002:a2e:b536:0:b0:2ee:4c2e:3d3b with SMTP id 38308e7fff4ca-2f039c8aaa4mr1691831fa.4.1721835113854; Wed, 24 Jul 2024 08:31:53 -0700 (PDT) From: Oleksii Kurochko To: xen-devel@lists.xenproject.org Cc: Oleksii Kurochko , Alistair Francis , Bob Eshleman , Connor Davis , Andrew Cooper , Jan Beulich , Julien Grall , Stefano Stabellini Subject: [PATCH v3 9/9] xen/riscv: introduce early_fdt_map() Date: Wed, 24 Jul 2024 17:31:40 +0200 Message-ID: <9b1d88b2ad6ba81e925fb4a560a2a2899b5e0b36.1721834549.git.oleksii.kurochko@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: References: MIME-Version: 1.0 Introduce function which allows to map FDT to Xen. Also, initialization of device_tree_flattened happens using early_fdt_map. Signed-off-by: Oleksii Kurochko Acked-by: Jan Beulich --- Changes in V3: - Code style fixes - s/SZ_2M/MB(2) - fix condition to check if early_fdt_map() in setup.c return NULL or not. --- Changes in V2: - rework early_fdt_map to use map_pages_to_xen() - move call early_fdt_map() to C code after MMU is enabled. --- xen/arch/riscv/include/asm/mm.h | 2 ++ xen/arch/riscv/mm.c | 55 +++++++++++++++++++++++++++++++++ xen/arch/riscv/setup.c | 9 ++++++ 3 files changed, 66 insertions(+) diff --git a/xen/arch/riscv/include/asm/mm.h b/xen/arch/riscv/include/asm/mm.h index a7550e77a7..56375bf833 100644 --- a/xen/arch/riscv/include/asm/mm.h +++ b/xen/arch/riscv/include/asm/mm.h @@ -259,4 +259,6 @@ static inline unsigned int arch_get_dma_bitsize(void) void setup_fixmap_mappings(void); +void *early_fdt_map(paddr_t fdt_paddr); + #endif /* _ASM_RISCV_MM_H */ diff --git a/xen/arch/riscv/mm.c b/xen/arch/riscv/mm.c index ecb0f15fa8..3ca2cc1950 100644 --- a/xen/arch/riscv/mm.c +++ b/xen/arch/riscv/mm.c @@ -1,13 +1,16 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +#include #include #include #include #include +#include #include #include #include #include +#include #include #include @@ -388,3 +391,55 @@ inline pte_t mfn_to_xen_entry(mfn_t mfn, unsigned int access_bits) return pte; } + +void * __init early_fdt_map(paddr_t fdt_paddr) +{ + /* We are using 2MB superpage for mapping the FDT */ + paddr_t base_paddr = fdt_paddr & XEN_PT_LEVEL_MAP_MASK(1); + paddr_t offset; + void *fdt_virt; + uint32_t size; + int rc; + + /* + * Check whether the physical FDT address is set and meets the minimum + * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be at + * least 8 bytes so that we always access the magic and size fields + * of the FDT header after mapping the first chunk, double check if + * that is indeed the case. + */ + BUILD_BUG_ON(MIN_FDT_ALIGN < 8); + if ( !fdt_paddr || fdt_paddr % MIN_FDT_ALIGN ) + return NULL; + + /* The FDT is mapped using 2MB superpage */ + BUILD_BUG_ON(BOOT_FDT_VIRT_START % MB(2)); + + rc = map_pages_to_xen(BOOT_FDT_VIRT_START, maddr_to_mfn(base_paddr), + MB(2) >> PAGE_SHIFT, + PAGE_HYPERVISOR_RO | _PAGE_BLOCK); + if ( rc ) + panic("Unable to map the device-tree.\n"); + + offset = fdt_paddr % XEN_PT_LEVEL_SIZE(1); + fdt_virt = (void *)BOOT_FDT_VIRT_START + offset; + + if ( fdt_magic(fdt_virt) != FDT_MAGIC ) + return NULL; + + size = fdt_totalsize(fdt_virt); + if ( size > BOOT_FDT_VIRT_SIZE ) + return NULL; + + if ( (offset + size) > MB(2) ) + { + rc = map_pages_to_xen(BOOT_FDT_VIRT_START + MB(2), + maddr_to_mfn(base_paddr + MB(2)), + MB(2) >> PAGE_SHIFT, + PAGE_HYPERVISOR_RO | _PAGE_BLOCK); + if ( rc ) + panic("Unable to map the device-tree\n"); + } + + return fdt_virt; +} diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 497e273081..e5f32a142a 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -64,6 +65,14 @@ void __init noreturn start_xen(unsigned long bootcpu_id, setup_fixmap_mappings(); + device_tree_flattened = early_fdt_map(dtb_addr); + if ( !device_tree_flattened ) + panic("Invalid device tree blob at physical address %#lx.\n" + "The DTB must be 8-byte aligned and must not exceed %lld " + "bytes in size.\n\n" + "Please check your bootloader.\n", + dtb_addr, BOOT_FDT_VIRT_SIZE); + printk("All set up\n"); for ( ;; )