LoongArch: Add FDT booting support from efi system table
authorBinbin Zhou <zhoubinbin@loongson.cn>
Sat, 10 Dec 2022 14:40:05 +0000 (22:40 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 14 Dec 2022 00:41:53 +0000 (08:41 +0800)
Since commit 40cd01a9c324("efi/loongarch: libstub: remove dependency on
flattened DT"), we can parse the FDT from efi system table.

And now, LoongArch is coming to support booting with FDT, so we add the
relevant booting support as well as parameter parsing.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/Kconfig
arch/loongarch/include/asm/efi.h
arch/loongarch/include/asm/setup.h
arch/loongarch/kernel/acpi.c
arch/loongarch/kernel/efi.c
arch/loongarch/kernel/env.c
arch/loongarch/kernel/numa.c
arch/loongarch/kernel/setup.c
arch/loongarch/kernel/smp.c
arch/loongarch/pci/acpi.c

index 0daf626..48db4b2 100644 (file)
@@ -111,6 +111,8 @@ config LOONGARCH
        select MODULES_USE_ELF_RELA if MODULES
        select NEED_PER_CPU_EMBED_FIRST_CHUNK
        select NEED_PER_CPU_PAGE_FIRST_CHUNK
+       select OF
+       select OF_EARLY_FLATTREE
        select PCI
        select PCI_DOMAINS_GENERIC
        select PCI_ECAM if ACPI
index 174567b..81e5d33 100644 (file)
@@ -9,6 +9,7 @@
 
 void __init efi_init(void);
 void __init efi_runtime_init(void);
+void __init *efi_fdt_pointer(void);
 void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
 
 #define ARCH_EFI_IRQ_FLAGS_MASK  0x00000004  /* Bit 2: CSR.CRMD.IE */
index ca373f8..72ead58 100644 (file)
@@ -13,6 +13,7 @@
 
 extern unsigned long eentry;
 extern unsigned long tlbrentry;
+extern char init_command_line[COMMAND_LINE_SIZE];
 extern void tlb_init(int cpu);
 extern void cpu_cache_init(void);
 extern void cache_error_setup(void);
index 8319cc4..5a63f85 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/memblock.h>
+#include <linux/of_fdt.h>
 #include <linux/serial_core.h>
 #include <asm/io.h>
 #include <asm/numa.h>
@@ -145,14 +146,14 @@ void __init acpi_boot_table_init(void)
         * If acpi_disabled, bail out
         */
        if (acpi_disabled)
-               return;
+               goto fdt_earlycon;
 
        /*
         * Initialize the ACPI boot-time table parser.
         */
        if (acpi_table_init()) {
                disable_acpi();
-               return;
+               goto fdt_earlycon;
        }
 
        loongson_sysconf.boot_cpu_id = read_csr_cpuid();
@@ -164,6 +165,12 @@ void __init acpi_boot_table_init(void)
 
        /* Do not enable ACPI SPCR console by default */
        acpi_parse_spcr(earlycon_acpi_spcr_enable, false);
+
+       return;
+
+fdt_earlycon:
+       if (earlycon_acpi_spcr_enable)
+               early_init_dt_scan_chosen_stdout();
 }
 
 #ifdef CONFIG_ACPI_NUMA
index a313299..ea485b0 100644 (file)
@@ -28,16 +28,29 @@ static unsigned long efi_nr_tables;
 static unsigned long efi_config_table;
 
 static unsigned long __initdata boot_memmap = EFI_INVALID_TABLE_ADDR;
+static unsigned long __initdata fdt_pointer = EFI_INVALID_TABLE_ADDR;
 
 static efi_system_table_t *efi_systab;
 static efi_config_table_type_t arch_tables[] __initdata = {
        {LINUX_EFI_BOOT_MEMMAP_GUID,    &boot_memmap,   "MEMMAP" },
+       {DEVICE_TREE_GUID,              &fdt_pointer,   "FDTPTR" },
        {},
 };
 
+void __init *efi_fdt_pointer(void)
+{
+       if (!efi_systab)
+               return NULL;
+
+       if (fdt_pointer == EFI_INVALID_TABLE_ADDR)
+               return NULL;
+
+       return early_memremap_ro(fdt_pointer, SZ_64K);
+}
+
 void __init efi_runtime_init(void)
 {
-       if (!efi_enabled(EFI_BOOT))
+       if (!efi_enabled(EFI_BOOT) || !efi_systab->runtime)
                return;
 
        if (efi_runtime_disabled()) {
index 6d56a46..6b3bfb0 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/early_ioremap.h>
 #include <asm/bootinfo.h>
 #include <asm/loongson.h>
+#include <asm/setup.h>
 
 u64 efi_system_table;
 struct loongson_system_configuration loongson_sysconf;
@@ -27,6 +28,7 @@ void __init init_environ(void)
                clear_bit(EFI_BOOT, &efi.flags);
 
        strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE);
+       strscpy(init_command_line, cmdline, COMMAND_LINE_SIZE);
        early_memunmap(cmdline, COMMAND_LINE_SIZE);
 
        efi_system_table = fw_arg2;
index a13f925..3019ca1 100644 (file)
@@ -388,6 +388,21 @@ static void __init numa_default_distance(void)
        }
 }
 
+/*
+ * fake_numa_init() - For Non-ACPI systems
+ * Return: 0 on success, -errno on failure.
+ */
+static int __init fake_numa_init(void)
+{
+       phys_addr_t start = memblock_start_of_DRAM();
+       phys_addr_t end = memblock_end_of_DRAM() - 1;
+
+       node_set(0, numa_nodes_parsed);
+       pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end);
+
+       return numa_add_memblk(0, start, end + 1);
+}
+
 int __init init_numa_memory(void)
 {
        int i;
@@ -404,7 +419,7 @@ int __init init_numa_memory(void)
        memset(&numa_meminfo, 0, sizeof(numa_meminfo));
 
        /* Parse SRAT and SLIT if provided by firmware. */
-       ret = acpi_numa_init();
+       ret = acpi_disabled ? fake_numa_init() : acpi_numa_init();
        if (ret < 0)
                return ret;
 
index 53831bc..f9f5a13 100644 (file)
@@ -28,6 +28,9 @@
 #include <linux/sizes.h>
 #include <linux/device.h>
 #include <linux/dma-map-ops.h>
+#include <linux/libfdt.h>
+#include <linux/of_fdt.h>
+#include <linux/of_address.h>
 #include <linux/swiotlb.h>
 
 #include <asm/addrspace.h>
@@ -69,6 +72,7 @@ static const char dmi_empty_string[] = "        ";
  *
  * These are initialized so they are in the .data section
  */
+char init_command_line[COMMAND_LINE_SIZE] __initdata;
 
 static int num_standard_resources;
 static struct resource *standard_resources;
@@ -253,6 +257,58 @@ static void __init arch_parse_crashkernel(void)
 #endif
 }
 
+static void __init fdt_setup(void)
+{
+#ifdef CONFIG_OF_EARLY_FLATTREE
+       void *fdt_pointer;
+
+       /* ACPI-based systems do not require parsing fdt */
+       if (acpi_os_get_root_pointer())
+               return;
+
+       /* Look for a device tree configuration table entry */
+       fdt_pointer = efi_fdt_pointer();
+       if (!fdt_pointer || fdt_check_header(fdt_pointer))
+               return;
+
+       early_init_dt_scan(fdt_pointer);
+       early_init_fdt_reserve_self();
+
+       max_low_pfn = PFN_PHYS(memblock_end_of_DRAM());
+#endif
+}
+
+static void __init bootcmdline_init(char **cmdline_p)
+{
+       /*
+        * If CONFIG_CMDLINE_FORCE is enabled then initializing the command line
+        * is trivial - we simply use the built-in command line unconditionally &
+        * unmodified.
+        */
+       if (IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
+               strscpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+               goto out;
+       }
+
+#ifdef CONFIG_OF_FLATTREE
+       /*
+        * If CONFIG_CMDLINE_BOOTLOADER is enabled and we are in FDT-based system,
+        * the boot_command_line will be overwritten by early_init_dt_scan_chosen().
+        * So we need to append init_command_line (the original copy of boot_command_line)
+        * to boot_command_line.
+        */
+       if (initial_boot_params) {
+               if (boot_command_line[0])
+                       strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+
+               strlcat(boot_command_line, init_command_line, COMMAND_LINE_SIZE);
+       }
+#endif
+
+out:
+       *cmdline_p = boot_command_line;
+}
+
 void __init platform_init(void)
 {
        arch_reserve_vmcore();
@@ -265,6 +321,7 @@ void __init platform_init(void)
        acpi_gbl_use_default_register_widths = false;
        acpi_boot_table_init();
 #endif
+       unflatten_and_copy_device_tree();
 
 #ifdef CONFIG_NUMA
        init_numa_memory();
@@ -297,6 +354,8 @@ static void __init arch_mem_init(char **cmdline_p)
 
        check_kernel_sections_mem();
 
+       early_init_fdt_scan_reserved_mem();
+
        /*
         * In order to reduce the possibility of kernel panic when failed to
         * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
@@ -422,12 +481,13 @@ static void __init prefill_possible_map(void)
 void __init setup_arch(char **cmdline_p)
 {
        cpu_probe();
-       *cmdline_p = boot_command_line;
 
        init_environ();
        efi_init();
+       fdt_setup();
        memblock_init();
        pagetable_init();
+       bootcmdline_init(cmdline_p);
        parse_early_param();
        reserve_initrd_mem();
 
index 14508d4..b78816c 100644 (file)
@@ -180,8 +180,42 @@ irqreturn_t loongson_ipi_interrupt(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
+static void __init fdt_smp_setup(void)
+{
+#ifdef CONFIG_OF
+       unsigned int cpu, cpuid;
+       struct device_node *node = NULL;
+
+       for_each_of_cpu_node(node) {
+               if (!of_device_is_available(node))
+                       continue;
+
+               cpuid = of_get_cpu_hwid(node, 0);
+               if (cpuid >= nr_cpu_ids)
+                       continue;
+
+               if (cpuid == loongson_sysconf.boot_cpu_id) {
+                       cpu = 0;
+                       numa_add_cpu(cpu);
+               } else {
+                       cpu = cpumask_next_zero(-1, cpu_present_mask);
+               }
+
+               num_processors++;
+               set_cpu_possible(cpu, true);
+               set_cpu_present(cpu, true);
+               __cpu_number_map[cpuid] = cpu;
+               __cpu_logical_map[cpu] = cpuid;
+       }
+
+       loongson_sysconf.nr_cpus = num_processors;
+#endif
+}
+
 void __init loongson_smp_setup(void)
 {
+       fdt_smp_setup();
+
        cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package;
        cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
 
index 8235ec9..365f7de 100644 (file)
@@ -26,9 +26,12 @@ void pcibios_add_bus(struct pci_bus *bus)
 
 int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
 {
-       struct pci_config_window *cfg = bridge->bus->sysdata;
-       struct acpi_device *adev = to_acpi_device(cfg->parent);
+       struct acpi_device *adev = NULL;
        struct device *bus_dev = &bridge->bus->dev;
+       struct pci_config_window *cfg = bridge->bus->sysdata;
+
+       if (!acpi_disabled)
+               adev = to_acpi_device(cfg->parent);
 
        ACPI_COMPANION_SET(&bridge->dev, adev);
        set_dev_node(bus_dev, pa_to_nid(cfg->res.start));