2022-02-26 11:17:51

by Huacai Chen

[permalink] [raw]
Subject: [PATCH V6 09/22] LoongArch: Add boot and setup routines

This patch adds basic boot, setup and reset routines for LoongArch.
LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
SMBIOS to pass configuration information to the Linux kernel (in elf
format).

Now the boot information passed to kernel is like this:
1, kernel get 3 register values (a0, a1 and a2) from bootloader.
2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
4, "struct bootparamsinterface" include a "systemtable" pointer, whose
type is "efi_system_table_t". Most configuration information, include
ACPI tables and SMBIOS tables, come from here.

Cc: Ard Biesheuvel <[email protected]>
Cc: [email protected]
Signed-off-by: Huacai Chen <[email protected]>
---
arch/loongarch/include/asm/acenv.h | 17 +
arch/loongarch/include/asm/acpi.h | 38 ++
arch/loongarch/include/asm/boot_param.h | 97 +++++
arch/loongarch/include/asm/bootinfo.h | 33 ++
arch/loongarch/include/asm/dmi.h | 24 ++
arch/loongarch/include/asm/efi.h | 33 ++
arch/loongarch/include/asm/fw.h | 18 +
arch/loongarch/include/asm/reboot.h | 10 +
arch/loongarch/include/asm/setup.h | 21 +
arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
arch/loongarch/kernel/cacheinfo.c | 122 ++++++
arch/loongarch/kernel/cmdline.c | 31 ++
arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
arch/loongarch/kernel/efi.c | 208 ++++++++++
arch/loongarch/kernel/env.c | 176 +++++++++
arch/loongarch/kernel/head.S | 72 ++++
arch/loongarch/kernel/mem.c | 89 +++++
arch/loongarch/kernel/reset.c | 90 +++++
arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
arch/loongarch/kernel/time.c | 220 +++++++++++
arch/loongarch/kernel/topology.c | 13 +
21 files changed, 2450 insertions(+)
create mode 100644 arch/loongarch/include/asm/acenv.h
create mode 100644 arch/loongarch/include/asm/acpi.h
create mode 100644 arch/loongarch/include/asm/boot_param.h
create mode 100644 arch/loongarch/include/asm/bootinfo.h
create mode 100644 arch/loongarch/include/asm/dmi.h
create mode 100644 arch/loongarch/include/asm/efi.h
create mode 100644 arch/loongarch/include/asm/fw.h
create mode 100644 arch/loongarch/include/asm/reboot.h
create mode 100644 arch/loongarch/include/asm/setup.h
create mode 100644 arch/loongarch/kernel/acpi.c
create mode 100644 arch/loongarch/kernel/cacheinfo.c
create mode 100644 arch/loongarch/kernel/cmdline.c
create mode 100644 arch/loongarch/kernel/cpu-probe.c
create mode 100644 arch/loongarch/kernel/efi.c
create mode 100644 arch/loongarch/kernel/env.c
create mode 100644 arch/loongarch/kernel/head.S
create mode 100644 arch/loongarch/kernel/mem.c
create mode 100644 arch/loongarch/kernel/reset.c
create mode 100644 arch/loongarch/kernel/setup.c
create mode 100644 arch/loongarch/kernel/time.c
create mode 100644 arch/loongarch/kernel/topology.c

diff --git a/arch/loongarch/include/asm/acenv.h b/arch/loongarch/include/asm/acenv.h
new file mode 100644
index 000000000000..290a15a41258
--- /dev/null
+++ b/arch/loongarch/include/asm/acenv.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * LoongArch specific ACPICA environments and implementation
+ *
+ * Author: Jianmin Lv <[email protected]>
+ * Huacai Chen <[email protected]>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_LOONGARCH_ACENV_H
+#define _ASM_LOONGARCH_ACENV_H
+
+/* The head file is required by ACPI core, but we have nothing to fill
+ * it now, update it later when needed.
+ */
+
+#endif /* _ASM_LOONGARCH_ACENV_H */
diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h
new file mode 100644
index 000000000000..62044cd5b7bc
--- /dev/null
+++ b/arch/loongarch/include/asm/acpi.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Jianmin Lv <[email protected]>
+ * Huacai Chen <[email protected]>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_LOONGARCH_ACPI_H
+#define _ASM_LOONGARCH_ACPI_H
+
+#ifdef CONFIG_ACPI
+extern int acpi_strict;
+extern int acpi_disabled;
+extern int acpi_pci_disabled;
+extern int acpi_noirq;
+
+#define acpi_os_ioremap acpi_os_ioremap
+void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size);
+
+static inline void disable_acpi(void)
+{
+ acpi_disabled = 1;
+ acpi_pci_disabled = 1;
+ acpi_noirq = 1;
+}
+
+static inline bool acpi_has_cpu_in_madt(void)
+{
+ return true;
+}
+
+extern struct list_head acpi_wakeup_device_list;
+
+#endif /* !CONFIG_ACPI */
+
+#define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
+
+#endif /* _ASM_LOONGARCH_ACPI_H */
diff --git a/arch/loongarch/include/asm/boot_param.h b/arch/loongarch/include/asm/boot_param.h
new file mode 100644
index 000000000000..cae274b7aa14
--- /dev/null
+++ b/arch/loongarch/include/asm/boot_param.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_BOOT_PARAM_H_
+#define __ASM_BOOT_PARAM_H_
+
+#ifdef CONFIG_VT
+#include <linux/screen_info.h>
+#endif
+
+#define ADDRESS_TYPE_SYSRAM 1
+#define ADDRESS_TYPE_RESERVED 2
+#define ADDRESS_TYPE_ACPI 3
+#define ADDRESS_TYPE_NVS 4
+#define ADDRESS_TYPE_PMEM 5
+
+#define EFI_RUNTIME_MAP_START 100
+#define LOONGSON3_BOOT_MEM_MAP_MAX 128
+
+#define LOONGSON_EFIBOOT_SIGNATURE "BPI"
+#define LOONGSON_MEM_SIGNATURE "MEM"
+#define LOONGSON_VBIOS_SIGNATURE "VBIOS"
+#define LOONGSON_SCREENINFO_SIGNATURE "SINFO"
+
+/* Values for Version BPI */
+enum bpi_version {
+ BPI_VERSION_V1 = 1000, /* Signature="BPI01000" */
+ BPI_VERSION_V2 = 1001, /* Signature="BPI01001" */
+ BPI_VERSION_V3 = 1002, /* Signature="BPI01002" */
+};
+
+/* Flags in bootparamsinterface */
+#define BPI_FLAGS_UEFI_SUPPORTED BIT(0)
+
+struct _extention_list_hdr {
+ u64 signature;
+ u32 length;
+ u8 revision;
+ u8 checksum;
+ u64 next_offset;
+} __packed;
+
+struct boot_params {
+ u64 signature; /* {"B", "P", "I", "0", "1", ... } */
+ void *systemtable;
+ u64 extlist_offset;
+ u64 flags;
+} __packed;
+
+struct loongsonlist_mem_map {
+ struct _extention_list_hdr header; /* {"M", "E", "M"} */
+ u8 map_count;
+ u32 desc_version;
+ struct efi_mmap {
+ u32 mem_type;
+ u32 padding;
+ u64 mem_start;
+ u64 mem_vaddr;
+ u64 mem_size;
+ u64 attribute;
+ } __packed map[LOONGSON3_BOOT_MEM_MAP_MAX];
+} __packed;
+
+struct loongsonlist_vbios {
+ struct _extention_list_hdr header; /* {"V", "B", "I", "O", "S"} */
+ u64 vbios_addr;
+} __packed;
+
+struct loongsonlist_screeninfo {
+ struct _extention_list_hdr header; /* {"S", "I", "N", "F", "O"} */
+ struct screen_info si;
+} __packed;
+
+struct loongson_board_info {
+ int bios_size;
+ char *bios_vendor;
+ char *bios_version;
+ char *bios_release_date;
+ char *board_name;
+ char *board_vendor;
+};
+
+struct loongson_system_configuration {
+ int bpi_ver;
+ int nr_cpus;
+ int nr_nodes;
+ int nr_io_pics;
+ int boot_cpu_id;
+ int cores_per_node;
+ int cores_per_package;
+ char *cpuname;
+ u64 vgabios_addr;
+};
+
+extern struct boot_params *efi_bp;
+extern struct loongson_board_info b_info;
+extern struct loongsonlist_mem_map *loongson_mem_map;
+extern struct loongson_system_configuration loongson_sysconf;
+#endif
diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h
new file mode 100644
index 000000000000..a0ef53c78ba0
--- /dev/null
+++ b/arch/loongarch/include/asm/bootinfo.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_BOOTINFO_H
+#define _ASM_BOOTINFO_H
+
+#include <linux/types.h>
+#include <asm/setup.h>
+
+const char *get_system_type(void);
+
+extern void early_memblock_init(void);
+extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max);
+
+extern void early_init(void);
+extern void platform_init(void);
+
+extern void free_init_pages(const char *what, unsigned long begin, unsigned long end);
+
+/*
+ * Initial kernel command line, usually setup by fw_init_cmdline()
+ */
+extern char arcs_cmdline[COMMAND_LINE_SIZE];
+
+/*
+ * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware
+ */
+extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3;
+
+extern unsigned long initrd_start, initrd_end;
+
+#endif /* _ASM_BOOTINFO_H */
diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h
new file mode 100644
index 000000000000..d2d4b89624f8
--- /dev/null
+++ b/arch/loongarch/include/asm/dmi.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_DMI_H
+#define _ASM_DMI_H
+
+#include <linux/io.h>
+#include <linux/memblock.h>
+
+#define dmi_early_remap(x, l) dmi_remap(x, l)
+#define dmi_early_unmap(x, l) dmi_unmap(x)
+#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE)
+
+static inline void *dmi_remap(u64 phys_addr, unsigned long size)
+{
+ return ((void *)TO_CAC(phys_addr));
+}
+
+static inline void dmi_unmap(void *addr)
+{
+}
+
+#endif /* _ASM_DMI_H */
diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h
new file mode 100644
index 000000000000..d5bf2e29ea31
--- /dev/null
+++ b/arch/loongarch/include/asm/efi.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_LOONGARCH_EFI_H
+#define _ASM_LOONGARCH_EFI_H
+
+#include <linux/efi.h>
+
+extern void __init efi_init(void);
+extern void __init efi_runtime_init(void);
+extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
+
+#define ARCH_EFI_IRQ_FLAGS_MASK 0x00000001 /*bit0: CP0 Status.IE*/
+
+#define arch_efi_call_virt_setup() \
+({ \
+})
+
+#define arch_efi_call_virt(p, f, args...) \
+({ \
+ efi_##f##_t * __f; \
+ __f = p->f; \
+ __f(args); \
+})
+
+#define arch_efi_call_virt_teardown() \
+({ \
+})
+
+#define EFI_ALLOC_ALIGN SZ_16K
+
+#endif /* _ASM_LOONGARCH_EFI_H */
diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h
new file mode 100644
index 000000000000..c1c3384630c2
--- /dev/null
+++ b/arch/loongarch/include/asm/fw.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_FW_H_
+#define __ASM_FW_H_
+
+#include <asm/bootinfo.h>
+
+extern int fw_argc;
+extern long *_fw_argv, *_fw_envp;
+
+#define fw_argv(index) ((char *)TO_CAC((long)_fw_argv[(index)]))
+#define fw_envp(index) ((char *)TO_CAC((long)_fw_envp[(index)]))
+
+extern void fw_init_cmdline(void);
+
+#endif /* __ASM_FW_H_ */
diff --git a/arch/loongarch/include/asm/reboot.h b/arch/loongarch/include/asm/reboot.h
new file mode 100644
index 000000000000..51151749d8f0
--- /dev/null
+++ b/arch/loongarch/include/asm/reboot.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_REBOOT_H
+#define _ASM_REBOOT_H
+
+extern void (*pm_restart)(void);
+
+#endif /* _ASM_REBOOT_H */
diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
new file mode 100644
index 000000000000..6d7d2a3e23dd
--- /dev/null
+++ b/arch/loongarch/include/asm/setup.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#ifndef _LOONGARCH_SETUP_H
+#define _LOONGARCH_SETUP_H
+
+#include <linux/types.h>
+#include <uapi/asm/setup.h>
+
+#define VECSIZE 0x200
+
+extern unsigned long eentry;
+extern unsigned long tlbrentry;
+extern void cpu_cache_init(void);
+extern void per_cpu_trap_init(int cpu);
+extern void set_handler(unsigned long offset, void *addr, unsigned long len);
+extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
+
+#endif /* __SETUP_H */
diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
new file mode 100644
index 000000000000..3f2101fd19bd
--- /dev/null
+++ b/arch/loongarch/kernel/acpi.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * acpi.c - Architecture-Specific Low-Level ACPI Boot Support
+ *
+ * Author: Jianmin Lv <[email protected]>
+ * Huacai Chen <[email protected]>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/memblock.h>
+#include <linux/serial_core.h>
+#include <asm/io.h>
+#include <asm/loongson.h>
+
+int acpi_disabled;
+EXPORT_SYMBOL(acpi_disabled);
+int acpi_noirq;
+int acpi_pci_disabled;
+EXPORT_SYMBOL(acpi_pci_disabled);
+int acpi_strict = 1; /* We have no workarounds on LoongArch */
+int num_processors;
+int disabled_cpus;
+enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
+
+u64 acpi_saved_sp;
+
+#define MAX_CORE_PIC 256
+
+#define PREFIX "ACPI: "
+
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
+{
+ if (irqp != NULL)
+ *irqp = acpi_register_gsi(NULL, gsi, -1, -1);
+ return (*irqp >= 0) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+
+int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
+{
+ if (gsi)
+ *gsi = isa_irq;
+ return 0;
+}
+
+/*
+ * success: return IRQ number (>=0)
+ * failure: return < 0
+ */
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
+{
+ int id;
+ struct irq_fwspec fwspec;
+
+ switch (gsi) {
+ case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
+ fwspec.fwnode = liointc_domain->fwnode;
+ fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
+ fwspec.param_count = 1;
+
+ return irq_create_fwspec_mapping(&fwspec);
+
+ case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
+ if (!pch_lpc_domain)
+ return -EINVAL;
+
+ fwspec.fwnode = pch_lpc_domain->fwnode;
+ fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
+ fwspec.param_count = 2;
+
+ return irq_create_fwspec_mapping(&fwspec);
+
+ case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
+ id = find_pch_pic(gsi);
+ if (id < 0)
+ return -EINVAL;
+
+ fwspec.fwnode = pch_pic_domain[id]->fwnode;
+ fwspec.param[0] = gsi - acpi_pchpic[id]->gsi_base;
+ fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+ fwspec.param_count = 2;
+
+ return irq_create_fwspec_mapping(&fwspec);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_register_gsi);
+
+void acpi_unregister_gsi(u32 gsi)
+{
+
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
+{
+
+ if (!phys || !size)
+ return NULL;
+
+ return early_memremap(phys, size);
+}
+void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
+{
+ if (!map || !size)
+ return;
+
+ early_memunmap(map, size);
+}
+
+void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
+{
+ if (!memblock_is_memory(phys))
+ return ioremap(phys, size);
+ else
+ return ioremap_cache(phys, size);
+}
+
+void __init acpi_boot_table_init(void)
+{
+ /*
+ * If acpi_disabled, bail out
+ */
+ if (acpi_disabled)
+ return;
+
+ /*
+ * Initialize the ACPI boot-time table parser.
+ */
+ if (acpi_table_init()) {
+ disable_acpi();
+ return;
+ }
+}
+
+static int __init
+acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end)
+{
+ struct acpi_madt_core_pic *processor = NULL;
+
+ processor = (struct acpi_madt_core_pic *)header;
+ if (BAD_MADT_ENTRY(processor, end))
+ return -EINVAL;
+
+ acpi_table_print_madt_entry(&header->common);
+
+ return 0;
+}
+
+static int __init
+acpi_parse_liointc(union acpi_subtable_headers *header, const unsigned long end)
+{
+ struct acpi_madt_lio_pic *liointc = NULL;
+
+ liointc = (struct acpi_madt_lio_pic *)header;
+
+ if (BAD_MADT_ENTRY(liointc, end))
+ return -EINVAL;
+
+ acpi_liointc = liointc;
+
+ return 0;
+}
+
+static int __init
+acpi_parse_eiointc(union acpi_subtable_headers *header, const unsigned long end)
+{
+ static int id = 0;
+ struct acpi_madt_eio_pic *eiointc = NULL;
+
+ eiointc = (struct acpi_madt_eio_pic *)header;
+
+ if (BAD_MADT_ENTRY(eiointc, end))
+ return -EINVAL;
+
+ acpi_eiointc[id++] = eiointc;
+ loongson_sysconf.nr_io_pics = id;
+
+ return 0;
+}
+
+static int __init
+acpi_parse_htintc(union acpi_subtable_headers *header, const unsigned long end)
+{
+ struct acpi_madt_ht_pic *htintc = NULL;
+
+ htintc = (struct acpi_madt_ht_pic *)header;
+
+ if (BAD_MADT_ENTRY(htintc, end))
+ return -EINVAL;
+
+ acpi_htintc = htintc;
+ loongson_sysconf.nr_io_pics = 1;
+
+ return 0;
+}
+
+static int __init
+acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end)
+{
+ static int id = 0;
+ struct acpi_madt_bio_pic *pchpic = NULL;
+
+ pchpic = (struct acpi_madt_bio_pic *)header;
+
+ if (BAD_MADT_ENTRY(pchpic, end))
+ return -EINVAL;
+
+ acpi_pchpic[id++] = pchpic;
+
+ return 0;
+}
+
+static int __init
+acpi_parse_pch_msi(union acpi_subtable_headers *header, const unsigned long end)
+{
+ static int id = 0;
+ struct acpi_madt_msi_pic *pchmsi = NULL;
+
+ pchmsi = (struct acpi_madt_msi_pic *)header;
+
+ if (BAD_MADT_ENTRY(pchmsi, end))
+ return -EINVAL;
+
+ acpi_pchmsi[id++] = pchmsi;
+
+ return 0;
+}
+
+static int __init
+acpi_parse_pch_lpc(union acpi_subtable_headers *header, const unsigned long end)
+{
+ struct acpi_madt_lpc_pic *pchlpc = NULL;
+
+ pchlpc = (struct acpi_madt_lpc_pic *)header;
+
+ if (BAD_MADT_ENTRY(pchlpc, end))
+ return -EINVAL;
+
+ acpi_pchlpc = pchlpc;
+
+ return 0;
+}
+
+static void __init acpi_process_madt(void)
+{
+ int error;
+
+ /* Parse MADT CPUINTC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (CPUINTC entries), ACPI disabled\n");
+ return;
+ }
+
+ loongson_sysconf.nr_cpus = num_processors;
+
+ /* Parse MADT LIOINTC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, acpi_parse_liointc, 1);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (LIOINTC entries), ACPI disabled\n");
+ return;
+ }
+
+ /* Parse MADT EIOINTC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eiointc, MAX_IO_PICS);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (EIOINTC entries), ACPI disabled\n");
+ return;
+ }
+
+ /* Parse MADT HTVEC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, acpi_parse_htintc, 1);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (HTVEC entries), ACPI disabled\n");
+ return;
+ }
+
+ /* Parse MADT PCHPIC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, acpi_parse_pch_pic, MAX_IO_PICS);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (PCHPIC entries), ACPI disabled\n");
+ return;
+ }
+
+ /* Parse MADT PCHMSI entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, acpi_parse_pch_msi, MAX_IO_PICS);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (PCHMSI entries), ACPI disabled\n");
+ return;
+ }
+
+ /* Parse MADT PCHLPC entries */
+ error = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, acpi_parse_pch_lpc, 1);
+ if (error < 0) {
+ disable_acpi();
+ pr_err(PREFIX "Invalid BIOS MADT (PCHLPC entries), ACPI disabled\n");
+ return;
+ }
+}
+
+int __init acpi_boot_init(void)
+{
+ /*
+ * If acpi_disabled, bail out
+ */
+ if (acpi_disabled)
+ return -1;
+
+ loongson_sysconf.boot_cpu_id = read_csr_cpuid();
+
+ /*
+ * Process the Multiple APIC Description Table (MADT), if present
+ */
+ acpi_process_madt();
+
+ /* Do not enable ACPI SPCR console by default */
+ acpi_parse_spcr(earlycon_acpi_spcr_enable, false);
+
+ return 0;
+}
+
+void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
+{
+ memblock_mark_nomap(addr, size);
+}
diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c
new file mode 100644
index 000000000000..8c9fe29e98f0
--- /dev/null
+++ b/arch/loongarch/kernel/cacheinfo.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LoongArch cacheinfo support
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/cacheinfo.h>
+
+/* Populates leaf and increments to next leaf */
+#define populate_cache(cache, leaf, c_level, c_type) \
+do { \
+ leaf->type = c_type; \
+ leaf->level = c_level; \
+ leaf->coherency_line_size = c->cache.linesz; \
+ leaf->number_of_sets = c->cache.sets; \
+ leaf->ways_of_associativity = c->cache.ways; \
+ leaf->size = c->cache.linesz * c->cache.sets * \
+ c->cache.ways; \
+ leaf++; \
+} while (0)
+
+int init_cache_level(unsigned int cpu)
+{
+ struct cpuinfo_loongarch *c = &current_cpu_data;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ int levels = 0, leaves = 0;
+
+ /*
+ * If Dcache is not set, we assume the cache structures
+ * are not properly initialized.
+ */
+ if (c->dcache.waysize)
+ levels += 1;
+ else
+ return -ENOENT;
+
+
+ leaves += (c->icache.waysize) ? 2 : 1;
+
+ if (c->vcache.waysize) {
+ levels++;
+ leaves++;
+ }
+
+ if (c->scache.waysize) {
+ levels++;
+ leaves++;
+ }
+
+ if (c->tcache.waysize) {
+ levels++;
+ leaves++;
+ }
+
+ this_cpu_ci->num_levels = levels;
+ this_cpu_ci->num_leaves = leaves;
+ return 0;
+}
+
+static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
+ struct cacheinfo *sib_leaf)
+{
+ return !((this_leaf->level == 1) || (this_leaf->level == 2));
+}
+
+static void cache_cpumap_setup(unsigned int cpu)
+{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *this_leaf, *sib_leaf;
+ unsigned int index;
+
+ for (index = 0; index < this_cpu_ci->num_leaves; index++) {
+ unsigned int i;
+
+ this_leaf = this_cpu_ci->info_list + index;
+ /* skip if shared_cpu_map is already populated */
+ if (!cpumask_empty(&this_leaf->shared_cpu_map))
+ continue;
+
+ cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
+ for_each_online_cpu(i) {
+ struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
+
+ if (i == cpu || !sib_cpu_ci->info_list)
+ continue;/* skip if itself or no cacheinfo */
+ sib_leaf = sib_cpu_ci->info_list + index;
+ if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
+ cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
+ cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
+ }
+ }
+ }
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+ int level = 1;
+ struct cpuinfo_loongarch *c = &current_cpu_data;
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+ struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+
+ if (c->icache.waysize) {
+ populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA);
+ populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST);
+ } else {
+ populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+ }
+
+ if (c->vcache.waysize)
+ populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+ if (c->scache.waysize)
+ populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+ if (c->tcache.waysize)
+ populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+ cache_cpumap_setup(cpu);
+ this_cpu_ci->cpu_map_populated = true;
+
+ return 0;
+}
diff --git a/arch/loongarch/kernel/cmdline.c b/arch/loongarch/kernel/cmdline.c
new file mode 100644
index 000000000000..a748214e0f2c
--- /dev/null
+++ b/arch/loongarch/kernel/cmdline.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <linux/string.h>
+
+#include <asm/addrspace.h>
+#include <asm/early_ioremap.h>
+#include <asm/fw.h>
+
+int fw_argc;
+long *_fw_argv, *_fw_envp;
+
+void __init fw_init_cmdline(void)
+{
+ int i;
+
+ fw_argc = fw_arg0;
+ _fw_argv = (long *)early_memremap_ro(fw_arg1, SZ_16K);
+ _fw_envp = (long *)early_memremap_ro(fw_arg2, SZ_64K);
+
+ arcs_cmdline[0] = '\0';
+ for (i = 1; i < fw_argc; i++) {
+ strlcat(arcs_cmdline, fw_argv(i), COMMAND_LINE_SIZE);
+ if (i < (fw_argc - 1))
+ strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE);
+ }
+}
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
new file mode 100644
index 000000000000..ea591fe747bd
--- /dev/null
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Processor capabilities determination functions.
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/uaccess.h>
+
+#include <asm/cpu-features.h>
+#include <asm/elf.h>
+#include <asm/fpu.h>
+#include <asm/loongarch.h>
+#include <asm/pgtable-bits.h>
+#include <asm/setup.h>
+
+/* Hardware capabilities */
+unsigned int elf_hwcap __read_mostly;
+EXPORT_SYMBOL_GPL(elf_hwcap);
+
+/*
+ * Determine the FCSR mask for FPU hardware.
+ */
+static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c)
+{
+ unsigned long sr, mask, fcsr, fcsr0, fcsr1;
+
+ fcsr = c->fpu_csr0;
+ mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
+
+ sr = read_csr_euen();
+ enable_fpu();
+
+ fcsr0 = fcsr & mask;
+ write_fcsr(LOONGARCH_FCSR0, fcsr0);
+ fcsr0 = read_fcsr(LOONGARCH_FCSR0);
+
+ fcsr1 = fcsr | ~mask;
+ write_fcsr(LOONGARCH_FCSR0, fcsr1);
+ fcsr1 = read_fcsr(LOONGARCH_FCSR0);
+
+ write_fcsr(LOONGARCH_FCSR0, fcsr);
+
+ write_csr_euen(sr);
+
+ c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask;
+}
+
+static inline void set_elf_platform(int cpu, const char *plat)
+{
+ if (cpu == 0)
+ __elf_platform = plat;
+}
+
+/* MAP BASE */
+unsigned long vm_map_base;
+EXPORT_SYMBOL_GPL(vm_map_base);
+
+static void cpu_probe_addrbits(struct cpuinfo_loongarch *c)
+{
+#ifdef __NEED_ADDRBITS_PROBE
+ c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4;
+ c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12;
+ vm_map_base = 0UL - (1UL << c->vabits);
+#endif
+}
+
+static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
+{
+ switch (isa) {
+ case LOONGARCH_CPU_ISA_LA64:
+ c->isa_level |= LOONGARCH_CPU_ISA_LA64;
+ fallthrough;
+ case LOONGARCH_CPU_ISA_LA32S:
+ c->isa_level |= LOONGARCH_CPU_ISA_LA32S;
+ fallthrough;
+ case LOONGARCH_CPU_ISA_LA32R:
+ c->isa_level |= LOONGARCH_CPU_ISA_LA32R;
+ break;
+ }
+}
+
+static void cpu_probe_common(struct cpuinfo_loongarch *c)
+{
+ unsigned int config;
+ unsigned long asid_mask;
+
+ c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR |
+ LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH;
+
+ elf_hwcap |= HWCAP_LOONGARCH_CRC32;
+
+ config = read_cpucfg(LOONGARCH_CPUCFG1);
+ if (config & CPUCFG1_UAL) {
+ c->options |= LOONGARCH_CPU_UAL;
+ elf_hwcap |= HWCAP_LOONGARCH_UAL;
+ }
+
+ config = read_cpucfg(LOONGARCH_CPUCFG2);
+ if (config & CPUCFG2_LAM) {
+ c->options |= LOONGARCH_CPU_LAM;
+ elf_hwcap |= HWCAP_LOONGARCH_LAM;
+ }
+ if (config & CPUCFG2_FP) {
+ c->options |= LOONGARCH_CPU_FPU;
+ elf_hwcap |= HWCAP_LOONGARCH_FPU;
+ }
+ if (config & CPUCFG2_COMPLEX) {
+ c->options |= LOONGARCH_CPU_COMPLEX;
+ elf_hwcap |= HWCAP_LOONGARCH_COMPLEX;
+ }
+ if (config & CPUCFG2_CRYPTO) {
+ c->options |= LOONGARCH_CPU_CRYPTO;
+ elf_hwcap |= HWCAP_LOONGARCH_CRYPTO;
+ }
+ if (config & CPUCFG2_LVZP) {
+ c->options |= LOONGARCH_CPU_LVZ;
+ elf_hwcap |= HWCAP_LOONGARCH_LVZ;
+ }
+
+ config = read_cpucfg(LOONGARCH_CPUCFG6);
+ if (config & CPUCFG6_PMP)
+ c->options |= LOONGARCH_CPU_PMP;
+
+ config = iocsr_readl(LOONGARCH_IOCSR_FEATURES);
+ if (config & IOCSRF_CSRIPI)
+ c->options |= LOONGARCH_CPU_CSRIPI;
+ if (config & IOCSRF_EXTIOI)
+ c->options |= LOONGARCH_CPU_EXTIOI;
+ if (config & IOCSRF_FREQSCALE)
+ c->options |= LOONGARCH_CPU_SCALEFREQ;
+ if (config & IOCSRF_FLATMODE)
+ c->options |= LOONGARCH_CPU_FLATMODE;
+ if (config & IOCSRF_EIODECODE)
+ c->options |= LOONGARCH_CPU_EIODECODE;
+ if (config & IOCSRF_VM)
+ c->options |= LOONGARCH_CPU_HYPERVISOR;
+
+ config = csr_readl(LOONGARCH_CSR_ASID);
+ config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
+ asid_mask = GENMASK(config - 1, 0);
+ set_cpu_asid_mask(c, asid_mask);
+
+ config = read_csr_prcfg1();
+ c->kscratch_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0);
+ c->kscratch_mask &= ~(EXC_KSCRATCH_MASK | PERCPU_KSCRATCH_MASK | KVM_KSCRATCH_MASK);
+
+ config = read_csr_prcfg3();
+ switch (config & CSR_CONF3_TLBTYPE) {
+ case 0:
+ c->tlbsizemtlb = 0;
+ c->tlbsizestlbsets = 0;
+ c->tlbsizestlbways = 0;
+ c->tlbsize = 0;
+ break;
+ case 1:
+ c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
+ c->tlbsizestlbsets = 0;
+ c->tlbsizestlbways = 0;
+ c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
+ break;
+ case 2:
+ c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
+ c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT);
+ c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1;
+ c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
+ break;
+ default:
+ pr_warn("Warning: unimplemented tlb type\n");
+ }
+}
+
+#define MAX_NAME_LEN 32
+#define VENDOR_OFFSET 0
+#define CPUNAME_OFFSET 9
+
+static char cpu_full_name[MAX_NAME_LEN] = " - ";
+
+static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu)
+{
+ uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]);
+ uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]);
+
+ __cpu_full_name[cpu] = cpu_full_name;
+ *vendor = iocsr_readq(LOONGARCH_IOCSR_VENDOR);
+ *cpuname = iocsr_readq(LOONGARCH_IOCSR_CPUNAME);
+
+ switch (c->processor_id & PRID_IMP_MASK) {
+ case PRID_IMP_LOONGSON_32:
+ c->cputype = CPU_LOONGSON32;
+ set_isa(c, LOONGARCH_CPU_ISA_LA32S);
+ __cpu_family[cpu] = "Loongson-32bit";
+ pr_info("Standard 32-bit Loongson Processor probed\n");
+ break;
+ case PRID_IMP_LOONGSON_64R:
+ c->cputype = CPU_LOONGSON64;
+ set_isa(c, LOONGARCH_CPU_ISA_LA64);
+ __cpu_family[cpu] = "Loongson-64bit";
+ pr_info("Reduced 64-bit Loongson Processor probed\n");
+ break;
+ case PRID_IMP_LOONGSON_64C:
+ c->cputype = CPU_LOONGSON64;
+ set_isa(c, LOONGARCH_CPU_ISA_LA64);
+ __cpu_family[cpu] = "Loongson-64bit";
+ pr_info("Classic 64-bit Loongson Processor probed\n");
+ break;
+ case PRID_IMP_LOONGSON_64G:
+ c->cputype = CPU_LOONGSON64;
+ set_isa(c, LOONGARCH_CPU_ISA_LA64);
+ __cpu_family[cpu] = "Loongson-64bit";
+ pr_info("Generic 64-bit Loongson Processor probed\n");
+ break;
+ default: /* Default to 64 bit */
+ c->cputype = CPU_LOONGSON64;
+ set_isa(c, LOONGARCH_CPU_ISA_LA64);
+ __cpu_family[cpu] = "Loongson-64bit";
+ pr_info("Unknown 64-bit Loongson Processor probed\n");
+ }
+}
+
+#ifdef CONFIG_64BIT
+/* For use by uaccess.h */
+u64 __ua_limit;
+EXPORT_SYMBOL(__ua_limit);
+#endif
+
+const char *__cpu_family[NR_CPUS];
+const char *__cpu_full_name[NR_CPUS];
+const char *__elf_platform;
+
+static void cpu_report(void)
+{
+ struct cpuinfo_loongarch *c = &current_cpu_data;
+
+ pr_info("CPU%d revision is: %08x (%s)\n",
+ smp_processor_id(), c->processor_id, cpu_family_string());
+ if (c->options & LOONGARCH_CPU_FPU)
+ pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers);
+}
+
+void cpu_probe(void)
+{
+ unsigned int cpu = smp_processor_id();
+ struct cpuinfo_loongarch *c = &current_cpu_data;
+
+ /*
+ * Set a default elf platform, cpu probe may later
+ * overwrite it with a more precise value
+ */
+ set_elf_platform(cpu, "loongarch");
+
+ c->cputype = CPU_UNKNOWN;
+ c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0);
+ c->fpu_vers = (read_cpucfg(LOONGARCH_CPUCFG2) >> 3) & 0x3;
+
+ c->fpu_csr0 = FPU_CSR_RN;
+ c->fpu_mask = FPU_CSR_RSVD;
+
+ cpu_probe_common(c);
+
+ per_cpu_trap_init(cpu);
+
+ switch (c->processor_id & PRID_COMP_MASK) {
+ case PRID_COMP_LOONGSON:
+ cpu_probe_loongson(c, cpu);
+ break;
+ }
+
+ BUG_ON(!__cpu_family[cpu]);
+ BUG_ON(c->cputype == CPU_UNKNOWN);
+
+ cpu_probe_addrbits(c);
+
+#ifdef CONFIG_64BIT
+ if (cpu == 0)
+ __ua_limit = ~((1ull << cpu_vabits) - 1);
+#endif
+
+ cpu_report();
+}
+
+void cpu_set_cluster(struct cpuinfo_loongarch *cpuinfo, unsigned int cluster)
+{
+ /* Ensure the core number fits in the field */
+ WARN_ON(cluster > (LOONGARCH_GLOBALNUMBER_CLUSTER >>
+ LOONGARCH_GLOBALNUMBER_CLUSTER_SHF));
+
+ cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CLUSTER;
+ cpuinfo->globalnumber |= cluster << LOONGARCH_GLOBALNUMBER_CLUSTER_SHF;
+}
+
+void cpu_set_core(struct cpuinfo_loongarch *cpuinfo, unsigned int core)
+{
+ /* Ensure the core number fits in the field */
+ WARN_ON(core > (LOONGARCH_GLOBALNUMBER_CORE >> LOONGARCH_GLOBALNUMBER_CORE_SHF));
+
+ cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CORE;
+ cpuinfo->globalnumber |= core << LOONGARCH_GLOBALNUMBER_CORE_SHF;
+}
diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
new file mode 100644
index 000000000000..a63274c12efc
--- /dev/null
+++ b/arch/loongarch/kernel/efi.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * EFI initialization
+ *
+ * Author: Jianmin Lv <[email protected]>
+ * Huacai Chen <[email protected]>
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/efi-bgrt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kobject.h>
+#include <linux/memblock.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+
+#include <asm/early_ioremap.h>
+#include <asm/efi.h>
+#include <asm/tlb.h>
+#include <asm/loongson.h>
+
+static unsigned long efi_nr_tables;
+static unsigned long efi_config_table;
+static efi_system_table_t *efi_systab;
+static efi_config_table_type_t arch_tables[] __initdata = {{},};
+
+static void __init create_tlb(u32 index, u64 vppn, u32 ps, u32 mat)
+{
+ unsigned long tlblo0, tlblo1;
+
+ write_csr_pagesize(ps);
+
+ tlblo0 = vppn | CSR_TLBLO0_V | CSR_TLBLO0_WE |
+ CSR_TLBLO0_GLOBAL | (mat << CSR_TLBLO0_CCA_SHIFT);
+ tlblo1 = tlblo0 + (1 << ps);
+
+ csr_writeq(vppn, LOONGARCH_CSR_TLBEHI);
+ csr_writeq(tlblo0, LOONGARCH_CSR_TLBELO0);
+ csr_writeq(tlblo1, LOONGARCH_CSR_TLBELO1);
+ csr_xchgl(0, CSR_TLBIDX_EHINV, LOONGARCH_CSR_TLBIDX);
+ csr_xchgl(index, CSR_TLBIDX_IDX, LOONGARCH_CSR_TLBIDX);
+
+ tlb_write_indexed();
+}
+
+#define MTLB_ENTRY_INDEX 0x800
+
+/* Create VA == PA mapping as UEFI */
+static void __init fix_efi_mapping(void)
+{
+ unsigned int i;
+ unsigned int index = MTLB_ENTRY_INDEX;
+ unsigned int tlbnr = boot_cpu_data.tlbsizemtlb - 2;
+ unsigned long vppn;
+
+ /* Low Memory, Cached */
+ create_tlb(index++, 0x00000000, PS_256M, 1);
+ /* MMIO Registers, Uncached */
+ create_tlb(index++, 0x10000000, PS_256M, 0);
+
+ /* High Memory, Cached */
+ for (i = 0; i < tlbnr; i++) {
+ vppn = 0x80000000 + (SZ_2G * i);
+ create_tlb(index++, vppn, PS_1G, 1);
+ }
+}
+
+/*
+ * set_virtual_map() - create a virtual mapping for the EFI memory map and call
+ * efi_set_virtual_address_map enter virtual for runtime service
+ *
+ * This function populates the virt_addr fields of all memory region descriptors
+ * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors
+ * are also copied to @runtime_map, and their total count is returned in @count.
+ */
+unsigned int __init set_virtual_map(void)
+{
+ int i, count = 0;
+ unsigned int size;
+ unsigned long attr;
+ efi_status_t status;
+ efi_runtime_services_t *rt;
+ efi_set_virtual_address_map_t *svam;
+ efi_memory_desc_t *runtime_map, *out;
+ struct loongsonlist_mem_map *map = loongson_mem_map;
+
+ size = sizeof(struct efi_mmap);
+ out = runtime_map = (efi_memory_desc_t *)&map->map[EFI_RUNTIME_MAP_START];
+
+ for (i = 0; i < map->map_count; i++) {
+ attr = map->map[i].attribute;
+ if (!(attr & EFI_MEMORY_RUNTIME))
+ continue;
+
+ map->map[i].mem_vaddr = TO_CAC(map->map[i].mem_start);
+ map->map[i].mem_size = map->map[i].mem_size >> EFI_PAGE_SHIFT;
+
+ memcpy(out, &map->map[i], size);
+ out = (void *)out + size;
+ ++count;
+
+ }
+
+ rt = early_memremap_ro((unsigned long)efi_systab->runtime, sizeof(*rt));
+
+ /* Install the new virtual address map */
+ svam = rt->set_virtual_address_map;
+
+ fix_efi_mapping();
+
+ status = svam(size * count, size, map->desc_version,
+ (efi_memory_desc_t *)TO_PHYS((unsigned long)runtime_map));
+
+ local_flush_tlb_all();
+
+ if (status != EFI_SUCCESS)
+ return -1;
+
+ return 0;
+}
+
+void __init efi_runtime_init(void)
+{
+ efi_status_t status;
+
+ if (!efi_enabled(EFI_BOOT))
+ return;
+
+ if (!efi_systab->runtime)
+ return;
+
+ status = set_virtual_map();
+ if (status < 0)
+ return;
+
+ if (efi_runtime_disabled()) {
+ pr_info("EFI runtime services will be disabled.\n");
+ return;
+ }
+
+ efi.runtime = (efi_runtime_services_t *)efi_systab->runtime;
+ efi.runtime_version = (unsigned int)efi.runtime->hdr.revision;
+
+ efi_native_runtime_setup();
+ set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+}
+
+void __init efi_init(void)
+{
+ int size;
+ void *config_tables;
+
+ if (!efi_bp)
+ return;
+
+ efi_systab = (efi_system_table_t *)early_memremap_ro
+ ((unsigned long)efi_bp->systemtable, sizeof(efi_systab));
+
+ if (!efi_systab) {
+ pr_err("Can't find EFI system table.\n");
+ return;
+ }
+
+ set_bit(EFI_64BIT, &efi.flags);
+ efi_nr_tables = efi_systab->nr_tables;
+ efi_config_table = (unsigned long)efi_systab->tables;
+
+ size = sizeof(efi_config_table_t);
+ config_tables = early_memremap(efi_config_table, efi_nr_tables * size);
+ efi_config_parse_tables(config_tables, efi_systab->nr_tables, arch_tables);
+ early_memunmap(config_tables, efi_nr_tables * size);
+}
+
+static ssize_t boardinfo_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf,
+ "BIOS Information\n"
+ "Vendor\t\t\t: %s\n"
+ "Version\t\t\t: %s\n"
+ "ROM Size\t\t: %d KB\n"
+ "Release Date\t\t: %s\n\n"
+ "Board Information\n"
+ "Manufacturer\t\t: %s\n"
+ "Board Name\t\t: %s\n"
+ "Family\t\t\t: LOONGSON64\n\n",
+ b_info.bios_vendor, b_info.bios_version,
+ b_info.bios_size, b_info.bios_release_date,
+ b_info.board_vendor, b_info.board_name);
+}
+
+static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444,
+ boardinfo_show, NULL);
+
+static int __init boardinfo_init(void)
+{
+ if (!efi_kobj)
+ return -EINVAL;
+
+ return sysfs_create_file(efi_kobj, &boardinfo_attr.attr);
+}
+late_initcall(boardinfo_init);
diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c
new file mode 100644
index 000000000000..491c209ec260
--- /dev/null
+++ b/arch/loongarch/kernel/env.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <[email protected]>
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <asm/early_ioremap.h>
+#include <asm/fw.h>
+#include <asm/time.h>
+#include <asm/bootinfo.h>
+#include <asm/loongson.h>
+
+struct boot_params *efi_bp;
+struct loongsonlist_mem_map *loongson_mem_map;
+struct loongsonlist_vbios *pvbios;
+struct loongson_system_configuration loongson_sysconf;
+EXPORT_SYMBOL(loongson_sysconf);
+
+u64 loongson_chipcfg[MAX_PACKAGES];
+u64 loongson_chiptemp[MAX_PACKAGES];
+u64 loongson_freqctrl[MAX_PACKAGES];
+unsigned long long smp_group[MAX_PACKAGES];
+
+static void __init register_addrs_set(u64 *registers, const u64 addr, int num)
+{
+ u64 i;
+
+ for (i = 0; i < num; i++) {
+ *registers = (i << 44) | addr;
+ registers++;
+ }
+}
+
+static u8 ext_listhdr_checksum(u8 *buffer, u32 length)
+{
+ u8 sum = 0;
+ u8 *end = buffer + length;
+
+ while (buffer < end) {
+ sum = (u8)(sum + *(buffer++));
+ }
+
+ return (sum);
+}
+
+static int parse_mem(struct _extention_list_hdr *head)
+{
+ loongson_mem_map = (struct loongsonlist_mem_map *)head;
+
+ if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) {
+ pr_warn("mem checksum error\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int parse_vbios(struct _extention_list_hdr *head)
+{
+ pvbios = (struct loongsonlist_vbios *)head;
+
+ if (ext_listhdr_checksum((u8 *)pvbios, head->length)) {
+ pr_warn("vbios_addr checksum error\n");
+ return -EPERM;
+ }
+
+ loongson_sysconf.vgabios_addr =
+ (u64)early_memremap_ro(pvbios->vbios_addr, 64);
+
+ return 0;
+}
+
+static int parse_screeninfo(struct _extention_list_hdr *head)
+{
+ struct loongsonlist_screeninfo *pscreeninfo;
+
+ pscreeninfo = (struct loongsonlist_screeninfo *)head;
+ if (ext_listhdr_checksum((u8 *)pscreeninfo, head->length)) {
+ pr_warn("screeninfo_addr checksum error\n");
+ return -EPERM;
+ }
+
+ memcpy(&screen_info, &pscreeninfo->si, sizeof(screen_info));
+
+ return 0;
+}
+
+static int parse_extlist(struct boot_params *bp)
+{
+ unsigned long next_offset;
+ struct _extention_list_hdr *fhead;
+
+ fhead = (struct _extention_list_hdr *)((void *)bp + bp->extlist_offset);
+ if (fhead == NULL) {
+ pr_warn("the ext struct is empty!\n");
+ return -1;
+ }
+
+ do {
+ next_offset = fhead->next_offset;
+ if (memcmp(&(fhead->signature), LOONGSON_MEM_SIGNATURE, 3) == 0) {
+ if (parse_mem(fhead) != 0) {
+ pr_warn("parse mem failed\n");
+ return -EPERM;
+ }
+ } else if (memcmp(&(fhead->signature), LOONGSON_VBIOS_SIGNATURE, 5) == 0) {
+ if (parse_vbios(fhead) != 0) {
+ pr_warn("parse vbios failed\n");
+ return -EPERM;
+ }
+ } else if (memcmp(&(fhead->signature), LOONGSON_SCREENINFO_SIGNATURE, 5) == 0) {
+ if (parse_screeninfo(fhead) != 0) {
+ pr_warn("parse screeninfo failed\n");
+ return -EPERM;
+ }
+ }
+ fhead = (struct _extention_list_hdr *)((void *)bp + next_offset);
+ } while (next_offset);
+
+ return 0;
+}
+
+static void __init parse_flags(u64 flags)
+{
+ if (flags & BPI_FLAGS_UEFI_SUPPORTED)
+ set_bit(EFI_BOOT, &efi.flags);
+ else
+ clear_bit(EFI_BOOT, &efi.flags);
+}
+
+static int get_bpi_version(void *signature)
+{
+ char data[8];
+ int r, version = 0;
+
+ memset(data, 0, 8);
+ memcpy(data, signature + 4, 4);
+ r = kstrtoint(data, 10, &version);
+
+ if (r < 0 || version < BPI_VERSION_V1)
+ panic("Fatal error, invalid BPI version: %d\n", version);
+
+ if (version >= BPI_VERSION_V2)
+ parse_flags(efi_bp->flags);
+
+ return version;
+}
+
+void __init fw_init_environ(void)
+{
+ efi_bp = (struct boot_params *)_fw_envp;
+ loongson_sysconf.bpi_ver = get_bpi_version(&efi_bp->signature);
+
+ register_addrs_set(smp_group, TO_UNCAC(0x1fe01000), 16);
+ register_addrs_set(loongson_chipcfg, TO_UNCAC(0x1fe00180), 16);
+ register_addrs_set(loongson_chiptemp, TO_UNCAC(0x1fe0019c), 16);
+ register_addrs_set(loongson_freqctrl, TO_UNCAC(0x1fe001d0), 16);
+
+ if (parse_extlist(efi_bp))
+ pr_warn("Scan bootparam failed\n");
+}
+
+static int __init init_cpu_fullname(void)
+{
+ int cpu;
+
+ if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) {
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ __cpu_full_name[cpu] = loongson_sysconf.cpuname;
+ }
+ return 0;
+}
+arch_initcall(init_cpu_fullname);
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
new file mode 100644
index 000000000000..b4a0b28da3e7
--- /dev/null
+++ b/arch/loongarch/kernel/head.S
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/threads.h>
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/regdef.h>
+#include <asm/loongarch.h>
+#include <asm/stackframe.h>
+
+SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE)
+
+ __REF
+
+SYM_CODE_START(kernel_entry) # kernel entry point
+
+ /* Config direct window and set PG */
+ li.d t0, CSR_DMW0_INIT # UC, PLV0, 0x8000 xxxx xxxx xxxx
+ csrwr t0, LOONGARCH_CSR_DMWIN0
+ li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx
+ csrwr t0, LOONGARCH_CSR_DMWIN1
+ /* Enable PG */
+ li.w t0, 0xb0 # PLV=0, IE=0, PG=1
+ csrwr t0, LOONGARCH_CSR_CRMD
+ li.w t0, 0x04 # PLV=0, PIE=1, PWE=0
+ csrwr t0, LOONGARCH_CSR_PRMD
+ li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0
+ csrwr t0, LOONGARCH_CSR_EUEN
+
+ /* We might not get launched at the address the kernel is linked to,
+ so we jump there. */
+ la.abs t0, 0f
+ jirl zero, t0, 0
+0:
+ la t0, __bss_start # clear .bss
+ st.d zero, t0, 0
+ la t1, __bss_stop - LONGSIZE
+1:
+ addi.d t0, t0, LONGSIZE
+ st.d zero, t0, 0
+ bne t0, t1, 1b
+
+ la t0, fw_arg0
+ st.d a0, t0, 0 # firmware arguments
+ la t0, fw_arg1
+ st.d a1, t0, 0
+ la t0, fw_arg2
+ st.d a2, t0, 0
+ la t0, fw_arg3
+ st.d a3, t0, 0
+
+ /* KScratch3 used for percpu base, initialized as 0 */
+ csrwr zero, PERCPU_BASE_KS
+ /* GPR21 used for percpu base (runtime), initialized as 0 */
+ or u0, zero, zero
+
+ la tp, init_thread_union
+ /* Set the SP after an empty pt_regs. */
+ PTR_LI sp, (_THREAD_SIZE - 32 - PT_SIZE)
+ PTR_ADDU sp, sp, tp
+ set_saved_sp sp, t0, t1
+ PTR_ADDIU sp, sp, -4 * SZREG # init stack pointer
+
+ bl start_kernel
+
+SYM_CODE_END(kernel_entry)
+
+SYM_ENTRY(kernel_entry_end, SYM_L_GLOBAL, SYM_A_NONE)
diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
new file mode 100644
index 000000000000..361d108a2b82
--- /dev/null
+++ b/arch/loongarch/kernel/mem.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+
+#include <asm/bootinfo.h>
+#include <asm/loongson.h>
+#include <asm/sections.h>
+
+void __init early_memblock_init(void)
+{
+ int i;
+ u32 mem_type;
+ u64 mem_start, mem_end, mem_size;
+
+ /* Parse memory information */
+ for (i = 0; i < loongson_mem_map->map_count; i++) {
+ mem_type = loongson_mem_map->map[i].mem_type;
+ mem_start = loongson_mem_map->map[i].mem_start;
+ mem_size = loongson_mem_map->map[i].mem_size;
+ mem_end = mem_start + mem_size;
+
+ switch (mem_type) {
+ case ADDRESS_TYPE_SYSRAM:
+ memblock_add(mem_start, mem_size);
+ if (max_low_pfn < (mem_end >> PAGE_SHIFT))
+ max_low_pfn = mem_end >> PAGE_SHIFT;
+ break;
+ }
+ }
+ memblock_set_current_limit(PFN_PHYS(max_low_pfn));
+}
+
+void __init fw_init_memory(void)
+{
+ int i;
+ u32 mem_type;
+ u64 mem_start, mem_end, mem_size;
+ unsigned long start_pfn, end_pfn;
+ static unsigned long num_physpages;
+
+ /* Parse memory information */
+ for (i = 0; i < loongson_mem_map->map_count; i++) {
+ mem_type = loongson_mem_map->map[i].mem_type;
+ mem_start = loongson_mem_map->map[i].mem_start;
+ mem_size = loongson_mem_map->map[i].mem_size;
+ mem_end = mem_start + mem_size;
+
+ switch (mem_type) {
+ case ADDRESS_TYPE_SYSRAM:
+ mem_start = PFN_ALIGN(mem_start);
+ mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
+ num_physpages += (mem_size >> PAGE_SHIFT);
+ memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
+ break;
+ case ADDRESS_TYPE_ACPI:
+ mem_start = PFN_ALIGN(mem_start);
+ mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
+ num_physpages += (mem_size >> PAGE_SHIFT);
+ memblock_add(mem_start, mem_size);
+ memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
+ memblock_mark_nomap(mem_start, mem_size);
+ fallthrough;
+ case ADDRESS_TYPE_RESERVED:
+ memblock_reserve(mem_start, mem_size);
+ break;
+ }
+ }
+
+ get_pfn_range_for_nid(0, &start_pfn, &end_pfn);
+ pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n",
+ start_pfn, end_pfn, num_physpages);
+
+ NODE_DATA(0)->node_start_pfn = start_pfn;
+ NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn;
+
+ /* used by finalize_initrd() */
+ max_low_pfn = end_pfn;
+
+ /* Reserve the first 2MB */
+ memblock_reserve(PHYS_OFFSET, 0x200000);
+
+ /* Reserve the kernel text/data/bss */
+ memblock_reserve(__pa_symbol(&_text),
+ __pa_symbol(&_end) - __pa_symbol(&_text));
+}
diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c
new file mode 100644
index 000000000000..ef484ce43c5c
--- /dev/null
+++ b/arch/loongarch/kernel/reset.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+
+#include <acpi/reboot.h>
+#include <asm/compiler.h>
+#include <asm/idle.h>
+#include <asm/loongarch.h>
+#include <asm/reboot.h>
+
+static void default_halt(void)
+{
+ local_irq_disable();
+ clear_csr_ecfg(ECFG0_IM);
+
+ pr_notice("\n\n** You can safely turn off the power now **\n\n");
+ console_flush_on_panic(CONSOLE_FLUSH_PENDING);
+
+ while (true) {
+ __arch_cpu_idle();
+ }
+}
+
+static void default_poweroff(void)
+{
+#ifdef CONFIG_EFI
+ efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL);
+#endif
+ while (true) {
+ __arch_cpu_idle();
+ }
+}
+
+static void default_restart(void)
+{
+#ifdef CONFIG_EFI
+ if (efi_capsule_pending(NULL))
+ efi_reboot(REBOOT_WARM, NULL);
+ else
+ efi_reboot(REBOOT_COLD, NULL);
+#endif
+ if (!acpi_disabled)
+ acpi_reboot();
+
+ while (true) {
+ __arch_cpu_idle();
+ }
+}
+
+void (*pm_restart)(void);
+EXPORT_SYMBOL(pm_restart);
+
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+void machine_halt(void)
+{
+ default_halt();
+}
+
+void machine_power_off(void)
+{
+ pm_power_off();
+}
+
+void machine_restart(char *command)
+{
+ do_kernel_restart(command);
+ pm_restart();
+}
+
+static int __init loongarch_reboot_setup(void)
+{
+ pm_restart = default_restart;
+ pm_power_off = default_poweroff;
+
+ return 0;
+}
+
+arch_initcall(loongarch_reboot_setup);
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
new file mode 100644
index 000000000000..8dfe1d9b55f7
--- /dev/null
+++ b/arch/loongarch/kernel/setup.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ *
+ * Derived from MIPS:
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 1995 Waldorf Electronics
+ * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03 Ralf Baechle
+ * Copyright (C) 1996 Stoned Elipot
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ * Copyright (C) 2000, 2001, 2002, 2007 Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/efi.h>
+#include <linux/export.h>
+#include <linux/screen_info.h>
+#include <linux/memblock.h>
+#include <linux/initrd.h>
+#include <linux/ioport.h>
+#include <linux/root_dev.h>
+#include <linux/console.h>
+#include <linux/pfn.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/device.h>
+#include <linux/dma-map-ops.h>
+#include <linux/swiotlb.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/cache.h>
+#include <asm/cpu.h>
+#include <asm/dma.h>
+#include <asm/efi.h>
+#include <asm/fw.h>
+#include <asm/loongson.h>
+#include <asm/pgalloc.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/time.h>
+
+#define SMBIOS_BIOSSIZE_OFFSET 0x09
+#define SMBIOS_BIOSEXTERN_OFFSET 0x13
+#define SMBIOS_FREQLOW_OFFSET 0x16
+#define SMBIOS_FREQHIGH_OFFSET 0x17
+#define SMBIOS_FREQLOW_MASK 0xFF
+#define SMBIOS_CORE_PACKAGE_OFFSET 0x23
+#define LOONGSON_EFI_ENABLE (1 << 3)
+
+#ifdef CONFIG_VT
+struct screen_info screen_info;
+#endif
+
+DEFINE_PER_CPU(unsigned long, kernelsp);
+unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3;
+struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly;
+
+EXPORT_SYMBOL(cpu_data);
+
+struct loongson_board_info b_info;
+static const char dmi_empty_string[] = " ";
+
+/*
+ * Setup information
+ *
+ * These are initialized so they are in the .data section
+ */
+
+char __initdata arcs_cmdline[COMMAND_LINE_SIZE];
+static char __initdata command_line[COMMAND_LINE_SIZE];
+
+static int num_standard_resources;
+static struct resource *standard_resources;
+
+static struct resource code_resource = { .name = "Kernel code", };
+static struct resource data_resource = { .name = "Kernel data", };
+static struct resource bss_resource = { .name = "Kernel bss", };
+
+const char *get_system_type(void)
+{
+ return "generic-loongson-machine";
+}
+
+static const char *dmi_string_parse(const struct dmi_header *dm, u8 s)
+{
+ const u8 *bp = ((u8 *) dm) + dm->length;
+
+ if (s) {
+ s--;
+ while (s > 0 && *bp) {
+ bp += strlen(bp) + 1;
+ s--;
+ }
+
+ if (*bp != 0) {
+ size_t len = strlen(bp)+1;
+ size_t cmp_len = len > 8 ? 8 : len;
+
+ if (!memcmp(bp, dmi_empty_string, cmp_len))
+ return dmi_empty_string;
+
+ return bp;
+ }
+ }
+
+ return "";
+}
+
+static void __init parse_cpu_table(const struct dmi_header *dm)
+{
+ long freq_temp = 0;
+ char *dmi_data = (char *)dm;
+
+ freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) +
+ ((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK));
+ cpu_clock_freq = freq_temp * 1000000;
+
+ loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]);
+ loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET);
+
+ pr_info("CpuClock = %llu\n", cpu_clock_freq);
+}
+
+static void __init parse_bios_table(const struct dmi_header *dm)
+{
+ int bios_extern;
+ char *dmi_data = (char *)dm;
+
+ bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET);
+ b_info.bios_size = *(dmi_data + SMBIOS_BIOSSIZE_OFFSET);
+
+ if (bios_extern & LOONGSON_EFI_ENABLE)
+ set_bit(EFI_BOOT, &efi.flags);
+ else
+ clear_bit(EFI_BOOT, &efi.flags);
+}
+
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
+{
+ switch (dm->type) {
+ case 0x0: /* Extern BIOS */
+ parse_bios_table(dm);
+ break;
+ case 0x4: /* Calling interface */
+ parse_cpu_table(dm);
+ break;
+ }
+}
+static void __init smbios_parse(void)
+{
+ b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
+ b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION);
+ b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE);
+ b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR);
+ b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME);
+ dmi_walk(find_tokens, NULL);
+}
+
+/*
+ * Manage initrd
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static unsigned long __init init_initrd(void)
+{
+ if (!phys_initrd_start || !phys_initrd_size)
+ goto disable;
+
+ initrd_start = (unsigned long)phys_to_virt(phys_initrd_start);
+ initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size);
+
+ if (!initrd_start || initrd_end <= initrd_start)
+ goto disable;
+
+ if (initrd_start & ~PAGE_MASK) {
+ pr_err("initrd start must be page aligned\n");
+ goto disable;
+ }
+ if (initrd_start < PAGE_OFFSET) {
+ pr_err("initrd start < PAGE_OFFSET\n");
+ goto disable;
+ }
+
+ ROOT_DEV = Root_RAM0;
+
+ return 0;
+disable:
+ initrd_start = 0;
+ initrd_end = 0;
+ return 0;
+}
+
+static void __init finalize_initrd(void)
+{
+ unsigned long size = initrd_end - initrd_start;
+
+ if (size == 0) {
+ pr_info("Initrd not found or empty");
+ goto disable;
+ }
+ if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) {
+ pr_err("Initrd extends beyond end of memory");
+ goto disable;
+ }
+
+
+ memblock_reserve(__pa(initrd_start), size);
+ initrd_below_start_ok = 1;
+
+ pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n",
+ initrd_start, size);
+ return;
+disable:
+ pr_cont(" - disabling initrd\n");
+ initrd_start = 0;
+ initrd_end = 0;
+}
+
+#else /* !CONFIG_BLK_DEV_INITRD */
+
+static unsigned long __init init_initrd(void)
+{
+ return 0;
+}
+
+#define finalize_initrd() do {} while (0)
+
+#endif
+
+static int usermem __initdata;
+
+static int __init early_parse_mem(char *p)
+{
+ phys_addr_t start, size;
+
+ /*
+ * If a user specifies memory size, we
+ * blow away any automatically generated
+ * size.
+ */
+ if (usermem == 0) {
+ usermem = 1;
+ memblock_remove(memblock_start_of_DRAM(),
+ memblock_end_of_DRAM() - memblock_start_of_DRAM());
+ }
+ start = 0;
+ size = memparse(p, &p);
+ if (*p == '@')
+ start = memparse(p + 1, &p);
+
+ memblock_add(start, size);
+
+ return 0;
+}
+early_param("mem", early_parse_mem);
+
+static int __init early_parse_memmap(char *p)
+{
+ char *oldp;
+ u64 start_at, mem_size;
+
+ if (!p)
+ return -EINVAL;
+
+ if (!strncmp(p, "exactmap", 8)) {
+ pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
+ return 0;
+ }
+
+ oldp = p;
+ mem_size = memparse(p, &p);
+ if (p == oldp)
+ return -EINVAL;
+
+ if (*p == '@') {
+ start_at = memparse(p+1, &p);
+ memblock_add(start_at, mem_size);
+ } else if (*p == '#') {
+ pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
+ return -EINVAL;
+ } else if (*p == '$') {
+ start_at = memparse(p+1, &p);
+ memblock_add(start_at, mem_size);
+ memblock_reserve(start_at, mem_size);
+ } else {
+ pr_err("\"memmap\" invalid format!\n");
+ return -EINVAL;
+ }
+
+ if (*p == '\0') {
+ usermem = 1;
+ return 0;
+ } else
+ return -EINVAL;
+}
+early_param("memmap", early_parse_memmap);
+
+static void __init bootcmdline_append(const char *s, size_t max)
+{
+ if (!s[0] || !max)
+ return;
+
+ if (boot_command_line[0])
+ strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+
+ strlcat(boot_command_line, s, max);
+}
+
+static void __init bootcmdline_init(char **cmdline_p)
+{
+ boot_command_line[0] = 0;
+
+ /*
+ * Take arguments from the bootloader at first. Early code should have
+ * filled arcs_cmdline with arguments from the bootloader.
+ */
+ bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE);
+
+ strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
+ *cmdline_p = command_line;
+
+ parse_early_param();
+}
+
+void __init platform_init(void)
+{
+ efi_init();
+#ifdef CONFIG_ACPI_TABLE_UPGRADE
+ acpi_table_upgrade();
+#endif
+#ifdef CONFIG_ACPI
+ acpi_gbl_use_default_register_widths = false;
+ acpi_boot_table_init();
+ acpi_boot_init();
+#endif
+
+ fw_init_memory();
+ dmi_setup();
+ smbios_parse();
+ pr_info("The BIOS Version: %s\n", b_info.bios_version);
+
+ efi_runtime_init();
+}
+
+static void __init check_kernel_sections_mem(void)
+{
+ phys_addr_t start = __pa_symbol(&_text);
+ phys_addr_t size = __pa_symbol(&_end) - start;
+
+ if (!memblock_is_region_memory(start, size)) {
+ pr_info("Kernel sections are not in the memory maps\n");
+ memblock_add(start, size);
+ }
+}
+
+/*
+ * arch_mem_init - initialize memory management subsystem
+ */
+static void __init arch_mem_init(char **cmdline_p)
+{
+ if (usermem)
+ pr_info("User-defined physical RAM map overwrite\n");
+
+ check_kernel_sections_mem();
+
+ memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
+
+ memblock_set_current_limit(PFN_PHYS(max_low_pfn));
+
+ /*
+ * In order to reduce the possibility of kernel panic when failed to
+ * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
+ * low memory as small as possible before plat_swiotlb_setup(), so
+ * make sparse_init() using top-down allocation.
+ */
+ memblock_set_bottom_up(false);
+ sparse_init();
+ memblock_set_bottom_up(true);
+
+ swiotlb_init(1);
+
+ dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
+
+ memblock_dump_all();
+
+ early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
+}
+
+static void __init resource_init(void)
+{
+ long i = 0;
+ size_t res_size;
+ struct resource *res;
+ struct memblock_region *region;
+
+ code_resource.start = __pa_symbol(&_text);
+ code_resource.end = __pa_symbol(&_etext) - 1;
+ data_resource.start = __pa_symbol(&_etext);
+ data_resource.end = __pa_symbol(&_edata) - 1;
+ bss_resource.start = __pa_symbol(&__bss_start);
+ bss_resource.end = __pa_symbol(&__bss_stop) - 1;
+
+ num_standard_resources = memblock.memory.cnt;
+ res_size = num_standard_resources * sizeof(*standard_resources);
+ standard_resources = memblock_alloc(res_size, SMP_CACHE_BYTES);
+
+ for_each_mem_region(region) {
+ res = &standard_resources[i++];
+ if (!memblock_is_nomap(region)) {
+ res->name = "System RAM";
+ res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
+ res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region));
+ res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1;
+ } else {
+ res->name = "Reserved";
+ res->flags = IORESOURCE_MEM;
+ res->start = __pfn_to_phys(memblock_region_reserved_base_pfn(region));
+ res->end = __pfn_to_phys(memblock_region_reserved_end_pfn(region)) - 1;
+ }
+
+ request_resource(&iomem_resource, res);
+
+ /*
+ * We don't know which RAM region contains kernel data,
+ * so we try it repeatedly and let the resource manager
+ * test it.
+ */
+ request_resource(res, &code_resource);
+ request_resource(res, &data_resource);
+ request_resource(res, &bss_resource);
+ }
+}
+
+static int __init reserve_memblock_reserved_regions(void)
+{
+ u64 i, j;
+
+ for (i = 0; i < num_standard_resources; ++i) {
+ struct resource *mem = &standard_resources[i];
+ phys_addr_t r_start, r_end, mem_size = resource_size(mem);
+
+ if (!memblock_is_region_reserved(mem->start, mem_size))
+ continue;
+
+ for_each_reserved_mem_range(j, &r_start, &r_end) {
+ resource_size_t start, end;
+
+ start = max(PFN_PHYS(PFN_DOWN(r_start)), mem->start);
+ end = min(PFN_PHYS(PFN_UP(r_end)) - 1, mem->end);
+
+ if (start > mem->end || end < mem->start)
+ continue;
+
+ reserve_region_with_split(mem, start, end, "Reserved");
+ }
+ }
+
+ return 0;
+}
+arch_initcall(reserve_memblock_reserved_regions);
+
+void __init setup_arch(char **cmdline_p)
+{
+ cpu_probe();
+
+ fw_init_cmdline();
+ fw_init_environ();
+ early_memblock_init();
+ bootcmdline_init(cmdline_p);
+
+ init_initrd();
+ platform_init();
+ pagetable_init();
+ finalize_initrd();
+
+ arch_mem_init(cmdline_p);
+
+ resource_init();
+
+ paging_init();
+}
+
+static int __init register_gop_device(void)
+{
+ void *pd;
+
+ if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
+ return 0;
+ pd = platform_device_register_data(NULL, "efi-framebuffer", 0,
+ &screen_info, sizeof(screen_info));
+ return PTR_ERR_OR_ZERO(pd);
+}
+subsys_initcall(register_gop_device);
diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c
new file mode 100644
index 000000000000..5d2b2c6712bc
--- /dev/null
+++ b/arch/loongarch/kernel/time.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common time service routines for LoongArch machines.
+ *
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/clockchips.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched_clock.h>
+#include <linux/spinlock.h>
+
+#include <asm/cpu-features.h>
+#include <asm/loongarch.h>
+#include <asm/time.h>
+
+u64 cpu_clock_freq;
+EXPORT_SYMBOL(cpu_clock_freq);
+u64 const_clock_freq;
+EXPORT_SYMBOL(const_clock_freq);
+
+static DEFINE_RAW_SPINLOCK(state_lock);
+static DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device);
+
+static void constant_event_handler(struct clock_event_device *dev)
+{
+}
+
+irqreturn_t constant_timer_interrupt(int irq, void *data)
+{
+ int cpu = smp_processor_id();
+ struct clock_event_device *cd;
+
+ /* Clear Timer Interrupt */
+ write_csr_tintclear(CSR_TINTCLR_TI);
+ cd = &per_cpu(constant_clockevent_device, cpu);
+ cd->event_handler(cd);
+
+ return IRQ_HANDLED;
+}
+
+static int constant_set_state_oneshot(struct clock_event_device *evt)
+{
+ unsigned long timer_config;
+
+ raw_spin_lock(&state_lock);
+
+ timer_config = csr_readq(LOONGARCH_CSR_TCFG);
+ timer_config |= CSR_TCFG_EN;
+ timer_config &= ~CSR_TCFG_PERIOD;
+ csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+ raw_spin_unlock(&state_lock);
+
+ return 0;
+}
+
+static int constant_set_state_oneshot_stopped(struct clock_event_device *evt)
+{
+ unsigned long timer_config;
+
+ raw_spin_lock(&state_lock);
+
+ timer_config = csr_readq(LOONGARCH_CSR_TCFG);
+ timer_config &= ~CSR_TCFG_EN;
+ csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+ raw_spin_unlock(&state_lock);
+
+ return 0;
+}
+
+static int constant_set_state_periodic(struct clock_event_device *evt)
+{
+ unsigned long period;
+ unsigned long timer_config;
+
+ raw_spin_lock(&state_lock);
+
+ period = const_clock_freq / HZ;
+ timer_config = period & CSR_TCFG_VAL;
+ timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN);
+ csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+ raw_spin_unlock(&state_lock);
+
+ return 0;
+}
+
+static int constant_set_state_shutdown(struct clock_event_device *evt)
+{
+ return 0;
+}
+
+static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt)
+{
+ unsigned long timer_config;
+
+ delta &= CSR_TCFG_VAL;
+ timer_config = delta | CSR_TCFG_EN;
+ csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+ return 0;
+}
+
+static unsigned long __init get_loops_per_jiffy(void)
+{
+ unsigned long lpj = (unsigned long)const_clock_freq;
+
+ do_div(lpj, HZ);
+
+ return lpj;
+}
+
+static long init_timeval;
+
+void sync_counter(void)
+{
+ /* Ensure counter begin at 0 */
+ csr_writeq(-init_timeval, LOONGARCH_CSR_CNTC);
+}
+
+int constant_clockevent_init(void)
+{
+ unsigned int irq;
+ unsigned int cpu = smp_processor_id();
+ unsigned long min_delta = 0x600;
+ unsigned long max_delta = (1UL << 48) - 1;
+ struct clock_event_device *cd;
+ static int timer_irq_installed = 0;
+
+ irq = get_timer_irq();
+
+ cd = &per_cpu(constant_clockevent_device, cpu);
+
+ cd->name = "Constant";
+ cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU;
+
+ cd->irq = irq;
+ cd->rating = 320;
+ cd->cpumask = cpumask_of(cpu);
+ cd->set_state_oneshot = constant_set_state_oneshot;
+ cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped;
+ cd->set_state_periodic = constant_set_state_periodic;
+ cd->set_state_shutdown = constant_set_state_shutdown;
+ cd->set_next_event = constant_timer_next_event;
+ cd->event_handler = constant_event_handler;
+
+ clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta);
+
+ if (timer_irq_installed)
+ return 0;
+
+ timer_irq_installed = 1;
+
+ sync_counter();
+
+ if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL))
+ pr_err("Failed to request irq %d (timer)\n", irq);
+
+ lpj_fine = get_loops_per_jiffy();
+ pr_info("Constant clock event device register\n");
+
+ return 0;
+}
+
+static u64 read_const_counter(struct clocksource *clk)
+{
+ return drdtime();
+}
+
+static u64 native_sched_clock(void)
+{
+ return read_const_counter(NULL);
+}
+
+static struct clocksource clocksource_const = {
+ .name = "Constant",
+ .rating = 400,
+ .read = read_const_counter,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .mult = 0,
+ .shift = 10,
+};
+
+int __init constant_clocksource_init(void)
+{
+ int res;
+ unsigned long freq;
+
+ freq = const_clock_freq;
+
+ clocksource_const.mult =
+ clocksource_hz2mult(freq, clocksource_const.shift);
+
+ res = clocksource_register_hz(&clocksource_const, freq);
+
+ sched_clock_register(native_sched_clock, 64, freq);
+
+ pr_info("Constant clock source device register\n");
+
+ return res;
+}
+
+void __init time_init(void)
+{
+ if (!cpu_has_cpucfg)
+ const_clock_freq = cpu_clock_freq;
+ else
+ const_clock_freq = calc_const_freq();
+
+ init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC);
+
+ constant_clockevent_init();
+ constant_clocksource_init();
+}
diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c
new file mode 100644
index 000000000000..3b2cbb95875b
--- /dev/null
+++ b/arch/loongarch/kernel/topology.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/cpu.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+
+static struct cpu cpu_device;
+
+static int __init topology_init(void)
+{
+ return register_cpu(&cpu_device, 0);
+}
+
+subsys_initcall(topology_init);
--
2.27.0


2022-02-27 15:40:19

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

(add Greg and ACPI maintainers)

On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
>
> This patch adds basic boot, setup and reset routines for LoongArch.
> LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> SMBIOS to pass configuration information to the Linux kernel (in elf
> format).
>
> Now the boot information passed to kernel is like this:
> 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> type is "efi_system_table_t". Most configuration information, include
> ACPI tables and SMBIOS tables, come from here.
>
> Cc: Ard Biesheuvel <[email protected]>
> Cc: [email protected]
> Signed-off-by: Huacai Chen <[email protected]>
> ---
> arch/loongarch/include/asm/acenv.h | 17 +
> arch/loongarch/include/asm/acpi.h | 38 ++
> arch/loongarch/include/asm/boot_param.h | 97 +++++
> arch/loongarch/include/asm/bootinfo.h | 33 ++
> arch/loongarch/include/asm/dmi.h | 24 ++
> arch/loongarch/include/asm/efi.h | 33 ++
> arch/loongarch/include/asm/fw.h | 18 +
> arch/loongarch/include/asm/reboot.h | 10 +
> arch/loongarch/include/asm/setup.h | 21 +
> arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> arch/loongarch/kernel/cmdline.c | 31 ++
> arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> arch/loongarch/kernel/efi.c | 208 ++++++++++
> arch/loongarch/kernel/env.c | 176 +++++++++
> arch/loongarch/kernel/head.S | 72 ++++
> arch/loongarch/kernel/mem.c | 89 +++++
> arch/loongarch/kernel/reset.c | 90 +++++
> arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> arch/loongarch/kernel/time.c | 220 +++++++++++
> arch/loongarch/kernel/topology.c | 13 +
> 21 files changed, 2450 insertions(+)

As I pointed out in response to an earlier revision of this code, I
don't think we should merge this until we decide on some ground rules
regarding the support level of this architecture in the UEFI and ACPI
subsystems.

The problem is that loongarch does not exist in the ACPI or UEFI
specifications at all, and as I understand it, the firmware
implementations themselves do not implement UEFI or ACPI entirely,
they simply present data structures in memory that look similar enough
for the Linux UEFI and ACPI code to boot the OS.

As the UEFI subsystem maintainer, I am concerned that future changes
to the UEFI subsystem that are rooted in the the UEFI specification as
it evolves may trigger unanticipated results on this architecture, and
I imagine the ACPI maintainers may have similar concerns.

So what can we do about this? Do we merge this code, but as a second
class citizen in terms of UEFI/ACPI subsystem support, i.e., you are
welcome to use it, but if something breaks, the UEFI/ACPI maintainers
are not on the hook to see to it that it gets fixed? I don't think
this is a great solution, but I'm not sure if there are alternatives
that are any better.

Thoughts, please?

--
Ard.

2022-02-27 22:10:50

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> (add Greg and ACPI maintainers)
>
> On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> >
> > This patch adds basic boot, setup and reset routines for LoongArch.
> > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > SMBIOS to pass configuration information to the Linux kernel (in elf
> > format).
> >
> > Now the boot information passed to kernel is like this:
> > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > type is "efi_system_table_t". Most configuration information, include
> > ACPI tables and SMBIOS tables, come from here.
> >
> > Cc: Ard Biesheuvel <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Huacai Chen <[email protected]>
> > ---
> > arch/loongarch/include/asm/acenv.h | 17 +
> > arch/loongarch/include/asm/acpi.h | 38 ++
> > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > arch/loongarch/include/asm/dmi.h | 24 ++
> > arch/loongarch/include/asm/efi.h | 33 ++
> > arch/loongarch/include/asm/fw.h | 18 +
> > arch/loongarch/include/asm/reboot.h | 10 +
> > arch/loongarch/include/asm/setup.h | 21 +
> > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > arch/loongarch/kernel/cmdline.c | 31 ++
> > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > arch/loongarch/kernel/env.c | 176 +++++++++
> > arch/loongarch/kernel/head.S | 72 ++++
> > arch/loongarch/kernel/mem.c | 89 +++++
> > arch/loongarch/kernel/reset.c | 90 +++++
> > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > arch/loongarch/kernel/time.c | 220 +++++++++++
> > arch/loongarch/kernel/topology.c | 13 +
> > 21 files changed, 2450 insertions(+)
>
> As I pointed out in response to an earlier revision of this code, I
> don't think we should merge this until we decide on some ground rules
> regarding the support level of this architecture in the UEFI and ACPI
> subsystems.
>
> The problem is that loongarch does not exist in the ACPI or UEFI
> specifications at all, and as I understand it, the firmware
> implementations themselves do not implement UEFI or ACPI entirely,
> they simply present data structures in memory that look similar enough
> for the Linux UEFI and ACPI code to boot the OS.

Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
spec groups by the comapny making these devices, or something else?

> As the UEFI subsystem maintainer, I am concerned that future changes
> to the UEFI subsystem that are rooted in the the UEFI specification as
> it evolves may trigger unanticipated results on this architecture, and
> I imagine the ACPI maintainers may have similar concerns.

I agree, I would be concerned about that as well.

> So what can we do about this? Do we merge this code, but as a second
> class citizen in terms of UEFI/ACPI subsystem support, i.e., you are
> welcome to use it, but if something breaks, the UEFI/ACPI maintainers
> are not on the hook to see to it that it gets fixed? I don't think
> this is a great solution, but I'm not sure if there are alternatives
> that are any better.

That's not going to work, it's the kernel's job to fix up hardware
problems. But as this is all firmware, why can't the firmware be
changed to properly follow the public specifications? What's wrong with
requiring that at this point in time (i.e. _BEFORE_ the code is merged)?

thanks,

greg k-h

2022-02-28 09:21:21

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Mon, 28 Feb 2022 at 09:38, Huacai Chen <[email protected]> wrote:
>
> Hi, Ard,
>
> On Mon, Feb 28, 2022 at 4:09 PM Ard Biesheuvel <[email protected]> wrote:
> >
> > On Mon, 28 Feb 2022 at 07:34, Huacai Chen <[email protected]> wrote:
> > >
> > > Hi, Ard and Greg,
> > >
> > > On Mon, Feb 28, 2022 at 12:40 AM Greg Kroah-Hartman
> > > <[email protected]> wrote:
> > > >
> > > > On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> > > > > (add Greg and ACPI maintainers)
> > > > >
> > > > > On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> > > > > >
> > > > > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > > > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > > > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > > > > format).
> > > > > >
> > > > > > Now the boot information passed to kernel is like this:
> > > > > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > > > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > > > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > > > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > > > > type is "efi_system_table_t". Most configuration information, include
> > > > > > ACPI tables and SMBIOS tables, come from here.
> > > > > >
> > > > > > Cc: Ard Biesheuvel <[email protected]>
> > > > > > Cc: [email protected]
> > > > > > Signed-off-by: Huacai Chen <[email protected]>
> > > > > > ---
> > > > > > arch/loongarch/include/asm/acenv.h | 17 +
> > > > > > arch/loongarch/include/asm/acpi.h | 38 ++
> > > > > > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > > > > > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > > > > > arch/loongarch/include/asm/dmi.h | 24 ++
> > > > > > arch/loongarch/include/asm/efi.h | 33 ++
> > > > > > arch/loongarch/include/asm/fw.h | 18 +
> > > > > > arch/loongarch/include/asm/reboot.h | 10 +
> > > > > > arch/loongarch/include/asm/setup.h | 21 +
> > > > > > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > > > > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > > > > > arch/loongarch/kernel/cmdline.c | 31 ++
> > > > > > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > > > > > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > > > > > arch/loongarch/kernel/env.c | 176 +++++++++
> > > > > > arch/loongarch/kernel/head.S | 72 ++++
> > > > > > arch/loongarch/kernel/mem.c | 89 +++++
> > > > > > arch/loongarch/kernel/reset.c | 90 +++++
> > > > > > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > > > > > arch/loongarch/kernel/time.c | 220 +++++++++++
> > > > > > arch/loongarch/kernel/topology.c | 13 +
> > > > > > 21 files changed, 2450 insertions(+)
> > > > >
> > > > > As I pointed out in response to an earlier revision of this code, I
> > > > > don't think we should merge this until we decide on some ground rules
> > > > > regarding the support level of this architecture in the UEFI and ACPI
> > > > > subsystems.
> > > > >
> > > > > The problem is that loongarch does not exist in the ACPI or UEFI
> > > > > specifications at all, and as I understand it, the firmware
> > > > > implementations themselves do not implement UEFI or ACPI entirely,
> > > > > they simply present data structures in memory that look similar enough
> > > > > for the Linux UEFI and ACPI code to boot the OS.
> > > >
> > > > Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
> > > > spec groups by the comapny making these devices, or something else?
> > > We have tried our best to make LoongArch parts be in ACPI and UEFI SPECs.
> > >
> > > ECR for adding LoongArch support in ACPI:
> > > https://mantis.uefi.org/mantis/view.php?id=2203
> > >
> > > ECR for adding LoongArch support in ACPI (version update):
> > > https://mantis.uefi.org/mantis/view.php?id=2268
> > >
> > > ECR for adding LoongArch support in UEFI:
> > > https://mantis.uefi.org/mantis/view.php?id=2313
> > >
> > > ACPI changes of LoongArch have been approved in the last year, but the
> > > new version of ACPI SPEC hasn't been made public yet. And UEFI changes
> > > of LoongArch are under review now.
> > >
> > > Is it a must that the kernel code be merged after all SPECs are
> > > public? If not, I think we can provide some snapshots (If it is legal,
> > > I'm not sure) of mantis.uefi.org to prove the above.
> > >
> >
> > Thanks for the links, those with access will be able to review,
> > although it would of course be preferable if this was open access.
> >
> > In any case, if UEFI and ACPI support is going to be ratified in the
> > respective specifications, we are in a much better place to support
> > this in Linux going forward.
> >
> > However, that still doesn't mean you should be using the internal API
> > used between the EFI stub and the core kernel as a boot interface.
> > Instead, you should implement LoongArch support into the EFI stub, and
> > build the kernel as a PE/COFF image that can boot from EFI directly,
> > from UEFI compliant firmware (u-boot or EDK2 are the most common
> > examples) that exposes all the UEFI stuff that the EFI stub relies on.
> We have implemented EFISTUB, but not in this first series:
> https://github.com/loongson/linux/commit/d415a8e57e4d248e239958f2f18b45ea7a5fec2c
> We want to add efistub support in the next series after new UEFI SPEC released.
>
> >
> > RISC-V is a useful reference for the changes needed - this is the most
> > recent addition to the EFI stub, and avoids some legacy stuff that new
> > architectures have no need for.
> We still want to support the raw elf kernel (RISC-V also does),
> because LoongArch also has MCU and SoC and we want to support FDT (I
> think this is reasonable, because RISC-V also supports raw elf).
>

That is fine. So perhaps the best course of action is to omit the
UEFI/ACPI parts entirely for now, and focus on the DT/embedded use
case. Once all the spec pieces are in place, the UEFI + ACPI changes
can be presented as a single coherent set.

--
Ard.

2022-02-28 09:49:19

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard and Greg,

On Mon, Feb 28, 2022 at 12:40 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> > (add Greg and ACPI maintainers)
> >
> > On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> > >
> > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > format).
> > >
> > > Now the boot information passed to kernel is like this:
> > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > type is "efi_system_table_t". Most configuration information, include
> > > ACPI tables and SMBIOS tables, come from here.
> > >
> > > Cc: Ard Biesheuvel <[email protected]>
> > > Cc: [email protected]
> > > Signed-off-by: Huacai Chen <[email protected]>
> > > ---
> > > arch/loongarch/include/asm/acenv.h | 17 +
> > > arch/loongarch/include/asm/acpi.h | 38 ++
> > > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > > arch/loongarch/include/asm/dmi.h | 24 ++
> > > arch/loongarch/include/asm/efi.h | 33 ++
> > > arch/loongarch/include/asm/fw.h | 18 +
> > > arch/loongarch/include/asm/reboot.h | 10 +
> > > arch/loongarch/include/asm/setup.h | 21 +
> > > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > > arch/loongarch/kernel/cmdline.c | 31 ++
> > > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > > arch/loongarch/kernel/env.c | 176 +++++++++
> > > arch/loongarch/kernel/head.S | 72 ++++
> > > arch/loongarch/kernel/mem.c | 89 +++++
> > > arch/loongarch/kernel/reset.c | 90 +++++
> > > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > > arch/loongarch/kernel/time.c | 220 +++++++++++
> > > arch/loongarch/kernel/topology.c | 13 +
> > > 21 files changed, 2450 insertions(+)
> >
> > As I pointed out in response to an earlier revision of this code, I
> > don't think we should merge this until we decide on some ground rules
> > regarding the support level of this architecture in the UEFI and ACPI
> > subsystems.
> >
> > The problem is that loongarch does not exist in the ACPI or UEFI
> > specifications at all, and as I understand it, the firmware
> > implementations themselves do not implement UEFI or ACPI entirely,
> > they simply present data structures in memory that look similar enough
> > for the Linux UEFI and ACPI code to boot the OS.
>
> Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
> spec groups by the comapny making these devices, or something else?
We have tried our best to make LoongArch parts be in ACPI and UEFI SPECs.

ECR for adding LoongArch support in ACPI:
https://mantis.uefi.org/mantis/view.php?id=2203

ECR for adding LoongArch support in ACPI (version update):
https://mantis.uefi.org/mantis/view.php?id=2268

ECR for adding LoongArch support in UEFI:
https://mantis.uefi.org/mantis/view.php?id=2313

ACPI changes of LoongArch have been approved in the last year, but the
new version of ACPI SPEC hasn't been made public yet. And UEFI changes
of LoongArch are under review now.

Is it a must that the kernel code be merged after all SPECs are
public? If not, I think we can provide some snapshots (If it is legal,
I'm not sure) of mantis.uefi.org to prove the above.

Huacai

>
> > As the UEFI subsystem maintainer, I am concerned that future changes
> > to the UEFI subsystem that are rooted in the the UEFI specification as
> > it evolves may trigger unanticipated results on this architecture, and
> > I imagine the ACPI maintainers may have similar concerns.
>
> I agree, I would be concerned about that as well.
>
> > So what can we do about this? Do we merge this code, but as a second
> > class citizen in terms of UEFI/ACPI subsystem support, i.e., you are
> > welcome to use it, but if something breaks, the UEFI/ACPI maintainers
> > are not on the hook to see to it that it gets fixed? I don't think
> > this is a great solution, but I'm not sure if there are alternatives
> > that are any better.
>
> That's not going to work, it's the kernel's job to fix up hardware
> problems. But as this is all firmware, why can't the firmware be
> changed to properly follow the public specifications? What's wrong with
> requiring that at this point in time (i.e. _BEFORE_ the code is merged)?
>
> thanks,
>
> greg k-h

2022-02-28 10:13:34

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Mon, 28 Feb 2022 at 07:34, Huacai Chen <[email protected]> wrote:
>
> Hi, Ard and Greg,
>
> On Mon, Feb 28, 2022 at 12:40 AM Greg Kroah-Hartman
> <[email protected]> wrote:
> >
> > On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> > > (add Greg and ACPI maintainers)
> > >
> > > On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> > > >
> > > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > > format).
> > > >
> > > > Now the boot information passed to kernel is like this:
> > > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > > type is "efi_system_table_t". Most configuration information, include
> > > > ACPI tables and SMBIOS tables, come from here.
> > > >
> > > > Cc: Ard Biesheuvel <[email protected]>
> > > > Cc: [email protected]
> > > > Signed-off-by: Huacai Chen <[email protected]>
> > > > ---
> > > > arch/loongarch/include/asm/acenv.h | 17 +
> > > > arch/loongarch/include/asm/acpi.h | 38 ++
> > > > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > > > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > > > arch/loongarch/include/asm/dmi.h | 24 ++
> > > > arch/loongarch/include/asm/efi.h | 33 ++
> > > > arch/loongarch/include/asm/fw.h | 18 +
> > > > arch/loongarch/include/asm/reboot.h | 10 +
> > > > arch/loongarch/include/asm/setup.h | 21 +
> > > > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > > > arch/loongarch/kernel/cmdline.c | 31 ++
> > > > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > > > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > > > arch/loongarch/kernel/env.c | 176 +++++++++
> > > > arch/loongarch/kernel/head.S | 72 ++++
> > > > arch/loongarch/kernel/mem.c | 89 +++++
> > > > arch/loongarch/kernel/reset.c | 90 +++++
> > > > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > > > arch/loongarch/kernel/time.c | 220 +++++++++++
> > > > arch/loongarch/kernel/topology.c | 13 +
> > > > 21 files changed, 2450 insertions(+)
> > >
> > > As I pointed out in response to an earlier revision of this code, I
> > > don't think we should merge this until we decide on some ground rules
> > > regarding the support level of this architecture in the UEFI and ACPI
> > > subsystems.
> > >
> > > The problem is that loongarch does not exist in the ACPI or UEFI
> > > specifications at all, and as I understand it, the firmware
> > > implementations themselves do not implement UEFI or ACPI entirely,
> > > they simply present data structures in memory that look similar enough
> > > for the Linux UEFI and ACPI code to boot the OS.
> >
> > Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
> > spec groups by the comapny making these devices, or something else?
> We have tried our best to make LoongArch parts be in ACPI and UEFI SPECs.
>
> ECR for adding LoongArch support in ACPI:
> https://mantis.uefi.org/mantis/view.php?id=2203
>
> ECR for adding LoongArch support in ACPI (version update):
> https://mantis.uefi.org/mantis/view.php?id=2268
>
> ECR for adding LoongArch support in UEFI:
> https://mantis.uefi.org/mantis/view.php?id=2313
>
> ACPI changes of LoongArch have been approved in the last year, but the
> new version of ACPI SPEC hasn't been made public yet. And UEFI changes
> of LoongArch are under review now.
>
> Is it a must that the kernel code be merged after all SPECs are
> public? If not, I think we can provide some snapshots (If it is legal,
> I'm not sure) of mantis.uefi.org to prove the above.
>

Thanks for the links, those with access will be able to review,
although it would of course be preferable if this was open access.

In any case, if UEFI and ACPI support is going to be ratified in the
respective specifications, we are in a much better place to support
this in Linux going forward.

However, that still doesn't mean you should be using the internal API
used between the EFI stub and the core kernel as a boot interface.
Instead, you should implement LoongArch support into the EFI stub, and
build the kernel as a PE/COFF image that can boot from EFI directly,
from UEFI compliant firmware (u-boot or EDK2 are the most common
examples) that exposes all the UEFI stuff that the EFI stub relies on.

RISC-V is a useful reference for the changes needed - this is the most
recent addition to the EFI stub, and avoids some legacy stuff that new
architectures have no need for.

Alternatively, if ACPI is what you are after mostly, to describe the
platform to the OS, you could expose the ACPI tables to the OS without
relying on UEFI, although this should be part of the LoongArch
bindings in the ACPI spec too.

--
Ard.

2022-02-28 11:11:23

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard,

On Mon, Feb 28, 2022 at 4:09 PM Ard Biesheuvel <[email protected]> wrote:
>
> On Mon, 28 Feb 2022 at 07:34, Huacai Chen <[email protected]> wrote:
> >
> > Hi, Ard and Greg,
> >
> > On Mon, Feb 28, 2022 at 12:40 AM Greg Kroah-Hartman
> > <[email protected]> wrote:
> > >
> > > On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> > > > (add Greg and ACPI maintainers)
> > > >
> > > > On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> > > > >
> > > > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > > > format).
> > > > >
> > > > > Now the boot information passed to kernel is like this:
> > > > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > > > type is "efi_system_table_t". Most configuration information, include
> > > > > ACPI tables and SMBIOS tables, come from here.
> > > > >
> > > > > Cc: Ard Biesheuvel <[email protected]>
> > > > > Cc: [email protected]
> > > > > Signed-off-by: Huacai Chen <[email protected]>
> > > > > ---
> > > > > arch/loongarch/include/asm/acenv.h | 17 +
> > > > > arch/loongarch/include/asm/acpi.h | 38 ++
> > > > > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > > > > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > > > > arch/loongarch/include/asm/dmi.h | 24 ++
> > > > > arch/loongarch/include/asm/efi.h | 33 ++
> > > > > arch/loongarch/include/asm/fw.h | 18 +
> > > > > arch/loongarch/include/asm/reboot.h | 10 +
> > > > > arch/loongarch/include/asm/setup.h | 21 +
> > > > > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > > > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > > > > arch/loongarch/kernel/cmdline.c | 31 ++
> > > > > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > > > > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > > > > arch/loongarch/kernel/env.c | 176 +++++++++
> > > > > arch/loongarch/kernel/head.S | 72 ++++
> > > > > arch/loongarch/kernel/mem.c | 89 +++++
> > > > > arch/loongarch/kernel/reset.c | 90 +++++
> > > > > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > > > > arch/loongarch/kernel/time.c | 220 +++++++++++
> > > > > arch/loongarch/kernel/topology.c | 13 +
> > > > > 21 files changed, 2450 insertions(+)
> > > >
> > > > As I pointed out in response to an earlier revision of this code, I
> > > > don't think we should merge this until we decide on some ground rules
> > > > regarding the support level of this architecture in the UEFI and ACPI
> > > > subsystems.
> > > >
> > > > The problem is that loongarch does not exist in the ACPI or UEFI
> > > > specifications at all, and as I understand it, the firmware
> > > > implementations themselves do not implement UEFI or ACPI entirely,
> > > > they simply present data structures in memory that look similar enough
> > > > for the Linux UEFI and ACPI code to boot the OS.
> > >
> > > Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
> > > spec groups by the comapny making these devices, or something else?
> > We have tried our best to make LoongArch parts be in ACPI and UEFI SPECs.
> >
> > ECR for adding LoongArch support in ACPI:
> > https://mantis.uefi.org/mantis/view.php?id=2203
> >
> > ECR for adding LoongArch support in ACPI (version update):
> > https://mantis.uefi.org/mantis/view.php?id=2268
> >
> > ECR for adding LoongArch support in UEFI:
> > https://mantis.uefi.org/mantis/view.php?id=2313
> >
> > ACPI changes of LoongArch have been approved in the last year, but the
> > new version of ACPI SPEC hasn't been made public yet. And UEFI changes
> > of LoongArch are under review now.
> >
> > Is it a must that the kernel code be merged after all SPECs are
> > public? If not, I think we can provide some snapshots (If it is legal,
> > I'm not sure) of mantis.uefi.org to prove the above.
> >
>
> Thanks for the links, those with access will be able to review,
> although it would of course be preferable if this was open access.
>
> In any case, if UEFI and ACPI support is going to be ratified in the
> respective specifications, we are in a much better place to support
> this in Linux going forward.
>
> However, that still doesn't mean you should be using the internal API
> used between the EFI stub and the core kernel as a boot interface.
> Instead, you should implement LoongArch support into the EFI stub, and
> build the kernel as a PE/COFF image that can boot from EFI directly,
> from UEFI compliant firmware (u-boot or EDK2 are the most common
> examples) that exposes all the UEFI stuff that the EFI stub relies on.
We have implemented EFISTUB, but not in this first series:
https://github.com/loongson/linux/commit/d415a8e57e4d248e239958f2f18b45ea7a5fec2c
We want to add efistub support in the next series after new UEFI SPEC released.

>
> RISC-V is a useful reference for the changes needed - this is the most
> recent addition to the EFI stub, and avoids some legacy stuff that new
> architectures have no need for.
We still want to support the raw elf kernel (RISC-V also does),
because LoongArch also has MCU and SoC and we want to support FDT (I
think this is reasonable, because RISC-V also supports raw elf).

Huacai
>
> Alternatively, if ACPI is what you are after mostly, to describe the
> platform to the OS, you could expose the ACPI tables to the OS without
> relying on UEFI, although this should be part of the LoongArch
> bindings in the ACPI spec too.
>
> --
> Ard.

2022-02-28 14:25:50

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard,

On Mon, Feb 28, 2022 at 4:52 PM Ard Biesheuvel <[email protected]> wrote:
>
> On Mon, 28 Feb 2022 at 09:38, Huacai Chen <[email protected]> wrote:
> >
> > Hi, Ard,
> >
> > On Mon, Feb 28, 2022 at 4:09 PM Ard Biesheuvel <[email protected]> wrote:
> > >
> > > On Mon, 28 Feb 2022 at 07:34, Huacai Chen <[email protected]> wrote:
> > > >
> > > > Hi, Ard and Greg,
> > > >
> > > > On Mon, Feb 28, 2022 at 12:40 AM Greg Kroah-Hartman
> > > > <[email protected]> wrote:
> > > > >
> > > > > On Sun, Feb 27, 2022 at 03:14:30PM +0100, Ard Biesheuvel wrote:
> > > > > > (add Greg and ACPI maintainers)
> > > > > >
> > > > > > On Sat, 26 Feb 2022 at 12:11, Huacai Chen <[email protected]> wrote:
> > > > > > >
> > > > > > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > > > > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > > > > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > > > > > format).
> > > > > > >
> > > > > > > Now the boot information passed to kernel is like this:
> > > > > > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > > > > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > > > > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > > > > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > > > > > type is "efi_system_table_t". Most configuration information, include
> > > > > > > ACPI tables and SMBIOS tables, come from here.
> > > > > > >
> > > > > > > Cc: Ard Biesheuvel <[email protected]>
> > > > > > > Cc: [email protected]
> > > > > > > Signed-off-by: Huacai Chen <[email protected]>
> > > > > > > ---
> > > > > > > arch/loongarch/include/asm/acenv.h | 17 +
> > > > > > > arch/loongarch/include/asm/acpi.h | 38 ++
> > > > > > > arch/loongarch/include/asm/boot_param.h | 97 +++++
> > > > > > > arch/loongarch/include/asm/bootinfo.h | 33 ++
> > > > > > > arch/loongarch/include/asm/dmi.h | 24 ++
> > > > > > > arch/loongarch/include/asm/efi.h | 33 ++
> > > > > > > arch/loongarch/include/asm/fw.h | 18 +
> > > > > > > arch/loongarch/include/asm/reboot.h | 10 +
> > > > > > > arch/loongarch/include/asm/setup.h | 21 +
> > > > > > > arch/loongarch/kernel/acpi.c | 338 ++++++++++++++++
> > > > > > > arch/loongarch/kernel/cacheinfo.c | 122 ++++++
> > > > > > > arch/loongarch/kernel/cmdline.c | 31 ++
> > > > > > > arch/loongarch/kernel/cpu-probe.c | 305 +++++++++++++++
> > > > > > > arch/loongarch/kernel/efi.c | 208 ++++++++++
> > > > > > > arch/loongarch/kernel/env.c | 176 +++++++++
> > > > > > > arch/loongarch/kernel/head.S | 72 ++++
> > > > > > > arch/loongarch/kernel/mem.c | 89 +++++
> > > > > > > arch/loongarch/kernel/reset.c | 90 +++++
> > > > > > > arch/loongarch/kernel/setup.c | 495 ++++++++++++++++++++++++
> > > > > > > arch/loongarch/kernel/time.c | 220 +++++++++++
> > > > > > > arch/loongarch/kernel/topology.c | 13 +
> > > > > > > 21 files changed, 2450 insertions(+)
> > > > > >
> > > > > > As I pointed out in response to an earlier revision of this code, I
> > > > > > don't think we should merge this until we decide on some ground rules
> > > > > > regarding the support level of this architecture in the UEFI and ACPI
> > > > > > subsystems.
> > > > > >
> > > > > > The problem is that loongarch does not exist in the ACPI or UEFI
> > > > > > specifications at all, and as I understand it, the firmware
> > > > > > implementations themselves do not implement UEFI or ACPI entirely,
> > > > > > they simply present data structures in memory that look similar enough
> > > > > > for the Linux UEFI and ACPI code to boot the OS.
> > > > >
> > > > > Why isn't this in the ACPI/UEFI specs? Is it a lack of access to the
> > > > > spec groups by the comapny making these devices, or something else?
> > > > We have tried our best to make LoongArch parts be in ACPI and UEFI SPECs.
> > > >
> > > > ECR for adding LoongArch support in ACPI:
> > > > https://mantis.uefi.org/mantis/view.php?id=2203
> > > >
> > > > ECR for adding LoongArch support in ACPI (version update):
> > > > https://mantis.uefi.org/mantis/view.php?id=2268
> > > >
> > > > ECR for adding LoongArch support in UEFI:
> > > > https://mantis.uefi.org/mantis/view.php?id=2313
> > > >
> > > > ACPI changes of LoongArch have been approved in the last year, but the
> > > > new version of ACPI SPEC hasn't been made public yet. And UEFI changes
> > > > of LoongArch are under review now.
> > > >
> > > > Is it a must that the kernel code be merged after all SPECs are
> > > > public? If not, I think we can provide some snapshots (If it is legal,
> > > > I'm not sure) of mantis.uefi.org to prove the above.
> > > >
> > >
> > > Thanks for the links, those with access will be able to review,
> > > although it would of course be preferable if this was open access.
> > >
> > > In any case, if UEFI and ACPI support is going to be ratified in the
> > > respective specifications, we are in a much better place to support
> > > this in Linux going forward.
> > >
> > > However, that still doesn't mean you should be using the internal API
> > > used between the EFI stub and the core kernel as a boot interface.
> > > Instead, you should implement LoongArch support into the EFI stub, and
> > > build the kernel as a PE/COFF image that can boot from EFI directly,
> > > from UEFI compliant firmware (u-boot or EDK2 are the most common
> > > examples) that exposes all the UEFI stuff that the EFI stub relies on.
> > We have implemented EFISTUB, but not in this first series:
> > https://github.com/loongson/linux/commit/d415a8e57e4d248e239958f2f18b45ea7a5fec2c
> > We want to add efistub support in the next series after new UEFI SPEC released.
> >
> > >
> > > RISC-V is a useful reference for the changes needed - this is the most
> > > recent addition to the EFI stub, and avoids some legacy stuff that new
> > > architectures have no need for.
> > We still want to support the raw elf kernel (RISC-V also does),
> > because LoongArch also has MCU and SoC and we want to support FDT (I
> > think this is reasonable, because RISC-V also supports raw elf).
> >
>
> That is fine. So perhaps the best course of action is to omit the
> UEFI/ACPI parts entirely for now, and focus on the DT/embedded use
> case. Once all the spec pieces are in place, the UEFI + ACPI changes
> can be presented as a single coherent set.
It seems that I made you confusing. :)
There are big CPUs and small CPUs (MCU and SoC), big CPUs use
UEFI+ACPI, while small CPUs use FDT.
At present, the only matured LoongArch CPU is Loongson-3A5000 (big
CPU) which uses UEFI+ACPI.
We want to support raw elf because it can run on both ACPI firmware
and FDT firmware, but at present we only have ACPI firmware.

In my commit message, the "a0 a1 a2" usage is really not the interface
between UEFI and kernel, but the interface between BootLoader (grub,
efistub, etc.) and kernel. Grub support is in the first phase, while
efistub support will be added later, so we describe the "a0 a1 a2"
usage here. However, we are not requesting the UEFI firmware to pass
such information directly. :)

Huacai

>
> --
> Ard.

2022-02-28 15:52:28

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> On Mon, Feb 28, 2022 at 4:52 PM Ard Biesheuvel <[email protected]> wrote:
> > On Mon, 28 Feb 2022 at 09:38, Huacai Chen <[email protected]> wrote:
> > > >
> > > > RISC-V is a useful reference for the changes needed - this is the most
> > > > recent addition to the EFI stub, and avoids some legacy stuff that new
> > > > architectures have no need for.
> > > We still want to support the raw elf kernel (RISC-V also does),
> > > because LoongArch also has MCU and SoC and we want to support FDT (I
> > > think this is reasonable, because RISC-V also supports raw elf).
> > >
> >
> > That is fine. So perhaps the best course of action is to omit the
> > UEFI/ACPI parts entirely for now, and focus on the DT/embedded use
> > case. Once all the spec pieces are in place, the UEFI + ACPI changes
> > can be presented as a single coherent set.
> It seems that I made you confusing. :)
> There are big CPUs and small CPUs (MCU and SoC), big CPUs use
> UEFI+ACPI, while small CPUs use FDT.
> At present, the only matured LoongArch CPU is Loongson-3A5000 (big
> CPU) which uses UEFI+ACPI.
> We want to support raw elf because it can run on both ACPI firmware
> and FDT firmware, but at present we only have ACPI firmware.

Can't you just use the UEFI protocol for kernel entry regardless
of the bootloader? It seems odd to use a different protocol for loading
grub and the kernel, especially if that means you end up having to
support both protocols inside of u-boot and grub, in order to chain-load
a uefi application like grub.

Arnd

2022-02-28 17:30:41

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
>
> On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > On Mon, Feb 28, 2022 at 4:52 PM Ard Biesheuvel <[email protected]> wrote:
> > > On Mon, 28 Feb 2022 at 09:38, Huacai Chen <[email protected]> wrote:
> > > > >
> > > > > RISC-V is a useful reference for the changes needed - this is the most
> > > > > recent addition to the EFI stub, and avoids some legacy stuff that new
> > > > > architectures have no need for.
> > > > We still want to support the raw elf kernel (RISC-V also does),
> > > > because LoongArch also has MCU and SoC and we want to support FDT (I
> > > > think this is reasonable, because RISC-V also supports raw elf).
> > > >
> > >
> > > That is fine. So perhaps the best course of action is to omit the
> > > UEFI/ACPI parts entirely for now, and focus on the DT/embedded use
> > > case. Once all the spec pieces are in place, the UEFI + ACPI changes
> > > can be presented as a single coherent set.
> > It seems that I made you confusing. :)
> > There are big CPUs and small CPUs (MCU and SoC), big CPUs use
> > UEFI+ACPI, while small CPUs use FDT.
> > At present, the only matured LoongArch CPU is Loongson-3A5000 (big
> > CPU) which uses UEFI+ACPI.
> > We want to support raw elf because it can run on both ACPI firmware
> > and FDT firmware, but at present we only have ACPI firmware.
>
> Can't you just use the UEFI protocol for kernel entry regardless
> of the bootloader? It seems odd to use a different protocol for loading
> grub and the kernel, especially if that means you end up having to
> support both protocols inside of u-boot and grub, in order to chain-load
> a uefi application like grub.
>

I think this would make sense. Now that the EFI stub has generic
support for loading the initrd via a UEFI specific protocol (of which
u-boot already carries an implementation), booting via UEFI only would
mean that no Linux boot protocol would need to be defined outside of
the kernel at all (i.e., where to load the kernel, where to put the
command line, where to put the initrd, other arch specific rules etc
etc) UEFI already supports both ACPI and DT boot

2022-03-01 05:44:33

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard & Arnd,

On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
>
> On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> >
> > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > On Mon, Feb 28, 2022 at 4:52 PM Ard Biesheuvel <[email protected]> wrote:
> > > > On Mon, 28 Feb 2022 at 09:38, Huacai Chen <[email protected]> wrote:
> > > > > >
> > > > > > RISC-V is a useful reference for the changes needed - this is the most
> > > > > > recent addition to the EFI stub, and avoids some legacy stuff that new
> > > > > > architectures have no need for.
> > > > > We still want to support the raw elf kernel (RISC-V also does),
> > > > > because LoongArch also has MCU and SoC and we want to support FDT (I
> > > > > think this is reasonable, because RISC-V also supports raw elf).
> > > > >
> > > >
> > > > That is fine. So perhaps the best course of action is to omit the
> > > > UEFI/ACPI parts entirely for now, and focus on the DT/embedded use
> > > > case. Once all the spec pieces are in place, the UEFI + ACPI changes
> > > > can be presented as a single coherent set.
> > > It seems that I made you confusing. :)
> > > There are big CPUs and small CPUs (MCU and SoC), big CPUs use
> > > UEFI+ACPI, while small CPUs use FDT.
> > > At present, the only matured LoongArch CPU is Loongson-3A5000 (big
> > > CPU) which uses UEFI+ACPI.
> > > We want to support raw elf because it can run on both ACPI firmware
> > > and FDT firmware, but at present we only have ACPI firmware.
> >
> > Can't you just use the UEFI protocol for kernel entry regardless
> > of the bootloader? It seems odd to use a different protocol for loading
> > grub and the kernel, especially if that means you end up having to
> > support both protocols inside of u-boot and grub, in order to chain-load
> > a uefi application like grub.
> >
>
> I think this would make sense. Now that the EFI stub has generic
> support for loading the initrd via a UEFI specific protocol (of which
> u-boot already carries an implementation), booting via UEFI only would
> mean that no Linux boot protocol would need to be defined outside of
> the kernel at all (i.e., where to load the kernel, where to put the
> command line, where to put the initrd, other arch specific rules etc
> etc) UEFI already supports both ACPI and DT boot

After one night thinking, I agree with Ard that we can use RISCV-style
fdt to support the raw elf kernel at present, and add efistub support
after new UEFI SPEC released.

If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
And in my opinion, the main drawback of current LoongArch method
(a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
non-standard method to pass kernel args and initrd. So, can the below
new solution be acceptable?

a0=bootparamsinterface pointer (the same as a2 in current method)
a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
this is the standard method)

Huacai

2022-03-01 12:00:20

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > Can't you just use the UEFI protocol for kernel entry regardless
> > > of the bootloader? It seems odd to use a different protocol for loading
> > > grub and the kernel, especially if that means you end up having to
> > > support both protocols inside of u-boot and grub, in order to chain-load
> > > a uefi application like grub.
> > >
> >
> > I think this would make sense. Now that the EFI stub has generic
> > support for loading the initrd via a UEFI specific protocol (of which
> > u-boot already carries an implementation), booting via UEFI only would
> > mean that no Linux boot protocol would need to be defined outside of
> > the kernel at all (i.e., where to load the kernel, where to put the
> > command line, where to put the initrd, other arch specific rules etc
> > etc) UEFI already supports both ACPI and DT boot
>
> After one night thinking, I agree with Ard that we can use RISCV-style
> fdt to support the raw elf kernel at present, and add efistub support
> after new UEFI SPEC released.

I think that is the opposite of what Ard and I discussed above.

> If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> And in my opinion, the main drawback of current LoongArch method
> (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> non-standard method to pass kernel args and initrd. So, can the below
> new solution be acceptable?
>
> a0=bootparamsinterface pointer (the same as a2 in current method)
> a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> this is the standard method)

It would seem more logical to me to keep those details as part of the
interface between the EFI stub and the kernel, rather than the
documented boot interface.

You said that there is already grub support using the UEFI
loader, so I assume you have a working draft of the boot
protocol. Are there still open questions about the interface
definition for that preventing you from using it as the only
way to enter the kernel from a bootloader?

Arnd

2022-03-02 09:38:33

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Wed, 2 Mar 2022 at 09:56, Huacai Chen <[email protected]> wrote:
>
> Hi, Arnd & Ard,
>
> On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
> >
> > On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > > grub and the kernel, especially if that means you end up having to
> > > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > > a uefi application like grub.
> > > > >
> > > >
> > > > I think this would make sense. Now that the EFI stub has generic
> > > > support for loading the initrd via a UEFI specific protocol (of which
> > > > u-boot already carries an implementation), booting via UEFI only would
> > > > mean that no Linux boot protocol would need to be defined outside of
> > > > the kernel at all (i.e., where to load the kernel, where to put the
> > > > command line, where to put the initrd, other arch specific rules etc
> > > > etc) UEFI already supports both ACPI and DT boot
> > >
> > > After one night thinking, I agree with Ard that we can use RISCV-style
> > > fdt to support the raw elf kernel at present, and add efistub support
> > > after new UEFI SPEC released.
> >
> > I think that is the opposite of what Ard and I discussed above.
> Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?
>
> >
> > > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > > And in my opinion, the main drawback of current LoongArch method
> > > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > > non-standard method to pass kernel args and initrd. So, can the below
> > > new solution be acceptable?
> > >
> > > a0=bootparamsinterface pointer (the same as a2 in current method)
> > > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > > this is the standard method)
> >
> > It would seem more logical to me to keep those details as part of the
> > interface between the EFI stub and the kernel, rather than the
> > documented boot interface.
> >
> > You said that there is already grub support using the UEFI
> > loader, so I assume you have a working draft of the boot
> > protocol. Are there still open questions about the interface
> > definition for that preventing you from using it as the only
> > way to enter the kernel from a bootloader?
> Things become simple if we only consider efistub rather than raw elf.
> But there are still some problems:
> 1, We want the first patch series as minimal as possible, efistub
> support will add a lot of code.
> 2, EFISTUB hides the interface between bootloader and raw kernel, but
> the interface does actually exist (efistub itself is also a
> bootloader, though it binds with the raw kernel). In the current
> implementation (a0=argc a1=argv a2=bootparaminterface), we should
> select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
> deprecated. Is this acceptable? If not, we still need to change the
> bootloader-kernel interface, maybe use the method in my previous
> email?

Why do you need this?

> 3, I know things without upstream means "nothing" for the community,
> but if we can provide raw elf kernel support to be compatible with
> existing products (not just a working draft, they are widely used
> now), it also seems reasonable.
>
> Huacai
>
> >
> > Arnd

2022-03-02 10:06:52

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard,

On Wed, Mar 2, 2022 at 4:58 PM Ard Biesheuvel <[email protected]> wrote:
>
> On Wed, 2 Mar 2022 at 09:56, Huacai Chen <[email protected]> wrote:
> >
> > Hi, Arnd & Ard,
> >
> > On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
> > >
> > > On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > > > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > > > grub and the kernel, especially if that means you end up having to
> > > > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > > > a uefi application like grub.
> > > > > >
> > > > >
> > > > > I think this would make sense. Now that the EFI stub has generic
> > > > > support for loading the initrd via a UEFI specific protocol (of which
> > > > > u-boot already carries an implementation), booting via UEFI only would
> > > > > mean that no Linux boot protocol would need to be defined outside of
> > > > > the kernel at all (i.e., where to load the kernel, where to put the
> > > > > command line, where to put the initrd, other arch specific rules etc
> > > > > etc) UEFI already supports both ACPI and DT boot
> > > >
> > > > After one night thinking, I agree with Ard that we can use RISCV-style
> > > > fdt to support the raw elf kernel at present, and add efistub support
> > > > after new UEFI SPEC released.
> > >
> > > I think that is the opposite of what Ard and I discussed above.
> > Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?
> >
> > >
> > > > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > > > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > > > And in my opinion, the main drawback of current LoongArch method
> > > > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > > > non-standard method to pass kernel args and initrd. So, can the below
> > > > new solution be acceptable?
> > > >
> > > > a0=bootparamsinterface pointer (the same as a2 in current method)
> > > > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > > > this is the standard method)
> > >
> > > It would seem more logical to me to keep those details as part of the
> > > interface between the EFI stub and the kernel, rather than the
> > > documented boot interface.
> > >
> > > You said that there is already grub support using the UEFI
> > > loader, so I assume you have a working draft of the boot
> > > protocol. Are there still open questions about the interface
> > > definition for that preventing you from using it as the only
> > > way to enter the kernel from a bootloader?
> > Things become simple if we only consider efistub rather than raw elf.
> > But there are still some problems:
> > 1, We want the first patch series as minimal as possible, efistub
> > support will add a lot of code.
> > 2, EFISTUB hides the interface between bootloader and raw kernel, but
> > the interface does actually exist (efistub itself is also a
> > bootloader, though it binds with the raw kernel). In the current
> > implementation (a0=argc a1=argv a2=bootparaminterface), we should
> > select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
> > deprecated. Is this acceptable? If not, we still need to change the
> > bootloader-kernel interface, maybe use the method in my previous
> > email?
>
> Why do you need this?
Because in the current implementation (a0=argc a1=argv
a2=bootparaminterface), initrd should be passed by cmdline
(initrd=xxxx). If without that option, efi_load_initrd_cmdline() will
not call handle_cmdline_files().

Huacai
>
> > 3, I know things without upstream means "nothing" for the community,
> > but if we can provide raw elf kernel support to be compatible with
> > existing products (not just a working draft, they are widely used
> > now), it also seems reasonable.
> >
> > Huacai
> >
> > >
> > > Arnd

2022-03-02 23:31:54

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Arnd & Ard,

On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
>
> On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > grub and the kernel, especially if that means you end up having to
> > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > a uefi application like grub.
> > > >
> > >
> > > I think this would make sense. Now that the EFI stub has generic
> > > support for loading the initrd via a UEFI specific protocol (of which
> > > u-boot already carries an implementation), booting via UEFI only would
> > > mean that no Linux boot protocol would need to be defined outside of
> > > the kernel at all (i.e., where to load the kernel, where to put the
> > > command line, where to put the initrd, other arch specific rules etc
> > > etc) UEFI already supports both ACPI and DT boot
> >
> > After one night thinking, I agree with Ard that we can use RISCV-style
> > fdt to support the raw elf kernel at present, and add efistub support
> > after new UEFI SPEC released.
>
> I think that is the opposite of what Ard and I discussed above.
Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?

>
> > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > And in my opinion, the main drawback of current LoongArch method
> > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > non-standard method to pass kernel args and initrd. So, can the below
> > new solution be acceptable?
> >
> > a0=bootparamsinterface pointer (the same as a2 in current method)
> > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > this is the standard method)
>
> It would seem more logical to me to keep those details as part of the
> interface between the EFI stub and the kernel, rather than the
> documented boot interface.
>
> You said that there is already grub support using the UEFI
> loader, so I assume you have a working draft of the boot
> protocol. Are there still open questions about the interface
> definition for that preventing you from using it as the only
> way to enter the kernel from a bootloader?
Things become simple if we only consider efistub rather than raw elf.
But there are still some problems:
1, We want the first patch series as minimal as possible, efistub
support will add a lot of code.
2, EFISTUB hides the interface between bootloader and raw kernel, but
the interface does actually exist (efistub itself is also a
bootloader, though it binds with the raw kernel). In the current
implementation (a0=argc a1=argv a2=bootparaminterface), we should
select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
deprecated. Is this acceptable? If not, we still need to change the
bootloader-kernel interface, maybe use the method in my previous
email?
3, I know things without upstream means "nothing" for the community,
but if we can provide raw elf kernel support to be compatible with
existing products (not just a working draft, they are widely used
now), it also seems reasonable.

Huacai

>
> Arnd

2022-03-03 08:22:35

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard & Arnd,

On Wed, Mar 2, 2022 at 5:20 PM Huacai Chen <[email protected]> wrote:
>
> Hi, Ard,
>
> On Wed, Mar 2, 2022 at 4:58 PM Ard Biesheuvel <[email protected]> wrote:
> >
> > On Wed, 2 Mar 2022 at 09:56, Huacai Chen <[email protected]> wrote:
> > >
> > > Hi, Arnd & Ard,
> > >
> > > On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
> > > >
> > > > On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > > > > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > > > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > > > > grub and the kernel, especially if that means you end up having to
> > > > > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > > > > a uefi application like grub.
> > > > > > >
> > > > > >
> > > > > > I think this would make sense. Now that the EFI stub has generic
> > > > > > support for loading the initrd via a UEFI specific protocol (of which
> > > > > > u-boot already carries an implementation), booting via UEFI only would
> > > > > > mean that no Linux boot protocol would need to be defined outside of
> > > > > > the kernel at all (i.e., where to load the kernel, where to put the
> > > > > > command line, where to put the initrd, other arch specific rules etc
> > > > > > etc) UEFI already supports both ACPI and DT boot
> > > > >
> > > > > After one night thinking, I agree with Ard that we can use RISCV-style
> > > > > fdt to support the raw elf kernel at present, and add efistub support
> > > > > after new UEFI SPEC released.
> > > >
> > > > I think that is the opposite of what Ard and I discussed above.
> > > Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?
> > >
> > > >
> > > > > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > > > > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > > > > And in my opinion, the main drawback of current LoongArch method
> > > > > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > > > > non-standard method to pass kernel args and initrd. So, can the below
> > > > > new solution be acceptable?
> > > > >
> > > > > a0=bootparamsinterface pointer (the same as a2 in current method)
> > > > > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > > > > this is the standard method)
> > > >
> > > > It would seem more logical to me to keep those details as part of the
> > > > interface between the EFI stub and the kernel, rather than the
> > > > documented boot interface.
> > > >
> > > > You said that there is already grub support using the UEFI
> > > > loader, so I assume you have a working draft of the boot
> > > > protocol. Are there still open questions about the interface
> > > > definition for that preventing you from using it as the only
> > > > way to enter the kernel from a bootloader?
> > > Things become simple if we only consider efistub rather than raw elf.
> > > But there are still some problems:
> > > 1, We want the first patch series as minimal as possible, efistub
> > > support will add a lot of code.
> > > 2, EFISTUB hides the interface between bootloader and raw kernel, but
> > > the interface does actually exist (efistub itself is also a
> > > bootloader, though it binds with the raw kernel). In the current
> > > implementation (a0=argc a1=argv a2=bootparaminterface), we should
> > > select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
> > > deprecated. Is this acceptable? If not, we still need to change the
> > > bootloader-kernel interface, maybe use the method in my previous
> > > email?
> >
> > Why do you need this?
> Because in the current implementation (a0=argc a1=argv
> a2=bootparaminterface), initrd should be passed by cmdline
> (initrd=xxxx). If without that option, efi_load_initrd_cmdline() will
> not call handle_cmdline_files().
It seems I'm wrong. EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER controls
"initrd=xxxx" from BIOS to EFISTUB, but has nothing to do with
a0/a1/a2 usage (which controls the "initrd=xxxx" from efistub to raw
kernel). The real reason is our UEFI BIOS has an old codebase without
LoadFile2 support.

Then, my new questions are:
1, Is EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER an unacceptable option
for a new Arch? If yes, we should backport LoadFile2 support to our
BIOS.
2, We now all agree that EFISTUB is the standard and maybe the only
way in future. But, can we do the efistub work in the second series,
in order to make the first series as minimal as possible? (I will
update the commit message to make it clear that a0/a1/a2 usage is only
an internal interface between efistub and raw kernel).

Huacai

>
> Huacai
> >
> > > 3, I know things without upstream means "nothing" for the community,
> > > but if we can provide raw elf kernel support to be compatible with
> > > existing products (not just a working draft, they are widely used
> > > now), it also seems reasonable.
> > >
> > > Huacai
> > >
> > > >
> > > > Arnd

2022-03-03 10:47:45

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Thu, 3 Mar 2022 at 07:26, Huacai Chen <[email protected]> wrote:
>
> Hi, Ard & Arnd,
>
> On Wed, Mar 2, 2022 at 5:20 PM Huacai Chen <[email protected]> wrote:
> >
> > Hi, Ard,
> >
> > On Wed, Mar 2, 2022 at 4:58 PM Ard Biesheuvel <[email protected]> wrote:
> > >
> > > On Wed, 2 Mar 2022 at 09:56, Huacai Chen <[email protected]> wrote:
> > > >
> > > > Hi, Arnd & Ard,
> > > >
> > > > On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
> > > > >
> > > > > On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > > > > > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > > > > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > > > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > > > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > > > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > > > > > grub and the kernel, especially if that means you end up having to
> > > > > > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > > > > > a uefi application like grub.
> > > > > > > >
> > > > > > >
> > > > > > > I think this would make sense. Now that the EFI stub has generic
> > > > > > > support for loading the initrd via a UEFI specific protocol (of which
> > > > > > > u-boot already carries an implementation), booting via UEFI only would
> > > > > > > mean that no Linux boot protocol would need to be defined outside of
> > > > > > > the kernel at all (i.e., where to load the kernel, where to put the
> > > > > > > command line, where to put the initrd, other arch specific rules etc
> > > > > > > etc) UEFI already supports both ACPI and DT boot
> > > > > >
> > > > > > After one night thinking, I agree with Ard that we can use RISCV-style
> > > > > > fdt to support the raw elf kernel at present, and add efistub support
> > > > > > after new UEFI SPEC released.
> > > > >
> > > > > I think that is the opposite of what Ard and I discussed above.
> > > > Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?
> > > >
> > > > >
> > > > > > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > > > > > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > > > > > And in my opinion, the main drawback of current LoongArch method
> > > > > > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > > > > > non-standard method to pass kernel args and initrd. So, can the below
> > > > > > new solution be acceptable?
> > > > > >
> > > > > > a0=bootparamsinterface pointer (the same as a2 in current method)
> > > > > > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > > > > > this is the standard method)
> > > > >
> > > > > It would seem more logical to me to keep those details as part of the
> > > > > interface between the EFI stub and the kernel, rather than the
> > > > > documented boot interface.
> > > > >
> > > > > You said that there is already grub support using the UEFI
> > > > > loader, so I assume you have a working draft of the boot
> > > > > protocol. Are there still open questions about the interface
> > > > > definition for that preventing you from using it as the only
> > > > > way to enter the kernel from a bootloader?
> > > > Things become simple if we only consider efistub rather than raw elf.
> > > > But there are still some problems:
> > > > 1, We want the first patch series as minimal as possible, efistub
> > > > support will add a lot of code.
> > > > 2, EFISTUB hides the interface between bootloader and raw kernel, but
> > > > the interface does actually exist (efistub itself is also a
> > > > bootloader, though it binds with the raw kernel). In the current
> > > > implementation (a0=argc a1=argv a2=bootparaminterface), we should
> > > > select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
> > > > deprecated. Is this acceptable? If not, we still need to change the
> > > > bootloader-kernel interface, maybe use the method in my previous
> > > > email?
> > >
> > > Why do you need this?
> > Because in the current implementation (a0=argc a1=argv
> > a2=bootparaminterface), initrd should be passed by cmdline
> > (initrd=xxxx). If without that option, efi_load_initrd_cmdline() will
> > not call handle_cmdline_files().
> It seems I'm wrong. EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER controls
> "initrd=xxxx" from BIOS to EFISTUB, but has nothing to do with
> a0/a1/a2 usage (which controls the "initrd=xxxx" from efistub to raw
> kernel). The real reason is our UEFI BIOS has an old codebase without
> LoadFile2 support.
>

The problem with initrd= is that it can only load the initrd from the
same EFI block device that the kernel was loaded from, which is highly
restrictive, and doesn't work with bootloaders that call LoadImage()
on a kernel image loaded into memory. This is why x86 supports passing
the initrd in memory, and provide the base/size via struct bootparams,
and arm64 supports the same using DT.

The LoadImage2 protocol based method intends to provide a generic
alternative to this, as it uses a pure EFI abstraction, and therefore
does not rely on struct bootparams or DT at all.

So the LoadImage2() based method is preferred, but if your
architecture implements DT support already, there is nothing
preventing you from passing initrd information directly to the kernel
via the /chosen node.

> Then, my new questions are:
> 1, Is EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER an unacceptable option
> for a new Arch? If yes, we should backport LoadFile2 support to our
> BIOS.

See above.

> 2, We now all agree that EFISTUB is the standard and maybe the only
> way in future. But, can we do the efistub work in the second series,
> in order to make the first series as minimal as possible? (I will
> update the commit message to make it clear that a0/a1/a2 usage is only
> an internal interface between efistub and raw kernel).
>

I think it would be better to drop the UEFI and ACPI pieces for now,
and resubmit it once the dust has settled around this.

2022-03-03 13:14:01

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

On Sat, Feb 26, 2022 at 07:03:25PM +0800, Huacai Chen wrote:
> This patch adds basic boot, setup and reset routines for LoongArch.
> LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> SMBIOS to pass configuration information to the Linux kernel (in elf
> format).
>
> Now the boot information passed to kernel is like this:
> 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> type is "efi_system_table_t". Most configuration information, include
> ACPI tables and SMBIOS tables, come from here.
>
> Cc: Ard Biesheuvel <[email protected]>
> Cc: [email protected]
> Signed-off-by: Huacai Chen <[email protected]>
> ---

...

> diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h
> new file mode 100644
> index 000000000000..d2d4b89624f8
> --- /dev/null
> +++ b/arch/loongarch/include/asm/dmi.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_DMI_H
> +#define _ASM_DMI_H
> +
> +#include <linux/io.h>
> +#include <linux/memblock.h>
> +
> +#define dmi_early_remap(x, l) dmi_remap(x, l)
> +#define dmi_early_unmap(x, l) dmi_unmap(x)
> +#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE)

Are there any restrictions on addressing of the memory allocated with
dmi_alloc()?

If no, please use memblock_alloc().

> +
> +static inline void *dmi_remap(u64 phys_addr, unsigned long size)
> +{
> + return ((void *)TO_CAC(phys_addr));
> +}
> +
> +static inline void dmi_unmap(void *addr)
> +{
> +}
> +
> +#endif /* _ASM_DMI_H */

...

> diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
> new file mode 100644
> index 000000000000..3f2101fd19bd
> --- /dev/null
> +++ b/arch/loongarch/kernel/acpi.c
> @@ -0,0 +1,338 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * acpi.c - Architecture-Specific Low-Level ACPI Boot Support
> + *
> + * Author: Jianmin Lv <[email protected]>
> + * Huacai Chen <[email protected]>
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/init.h>
> +#include <linux/acpi.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/memblock.h>
> +#include <linux/serial_core.h>
> +#include <asm/io.h>
> +#include <asm/loongson.h>
> +
> +int acpi_disabled;
> +EXPORT_SYMBOL(acpi_disabled);
> +int acpi_noirq;
> +int acpi_pci_disabled;
> +EXPORT_SYMBOL(acpi_pci_disabled);
> +int acpi_strict = 1; /* We have no workarounds on LoongArch */
> +int num_processors;
> +int disabled_cpus;
> +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
> +
> +u64 acpi_saved_sp;
> +
> +#define MAX_CORE_PIC 256
> +
> +#define PREFIX "ACPI: "
> +
> +int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
> +{
> + if (irqp != NULL)
> + *irqp = acpi_register_gsi(NULL, gsi, -1, -1);
> + return (*irqp >= 0) ? 0 : -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
> +
> +int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
> +{
> + if (gsi)
> + *gsi = isa_irq;
> + return 0;
> +}
> +
> +/*
> + * success: return IRQ number (>=0)
> + * failure: return < 0
> + */
> +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
> +{
> + int id;
> + struct irq_fwspec fwspec;
> +
> + switch (gsi) {
> + case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
> + fwspec.fwnode = liointc_domain->fwnode;
> + fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
> + fwspec.param_count = 1;
> +
> + return irq_create_fwspec_mapping(&fwspec);
> +
> + case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
> + if (!pch_lpc_domain)
> + return -EINVAL;
> +
> + fwspec.fwnode = pch_lpc_domain->fwnode;
> + fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
> + fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
> + fwspec.param_count = 2;
> +
> + return irq_create_fwspec_mapping(&fwspec);
> +
> + case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
> + id = find_pch_pic(gsi);
> + if (id < 0)
> + return -EINVAL;
> +
> + fwspec.fwnode = pch_pic_domain[id]->fwnode;
> + fwspec.param[0] = gsi - acpi_pchpic[id]->gsi_base;
> + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
> + fwspec.param_count = 2;
> +
> + return irq_create_fwspec_mapping(&fwspec);
> + }
> +
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(acpi_register_gsi);
> +
> +void acpi_unregister_gsi(u32 gsi)
> +{
> +
> +}
> +EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
> +
> +void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
> +{
> +
> + if (!phys || !size)
> + return NULL;
> +
> + return early_memremap(phys, size);
> +}
> +void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
> +{
> + if (!map || !size)
> + return;
> +
> + early_memunmap(map, size);
> +}
> +
> +void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
> +{
> + if (!memblock_is_memory(phys))
> + return ioremap(phys, size);

Is it possible that ACPI memory will be backed by a different *physical*
device than system RAM?

> + else
> + return ioremap_cache(phys, size);

If the address is in memory, why it needs to be ioremap'ed?

> +}

...

> +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
> +{
> + memblock_mark_nomap(addr, size);
> +}

Is there any problem if the memory ranges used by ACPI will be mapped into
the kernel page tables?

If not, consider dropping this function.

...

> diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
> new file mode 100644
> index 000000000000..361d108a2b82
> --- /dev/null
> +++ b/arch/loongarch/kernel/mem.c
> @@ -0,0 +1,89 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> + */
> +#include <linux/fs.h>
> +#include <linux/mm.h>
> +#include <linux/memblock.h>
> +
> +#include <asm/bootinfo.h>
> +#include <asm/loongson.h>
> +#include <asm/sections.h>
> +
> +void __init early_memblock_init(void)
> +{
> + int i;
> + u32 mem_type;
> + u64 mem_start, mem_end, mem_size;
> +
> + /* Parse memory information */
> + for (i = 0; i < loongson_mem_map->map_count; i++) {
> + mem_type = loongson_mem_map->map[i].mem_type;
> + mem_start = loongson_mem_map->map[i].mem_start;
> + mem_size = loongson_mem_map->map[i].mem_size;
> + mem_end = mem_start + mem_size;
> +
> + switch (mem_type) {
> + case ADDRESS_TYPE_SYSRAM:
> + memblock_add(mem_start, mem_size);
> + if (max_low_pfn < (mem_end >> PAGE_SHIFT))
> + max_low_pfn = mem_end >> PAGE_SHIFT;
> + break;
> + }
> + }
> + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> +}
> +
> +void __init fw_init_memory(void)
> +{
> + int i;
> + u32 mem_type;
> + u64 mem_start, mem_end, mem_size;
> + unsigned long start_pfn, end_pfn;
> + static unsigned long num_physpages;
> +
> + /* Parse memory information */
> + for (i = 0; i < loongson_mem_map->map_count; i++) {
> + mem_type = loongson_mem_map->map[i].mem_type;
> + mem_start = loongson_mem_map->map[i].mem_start;
> + mem_size = loongson_mem_map->map[i].mem_size;
> + mem_end = mem_start + mem_size;

I think this loop can be merged with loop in early_memblock_init() then ...

> +
> + switch (mem_type) {
> + case ADDRESS_TYPE_SYSRAM:
> + mem_start = PFN_ALIGN(mem_start);
> + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> + num_physpages += (mem_size >> PAGE_SHIFT);
> + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);

this will become memblock_add_node()

> + break;
> + case ADDRESS_TYPE_ACPI:
> + mem_start = PFN_ALIGN(mem_start);
> + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> + num_physpages += (mem_size >> PAGE_SHIFT);
> + memblock_add(mem_start, mem_size);
> + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);

as well as this.

> + memblock_mark_nomap(mem_start, mem_size);

You don't want to use MEMBLOCK_NOMAP unless there is a problem with normal
accesses to this memory.

> + fallthrough;
> + case ADDRESS_TYPE_RESERVED:
> + memblock_reserve(mem_start, mem_size);
> + break;
> + }
> + }
> +
> + get_pfn_range_for_nid(0, &start_pfn, &end_pfn);
> + pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n",
> + start_pfn, end_pfn, num_physpages);
> +
> + NODE_DATA(0)->node_start_pfn = start_pfn;
> + NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn;

This is now handled by the generic code at free_area_init(), no need to
keep it here.

> +
> + /* used by finalize_initrd() */
> + max_low_pfn = end_pfn;
> +
> + /* Reserve the first 2MB */
> + memblock_reserve(PHYS_OFFSET, 0x200000);
> +
> + /* Reserve the kernel text/data/bss */
> + memblock_reserve(__pa_symbol(&_text),
> + __pa_symbol(&_end) - __pa_symbol(&_text));
> +}

...

> diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> new file mode 100644
> index 000000000000..8dfe1d9b55f7
> --- /dev/null
> +++ b/arch/loongarch/kernel/setup.c
> @@ -0,0 +1,495 @@

...

> +/*
> + * Manage initrd
> + */
> +#ifdef CONFIG_BLK_DEV_INITRD
> +
> +static unsigned long __init init_initrd(void)
> +{
> + if (!phys_initrd_start || !phys_initrd_size)
> + goto disable;
> +
> + initrd_start = (unsigned long)phys_to_virt(phys_initrd_start);
> + initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size);
> +
> + if (!initrd_start || initrd_end <= initrd_start)
> + goto disable;
> +
> + if (initrd_start & ~PAGE_MASK) {
> + pr_err("initrd start must be page aligned\n");
> + goto disable;
> + }
> + if (initrd_start < PAGE_OFFSET) {
> + pr_err("initrd start < PAGE_OFFSET\n");
> + goto disable;
> + }
> +
> + ROOT_DEV = Root_RAM0;
> +
> + return 0;
> +disable:
> + initrd_start = 0;
> + initrd_end = 0;
> + return 0;
> +}
> +
> +static void __init finalize_initrd(void)
> +{

Any reason to have this separate function from init_initrd?

> + unsigned long size = initrd_end - initrd_start;
> +
> + if (size == 0) {
> + pr_info("Initrd not found or empty");
> + goto disable;
> + }
> + if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) {
> + pr_err("Initrd extends beyond end of memory");
> + goto disable;
> + }
> +
> +
> + memblock_reserve(__pa(initrd_start), size);
> + initrd_below_start_ok = 1;
> +
> + pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n",
> + initrd_start, size);
> + return;
> +disable:
> + pr_cont(" - disabling initrd\n");
> + initrd_start = 0;
> + initrd_end = 0;
> +}
> +
> +#else /* !CONFIG_BLK_DEV_INITRD */
> +
> +static unsigned long __init init_initrd(void)
> +{
> + return 0;
> +}
> +
> +#define finalize_initrd() do {} while (0)
> +
> +#endif
> +
> +static int usermem __initdata;
> +
> +static int __init early_parse_mem(char *p)
> +{
> + phys_addr_t start, size;
> +
> + /*
> + * If a user specifies memory size, we
> + * blow away any automatically generated
> + * size.
> + */
> + if (usermem == 0) {
> + usermem = 1;
> + memblock_remove(memblock_start_of_DRAM(),
> + memblock_end_of_DRAM() - memblock_start_of_DRAM());
> + }
> + start = 0;
> + size = memparse(p, &p);
> + if (*p == '@')
> + start = memparse(p + 1, &p);
> +
> + memblock_add(start, size);
> +
> + return 0;
> +}
> +early_param("mem", early_parse_mem);
> +
> +static int __init early_parse_memmap(char *p)
> +{
> + char *oldp;
> + u64 start_at, mem_size;
> +
> + if (!p)
> + return -EINVAL;
> +
> + if (!strncmp(p, "exactmap", 8)) {
> + pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
> + return 0;
> + }
> +
> + oldp = p;
> + mem_size = memparse(p, &p);
> + if (p == oldp)
> + return -EINVAL;
> +
> + if (*p == '@') {
> + start_at = memparse(p+1, &p);
> + memblock_add(start_at, mem_size);
> + } else if (*p == '#') {
> + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
> + return -EINVAL;
> + } else if (*p == '$') {
> + start_at = memparse(p+1, &p);
> + memblock_add(start_at, mem_size);
> + memblock_reserve(start_at, mem_size);
> + } else {
> + pr_err("\"memmap\" invalid format!\n");
> + return -EINVAL;
> + }
> +
> + if (*p == '\0') {
> + usermem = 1;
> + return 0;
> + } else
> + return -EINVAL;
> +}
> +early_param("memmap", early_parse_memmap);

The memmap= processing is a hack indented to workaround bugs in firmware
related to the memory detection. Please don't copy if over unless there is
really strong reason.

...

> +/*
> + * arch_mem_init - initialize memory management subsystem
> + */
> +static void __init arch_mem_init(char **cmdline_p)
> +{
> + if (usermem)
> + pr_info("User-defined physical RAM map overwrite\n");
> +
> + check_kernel_sections_mem();
> +
> + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
> +
> + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> +
> + /*
> + * In order to reduce the possibility of kernel panic when failed to
> + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
> + * low memory as small as possible before plat_swiotlb_setup(), so
> + * make sparse_init() using top-down allocation.
> + */
> + memblock_set_bottom_up(false);
> + sparse_init();
> + memblock_set_bottom_up(true);

Does loongarch have the same IO TLB requirements as MIPS?

> +
> + swiotlb_init(1);
> +
> + dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
> +
> + memblock_dump_all();
> +
> + early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
> +}

--
Sincerely yours,
Mike.

2022-03-03 16:05:25

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Mike,

On Thu, Mar 3, 2022 at 7:41 PM Mike Rapoport <[email protected]> wrote:
>
> On Sat, Feb 26, 2022 at 07:03:25PM +0800, Huacai Chen wrote:
> > This patch adds basic boot, setup and reset routines for LoongArch.
> > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > SMBIOS to pass configuration information to the Linux kernel (in elf
> > format).
> >
> > Now the boot information passed to kernel is like this:
> > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > type is "efi_system_table_t". Most configuration information, include
> > ACPI tables and SMBIOS tables, come from here.
> >
> > Cc: Ard Biesheuvel <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Huacai Chen <[email protected]>
> > ---
>
> ...
>
> > diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h
> > new file mode 100644
> > index 000000000000..d2d4b89624f8
> > --- /dev/null
> > +++ b/arch/loongarch/include/asm/dmi.h
> > @@ -0,0 +1,24 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> > + */
> > +#ifndef _ASM_DMI_H
> > +#define _ASM_DMI_H
> > +
> > +#include <linux/io.h>
> > +#include <linux/memblock.h>
> > +
> > +#define dmi_early_remap(x, l) dmi_remap(x, l)
> > +#define dmi_early_unmap(x, l) dmi_unmap(x)
> > +#define dmi_alloc(l) memblock_alloc_low(l, PAGE_SIZE)
>
> Are there any restrictions on addressing of the memory allocated with
> dmi_alloc()?
>
> If no, please use memblock_alloc().
OK, I will try memblock_alloc().
>
> > +
> > +static inline void *dmi_remap(u64 phys_addr, unsigned long size)
> > +{
> > + return ((void *)TO_CAC(phys_addr));
> > +}
> > +
> > +static inline void dmi_unmap(void *addr)
> > +{
> > +}
> > +
> > +#endif /* _ASM_DMI_H */
>
> ...
>
> > diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
> > new file mode 100644
> > index 000000000000..3f2101fd19bd
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/acpi.c
> > @@ -0,0 +1,338 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * acpi.c - Architecture-Specific Low-Level ACPI Boot Support
> > + *
> > + * Author: Jianmin Lv <[email protected]>
> > + * Huacai Chen <[email protected]>
> > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/acpi.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/memblock.h>
> > +#include <linux/serial_core.h>
> > +#include <asm/io.h>
> > +#include <asm/loongson.h>
> > +
> > +int acpi_disabled;
> > +EXPORT_SYMBOL(acpi_disabled);
> > +int acpi_noirq;
> > +int acpi_pci_disabled;
> > +EXPORT_SYMBOL(acpi_pci_disabled);
> > +int acpi_strict = 1; /* We have no workarounds on LoongArch */
> > +int num_processors;
> > +int disabled_cpus;
> > +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
> > +
> > +u64 acpi_saved_sp;
> > +
> > +#define MAX_CORE_PIC 256
> > +
> > +#define PREFIX "ACPI: "
> > +
> > +int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
> > +{
> > + if (irqp != NULL)
> > + *irqp = acpi_register_gsi(NULL, gsi, -1, -1);
> > + return (*irqp >= 0) ? 0 : -EINVAL;
> > +}
> > +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
> > +
> > +int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
> > +{
> > + if (gsi)
> > + *gsi = isa_irq;
> > + return 0;
> > +}
> > +
> > +/*
> > + * success: return IRQ number (>=0)
> > + * failure: return < 0
> > + */
> > +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
> > +{
> > + int id;
> > + struct irq_fwspec fwspec;
> > +
> > + switch (gsi) {
> > + case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
> > + fwspec.fwnode = liointc_domain->fwnode;
> > + fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
> > + fwspec.param_count = 1;
> > +
> > + return irq_create_fwspec_mapping(&fwspec);
> > +
> > + case GSI_MIN_LPC_IRQ ... GSI_MAX_LPC_IRQ:
> > + if (!pch_lpc_domain)
> > + return -EINVAL;
> > +
> > + fwspec.fwnode = pch_lpc_domain->fwnode;
> > + fwspec.param[0] = gsi - GSI_MIN_LPC_IRQ;
> > + fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
> > + fwspec.param_count = 2;
> > +
> > + return irq_create_fwspec_mapping(&fwspec);
> > +
> > + case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
> > + id = find_pch_pic(gsi);
> > + if (id < 0)
> > + return -EINVAL;
> > +
> > + fwspec.fwnode = pch_pic_domain[id]->fwnode;
> > + fwspec.param[0] = gsi - acpi_pchpic[id]->gsi_base;
> > + fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
> > + fwspec.param_count = 2;
> > +
> > + return irq_create_fwspec_mapping(&fwspec);
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +EXPORT_SYMBOL_GPL(acpi_register_gsi);
> > +
> > +void acpi_unregister_gsi(u32 gsi)
> > +{
> > +
> > +}
> > +EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
> > +
> > +void __init __iomem * __acpi_map_table(unsigned long phys, unsigned long size)
> > +{
> > +
> > + if (!phys || !size)
> > + return NULL;
> > +
> > + return early_memremap(phys, size);
> > +}
> > +void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
> > +{
> > + if (!map || !size)
> > + return;
> > +
> > + early_memunmap(map, size);
> > +}
> > +
> > +void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
> > +{
> > + if (!memblock_is_memory(phys))
> > + return ioremap(phys, size);
>
> Is it possible that ACPI memory will be backed by a different *physical*
> device than system RAM?
Because acpi_os_ioremap() is used both for ACPI memory and ACPI
registers, registers need uncached maps.

>
> > + else
> > + return ioremap_cache(phys, size);
>
> If the address is in memory, why it needs to be ioremap'ed?
The default acpi_os_ioremap() implementation is ioremap_cache(), so we
keep it for memory.

>
> > +}
>
> ...
>
> > +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
> > +{
> > + memblock_mark_nomap(addr, size);
> > +}
>
> Is there any problem if the memory ranges used by ACPI will be mapped into
> the kernel page tables?
>
> If not, consider dropping this function.
This API is mostly used for ACPI upgrading. ACPI upgrading alloc a
normal memory block and then is used as ACPI memory, and this memory
block will not be used by the page allocator. Other architectures,
such as ARM64, do the same thing here.

>
> ...
>
> > diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
> > new file mode 100644
> > index 000000000000..361d108a2b82
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/mem.c
> > @@ -0,0 +1,89 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> > + */
> > +#include <linux/fs.h>
> > +#include <linux/mm.h>
> > +#include <linux/memblock.h>
> > +
> > +#include <asm/bootinfo.h>
> > +#include <asm/loongson.h>
> > +#include <asm/sections.h>
> > +
> > +void __init early_memblock_init(void)
> > +{
> > + int i;
> > + u32 mem_type;
> > + u64 mem_start, mem_end, mem_size;
> > +
> > + /* Parse memory information */
> > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > + mem_type = loongson_mem_map->map[i].mem_type;
> > + mem_start = loongson_mem_map->map[i].mem_start;
> > + mem_size = loongson_mem_map->map[i].mem_size;
> > + mem_end = mem_start + mem_size;
> > +
> > + switch (mem_type) {
> > + case ADDRESS_TYPE_SYSRAM:
> > + memblock_add(mem_start, mem_size);
> > + if (max_low_pfn < (mem_end >> PAGE_SHIFT))
> > + max_low_pfn = mem_end >> PAGE_SHIFT;
> > + break;
> > + }
> > + }
> > + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> > +}
> > +
> > +void __init fw_init_memory(void)
> > +{
> > + int i;
> > + u32 mem_type;
> > + u64 mem_start, mem_end, mem_size;
> > + unsigned long start_pfn, end_pfn;
> > + static unsigned long num_physpages;
> > +
> > + /* Parse memory information */
> > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > + mem_type = loongson_mem_map->map[i].mem_type;
> > + mem_start = loongson_mem_map->map[i].mem_start;
> > + mem_size = loongson_mem_map->map[i].mem_size;
> > + mem_end = mem_start + mem_size;
>
> I think this loop can be merged with loop in early_memblock_init() then ...
>
> > +
> > + switch (mem_type) {
> > + case ADDRESS_TYPE_SYSRAM:
> > + mem_start = PFN_ALIGN(mem_start);
> > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > + num_physpages += (mem_size >> PAGE_SHIFT);
> > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
>
> this will become memblock_add_node()
>
> > + break;
> > + case ADDRESS_TYPE_ACPI:
> > + mem_start = PFN_ALIGN(mem_start);
> > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > + num_physpages += (mem_size >> PAGE_SHIFT);
> > + memblock_add(mem_start, mem_size);
> > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
>
> as well as this.
early_memblock_init() only adds the "usable" memory (SYSRAM) for early
use and without numa node information. Other types of memory are
handled later by fw_init_memory()/fw_init_numa_memory(), depending on
whether CONFIG_NUMA is enabled. So, in
fw_init_memory()/fw_init_numa_memory() we only need to call
memblock_set_node() to add the node information for SYSRAM type.

>
> > + memblock_mark_nomap(mem_start, mem_size);
>
> You don't want to use MEMBLOCK_NOMAP unless there is a problem with normal
> accesses to this memory.
>
> > + fallthrough;
> > + case ADDRESS_TYPE_RESERVED:
> > + memblock_reserve(mem_start, mem_size);
> > + break;
> > + }
> > + }
> > +
> > + get_pfn_range_for_nid(0, &start_pfn, &end_pfn);
> > + pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n",
> > + start_pfn, end_pfn, num_physpages);
> > +
> > + NODE_DATA(0)->node_start_pfn = start_pfn;
> > + NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn;
>
> This is now handled by the generic code at free_area_init(), no need to
> keep it here.
OK, thanks.

>
> > +
> > + /* used by finalize_initrd() */
> > + max_low_pfn = end_pfn;
> > +
> > + /* Reserve the first 2MB */
> > + memblock_reserve(PHYS_OFFSET, 0x200000);
> > +
> > + /* Reserve the kernel text/data/bss */
> > + memblock_reserve(__pa_symbol(&_text),
> > + __pa_symbol(&_end) - __pa_symbol(&_text));
> > +}
>
> ...
>
> > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> > new file mode 100644
> > index 000000000000..8dfe1d9b55f7
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/setup.c
> > @@ -0,0 +1,495 @@
>
> ...
>
> > +/*
> > + * Manage initrd
> > + */
> > +#ifdef CONFIG_BLK_DEV_INITRD
> > +
> > +static unsigned long __init init_initrd(void)
> > +{
> > + if (!phys_initrd_start || !phys_initrd_size)
> > + goto disable;
> > +
> > + initrd_start = (unsigned long)phys_to_virt(phys_initrd_start);
> > + initrd_end = (unsigned long)phys_to_virt(phys_initrd_start + phys_initrd_size);
> > +
> > + if (!initrd_start || initrd_end <= initrd_start)
> > + goto disable;
> > +
> > + if (initrd_start & ~PAGE_MASK) {
> > + pr_err("initrd start must be page aligned\n");
> > + goto disable;
> > + }
> > + if (initrd_start < PAGE_OFFSET) {
> > + pr_err("initrd start < PAGE_OFFSET\n");
> > + goto disable;
> > + }
> > +
> > + ROOT_DEV = Root_RAM0;
> > +
> > + return 0;
> > +disable:
> > + initrd_start = 0;
> > + initrd_end = 0;
> > + return 0;
> > +}
> > +
> > +static void __init finalize_initrd(void)
> > +{
>
> Any reason to have this separate function from init_initrd?
OK, I'll try to combine them.

>
> > + unsigned long size = initrd_end - initrd_start;
> > +
> > + if (size == 0) {
> > + pr_info("Initrd not found or empty");
> > + goto disable;
> > + }
> > + if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) {
> > + pr_err("Initrd extends beyond end of memory");
> > + goto disable;
> > + }
> > +
> > +
> > + memblock_reserve(__pa(initrd_start), size);
> > + initrd_below_start_ok = 1;
> > +
> > + pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n",
> > + initrd_start, size);
> > + return;
> > +disable:
> > + pr_cont(" - disabling initrd\n");
> > + initrd_start = 0;
> > + initrd_end = 0;
> > +}
> > +
> > +#else /* !CONFIG_BLK_DEV_INITRD */
> > +
> > +static unsigned long __init init_initrd(void)
> > +{
> > + return 0;
> > +}
> > +
> > +#define finalize_initrd() do {} while (0)
> > +
> > +#endif
> > +
> > +static int usermem __initdata;
> > +
> > +static int __init early_parse_mem(char *p)
> > +{
> > + phys_addr_t start, size;
> > +
> > + /*
> > + * If a user specifies memory size, we
> > + * blow away any automatically generated
> > + * size.
> > + */
> > + if (usermem == 0) {
> > + usermem = 1;
> > + memblock_remove(memblock_start_of_DRAM(),
> > + memblock_end_of_DRAM() - memblock_start_of_DRAM());
> > + }
> > + start = 0;
> > + size = memparse(p, &p);
> > + if (*p == '@')
> > + start = memparse(p + 1, &p);
> > +
> > + memblock_add(start, size);
> > +
> > + return 0;
> > +}
> > +early_param("mem", early_parse_mem);
> > +
> > +static int __init early_parse_memmap(char *p)
> > +{
> > + char *oldp;
> > + u64 start_at, mem_size;
> > +
> > + if (!p)
> > + return -EINVAL;
> > +
> > + if (!strncmp(p, "exactmap", 8)) {
> > + pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
> > + return 0;
> > + }
> > +
> > + oldp = p;
> > + mem_size = memparse(p, &p);
> > + if (p == oldp)
> > + return -EINVAL;
> > +
> > + if (*p == '@') {
> > + start_at = memparse(p+1, &p);
> > + memblock_add(start_at, mem_size);
> > + } else if (*p == '#') {
> > + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
> > + return -EINVAL;
> > + } else if (*p == '$') {
> > + start_at = memparse(p+1, &p);
> > + memblock_add(start_at, mem_size);
> > + memblock_reserve(start_at, mem_size);
> > + } else {
> > + pr_err("\"memmap\" invalid format!\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (*p == '\0') {
> > + usermem = 1;
> > + return 0;
> > + } else
> > + return -EINVAL;
> > +}
> > +early_param("memmap", early_parse_memmap);
>
> The memmap= processing is a hack indented to workaround bugs in firmware
> related to the memory detection. Please don't copy if over unless there is
> really strong reason.
Hmmm, I have read the documents, most archs only support mem=limit,
but MIPS support mem=limit@base. memmap not only supports
memmap=limit@base, but also a lot of advanced syntax. LoongArch needs
both limit and limit@base syntax. So can we make our code to support
only mem=limit and memmap=limit@base, and remove all other syntax
here?

>
> ...
>
> > +/*
> > + * arch_mem_init - initialize memory management subsystem
> > + */
> > +static void __init arch_mem_init(char **cmdline_p)
> > +{
> > + if (usermem)
> > + pr_info("User-defined physical RAM map overwrite\n");
> > +
> > + check_kernel_sections_mem();
> > +
> > + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
> > +
> > + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> > +
> > + /*
> > + * In order to reduce the possibility of kernel panic when failed to
> > + * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
> > + * low memory as small as possible before plat_swiotlb_setup(), so
> > + * make sparse_init() using top-down allocation.
> > + */
> > + memblock_set_bottom_up(false);
> > + sparse_init();
> > + memblock_set_bottom_up(true);
>
> Does loongarch have the same IO TLB requirements as MIPS?
Yes, we have a large amount of memory, but there are many 32-bit PCI devices.

>
> > +
> > + swiotlb_init(1);
> > +
> > + dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
> > +
> > + memblock_dump_all();
> > +
> > + early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
> > +}
>
> --
> Sincerely yours,
> Mike.

2022-03-04 14:12:46

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi,

On Thu, Mar 03, 2022 at 10:47:53PM +0800, Huacai Chen wrote:
> Hi, Mike,
>
> On Thu, Mar 3, 2022 at 7:41 PM Mike Rapoport <[email protected]> wrote:
> >
> > On Sat, Feb 26, 2022 at 07:03:25PM +0800, Huacai Chen wrote:
> > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > format).
> > >
> > > Now the boot information passed to kernel is like this:
> > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > type is "efi_system_table_t". Most configuration information, include
> > > ACPI tables and SMBIOS tables, come from here.
> > >
> > > Cc: Ard Biesheuvel <[email protected]>
> > > Cc: [email protected]
> > > Signed-off-by: Huacai Chen <[email protected]>
> > > ---
> >
> > > +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
> > > +{
> > > + memblock_mark_nomap(addr, size);
> > > +}
> >
> > Is there any problem if the memory ranges used by ACPI will be mapped into
> > the kernel page tables?
> >
> > If not, consider dropping this function.
>
> This API is mostly used for ACPI upgrading. ACPI upgrading alloc a
> normal memory block and then is used as ACPI memory, and this memory
> block will not be used by the page allocator. Other architectures,
> such as ARM64, do the same thing here.

ARM64 had quite a lot of issues with NOMAP memory, so I'd recommend to
avoid using memblock_mark_nomap() unless it is required by MMU constraints
on loongarch.

I'm not familiar with loongarch MMU details, so I can only give some
background for NOMAP for you to decide.

Marking memory region NOMAP is required when this region cannot be a part
of the kernel linear mapping because MMU does not allow aliased mappings
with different caching modes. E.g. in ARM64 case, ACPI memory that should
be mapped uncached cannot be mapped as cached in the kernel linear map.

If the memory block should not be used by the page allocator, it should be
memblock_reserve()'ed rather than marked NOMAP.

> > > diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
> > > new file mode 100644
> > > index 000000000000..361d108a2b82
> > > --- /dev/null
> > > +++ b/arch/loongarch/kernel/mem.c
> > > @@ -0,0 +1,89 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> > > + */
> > > +#include <linux/fs.h>
> > > +#include <linux/mm.h>
> > > +#include <linux/memblock.h>
> > > +
> > > +#include <asm/bootinfo.h>
> > > +#include <asm/loongson.h>
> > > +#include <asm/sections.h>
> > > +
> > > +void __init early_memblock_init(void)
> > > +{
> > > + int i;
> > > + u32 mem_type;
> > > + u64 mem_start, mem_end, mem_size;
> > > +
> > > + /* Parse memory information */
> > > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > > + mem_type = loongson_mem_map->map[i].mem_type;
> > > + mem_start = loongson_mem_map->map[i].mem_start;
> > > + mem_size = loongson_mem_map->map[i].mem_size;
> > > + mem_end = mem_start + mem_size;
> > > +
> > > + switch (mem_type) {
> > > + case ADDRESS_TYPE_SYSRAM:
> > > + memblock_add(mem_start, mem_size);
> > > + if (max_low_pfn < (mem_end >> PAGE_SHIFT))
> > > + max_low_pfn = mem_end >> PAGE_SHIFT;
> > > + break;
> > > + }
> > > + }
> > > + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> > > +}
> > > +
> > > +void __init fw_init_memory(void)
> > > +{
> > > + int i;
> > > + u32 mem_type;
> > > + u64 mem_start, mem_end, mem_size;
> > > + unsigned long start_pfn, end_pfn;
> > > + static unsigned long num_physpages;
> > > +
> > > + /* Parse memory information */
> > > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > > + mem_type = loongson_mem_map->map[i].mem_type;
> > > + mem_start = loongson_mem_map->map[i].mem_start;
> > > + mem_size = loongson_mem_map->map[i].mem_size;
> > > + mem_end = mem_start + mem_size;
> >
> > I think this loop can be merged with loop in early_memblock_init() then ...
> >
> > > +
> > > + switch (mem_type) {
> > > + case ADDRESS_TYPE_SYSRAM:
> > > + mem_start = PFN_ALIGN(mem_start);
> > > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > > + num_physpages += (mem_size >> PAGE_SHIFT);
> > > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
> >
> > this will become memblock_add_node()
> >
> > > + break;
> > > + case ADDRESS_TYPE_ACPI:
> > > + mem_start = PFN_ALIGN(mem_start);
> > > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > > + num_physpages += (mem_size >> PAGE_SHIFT);
> > > + memblock_add(mem_start, mem_size);
> > > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
> >
> > as well as this.
> early_memblock_init() only adds the "usable" memory (SYSRAM) for early
> use and without numa node information. Other types of memory are
> handled later by fw_init_memory()/fw_init_numa_memory(), depending on
> whether CONFIG_NUMA is enabled. So, in
> fw_init_memory()/fw_init_numa_memory() we only need to call
> memblock_set_node() to add the node information for SYSRAM type.

There are two potential issues here with doing memblock_add() and
memblock_set_node() and memblock_reserve() separately with a couple of
functions called in between.

First, and most important is that you must to memblock_reserve() all the
memory used by the firmware, like ADDRESS_TYPE_ACPI, ADDRESS_TYPE_RESERVED,
kernel image, initrd etc *before* any call to memblock_alloc*()
functions. If you add memory to memblock before reserving firmware regions,
a call to memblock_alloc*() may allocate the used memory and all kinds of
errors may happen because of that.

Second, presuming you use SRAT for NUMA information, if you set nodes in
memblock after there were memory allocations from memblock you may impair
the ability to hot-remove memory banks.

So ideally, the physical memory detection and registration should follow
something like:

* memblock_reserve() the memory used by firmware, kernel and initrd
* detect NUMA topology
* add memory regions along with their node ids to memblock.

s390::setup_arch() is a good example of doing early reservations:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/s390/kernel/setup.c#n988

> > > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> > > new file mode 100644
> > > index 000000000000..8dfe1d9b55f7
> > > --- /dev/null
> > > +++ b/arch/loongarch/kernel/setup.c
> > > +
> > > +static int usermem __initdata;
> > > +
> > > +static int __init early_parse_mem(char *p)
> > > +{
> > > + phys_addr_t start, size;
> > > +
> > > + /*
> > > + * If a user specifies memory size, we
> > > + * blow away any automatically generated
> > > + * size.
> > > + */
> > > + if (usermem == 0) {
> > > + usermem = 1;
> > > + memblock_remove(memblock_start_of_DRAM(),
> > > + memblock_end_of_DRAM() - memblock_start_of_DRAM());
> > > + }
> > > + start = 0;
> > > + size = memparse(p, &p);
> > > + if (*p == '@')
> > > + start = memparse(p + 1, &p);
> > > +
> > > + memblock_add(start, size);
> > > +
> > > + return 0;
> > > +}
> > > +early_param("mem", early_parse_mem);
> > > +
> > > +static int __init early_parse_memmap(char *p)
> > > +{
> > > + char *oldp;
> > > + u64 start_at, mem_size;
> > > +
> > > + if (!p)
> > > + return -EINVAL;
> > > +
> > > + if (!strncmp(p, "exactmap", 8)) {
> > > + pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
> > > + return 0;
> > > + }
> > > +
> > > + oldp = p;
> > > + mem_size = memparse(p, &p);
> > > + if (p == oldp)
> > > + return -EINVAL;
> > > +
> > > + if (*p == '@') {
> > > + start_at = memparse(p+1, &p);
> > > + memblock_add(start_at, mem_size);
> > > + } else if (*p == '#') {
> > > + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
> > > + return -EINVAL;
> > > + } else if (*p == '$') {
> > > + start_at = memparse(p+1, &p);
> > > + memblock_add(start_at, mem_size);
> > > + memblock_reserve(start_at, mem_size);
> > > + } else {
> > > + pr_err("\"memmap\" invalid format!\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (*p == '\0') {
> > > + usermem = 1;
> > > + return 0;
> > > + } else
> > > + return -EINVAL;
> > > +}
> > > +early_param("memmap", early_parse_memmap);
> >
> > The memmap= processing is a hack indented to workaround bugs in firmware
> > related to the memory detection. Please don't copy if over unless there is
> > really strong reason.
>
> Hmmm, I have read the documents, most archs only support mem=limit,
> but MIPS support mem=limit@base. memmap not only supports
> memmap=limit@base, but also a lot of advanced syntax. LoongArch needs
> both limit and limit@base syntax. So can we make our code to support
> only mem=limit and memmap=limit@base, and remove all other syntax
> here?

The documentation describes what was there historically and both these
options tend not to play well with complex memory layouts.

If you must have them it's better to use x86 as an example rather than
MIPS, just take into the account that on x86 memory always starts from 0,
so they never needed to have a different base.

For what use-cases LoongArch needs options?

--
Sincerely yours,
Mike.

2022-03-04 14:20:44

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Mike,

On Fri, Mar 4, 2022 at 6:49 PM Mike Rapoport <[email protected]> wrote:
>
> Hi,
>
> On Thu, Mar 03, 2022 at 10:47:53PM +0800, Huacai Chen wrote:
> > Hi, Mike,
> >
> > On Thu, Mar 3, 2022 at 7:41 PM Mike Rapoport <[email protected]> wrote:
> > >
> > > On Sat, Feb 26, 2022 at 07:03:25PM +0800, Huacai Chen wrote:
> > > > This patch adds basic boot, setup and reset routines for LoongArch.
> > > > LoongArch uses UEFI-based firmware. The firmware uses ACPI and DMI/
> > > > SMBIOS to pass configuration information to the Linux kernel (in elf
> > > > format).
> > > >
> > > > Now the boot information passed to kernel is like this:
> > > > 1, kernel get 3 register values (a0, a1 and a2) from bootloader.
> > > > 2, a0 is "argc", a1 is "argv", so "kernel cmdline" comes from a0/a1.
> > > > 3, a2 is "environ", which is a pointer to "struct bootparamsinterface".
> > > > 4, "struct bootparamsinterface" include a "systemtable" pointer, whose
> > > > type is "efi_system_table_t". Most configuration information, include
> > > > ACPI tables and SMBIOS tables, come from here.
> > > >
> > > > Cc: Ard Biesheuvel <[email protected]>
> > > > Cc: [email protected]
> > > > Signed-off-by: Huacai Chen <[email protected]>
> > > > ---
> > >
> > > > +void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
> > > > +{
> > > > + memblock_mark_nomap(addr, size);
> > > > +}
> > >
> > > Is there any problem if the memory ranges used by ACPI will be mapped into
> > > the kernel page tables?
> > >
> > > If not, consider dropping this function.
> >
> > This API is mostly used for ACPI upgrading. ACPI upgrading alloc a
> > normal memory block and then is used as ACPI memory, and this memory
> > block will not be used by the page allocator. Other architectures,
> > such as ARM64, do the same thing here.
>
> ARM64 had quite a lot of issues with NOMAP memory, so I'd recommend to
> avoid using memblock_mark_nomap() unless it is required by MMU constraints
> on loongarch.
>
> I'm not familiar with loongarch MMU details, so I can only give some
> background for NOMAP for you to decide.
>
> Marking memory region NOMAP is required when this region cannot be a part
> of the kernel linear mapping because MMU does not allow aliased mappings
> with different caching modes. E.g. in ARM64 case, ACPI memory that should
> be mapped uncached cannot be mapped as cached in the kernel linear map.
>
> If the memory block should not be used by the page allocator, it should be
> memblock_reserve()'ed rather than marked NOMAP.
Thank you for telling me the background, we will use memblock_reserve() instead.

>
> > > > diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
> > > > new file mode 100644
> > > > index 000000000000..361d108a2b82
> > > > --- /dev/null
> > > > +++ b/arch/loongarch/kernel/mem.c
> > > > @@ -0,0 +1,89 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > +/*
> > > > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
> > > > + */
> > > > +#include <linux/fs.h>
> > > > +#include <linux/mm.h>
> > > > +#include <linux/memblock.h>
> > > > +
> > > > +#include <asm/bootinfo.h>
> > > > +#include <asm/loongson.h>
> > > > +#include <asm/sections.h>
> > > > +
> > > > +void __init early_memblock_init(void)
> > > > +{
> > > > + int i;
> > > > + u32 mem_type;
> > > > + u64 mem_start, mem_end, mem_size;
> > > > +
> > > > + /* Parse memory information */
> > > > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > > > + mem_type = loongson_mem_map->map[i].mem_type;
> > > > + mem_start = loongson_mem_map->map[i].mem_start;
> > > > + mem_size = loongson_mem_map->map[i].mem_size;
> > > > + mem_end = mem_start + mem_size;
> > > > +
> > > > + switch (mem_type) {
> > > > + case ADDRESS_TYPE_SYSRAM:
> > > > + memblock_add(mem_start, mem_size);
> > > > + if (max_low_pfn < (mem_end >> PAGE_SHIFT))
> > > > + max_low_pfn = mem_end >> PAGE_SHIFT;
> > > > + break;
> > > > + }
> > > > + }
> > > > + memblock_set_current_limit(PFN_PHYS(max_low_pfn));
> > > > +}
> > > > +
> > > > +void __init fw_init_memory(void)
> > > > +{
> > > > + int i;
> > > > + u32 mem_type;
> > > > + u64 mem_start, mem_end, mem_size;
> > > > + unsigned long start_pfn, end_pfn;
> > > > + static unsigned long num_physpages;
> > > > +
> > > > + /* Parse memory information */
> > > > + for (i = 0; i < loongson_mem_map->map_count; i++) {
> > > > + mem_type = loongson_mem_map->map[i].mem_type;
> > > > + mem_start = loongson_mem_map->map[i].mem_start;
> > > > + mem_size = loongson_mem_map->map[i].mem_size;
> > > > + mem_end = mem_start + mem_size;
> > >
> > > I think this loop can be merged with loop in early_memblock_init() then ...
> > >
> > > > +
> > > > + switch (mem_type) {
> > > > + case ADDRESS_TYPE_SYSRAM:
> > > > + mem_start = PFN_ALIGN(mem_start);
> > > > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > > > + num_physpages += (mem_size >> PAGE_SHIFT);
> > > > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
> > >
> > > this will become memblock_add_node()
> > >
> > > > + break;
> > > > + case ADDRESS_TYPE_ACPI:
> > > > + mem_start = PFN_ALIGN(mem_start);
> > > > + mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
> > > > + num_physpages += (mem_size >> PAGE_SHIFT);
> > > > + memblock_add(mem_start, mem_size);
> > > > + memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
> > >
> > > as well as this.
> > early_memblock_init() only adds the "usable" memory (SYSRAM) for early
> > use and without numa node information. Other types of memory are
> > handled later by fw_init_memory()/fw_init_numa_memory(), depending on
> > whether CONFIG_NUMA is enabled. So, in
> > fw_init_memory()/fw_init_numa_memory() we only need to call
> > memblock_set_node() to add the node information for SYSRAM type.
>
> There are two potential issues here with doing memblock_add() and
> memblock_set_node() and memblock_reserve() separately with a couple of
> functions called in between.
>
> First, and most important is that you must to memblock_reserve() all the
> memory used by the firmware, like ADDRESS_TYPE_ACPI, ADDRESS_TYPE_RESERVED,
> kernel image, initrd etc *before* any call to memblock_alloc*()
> functions. If you add memory to memblock before reserving firmware regions,
> a call to memblock_alloc*() may allocate the used memory and all kinds of
> errors may happen because of that.
>
> Second, presuming you use SRAT for NUMA information, if you set nodes in
> memblock after there were memory allocations from memblock you may impair
> the ability to hot-remove memory banks.
>
> So ideally, the physical memory detection and registration should follow
> something like:
>
> * memblock_reserve() the memory used by firmware, kernel and initrd
> * detect NUMA topology
> * add memory regions along with their node ids to memblock.
>
> s390::setup_arch() is a good example of doing early reservations:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/s390/kernel/setup.c#n988
I have a fast reading of S390, and I think we can do some adjust:
1, call memblock_set_node(0, ULONG_MAX, &memblock.memory, 0) in
early_memblock_init().
2, move memblock_reserve(PHYS_OFFSET, 0x200000) and
memblock_reserve(__pa_symbol(&_text), __pa_symbol(&_end) -
__pa_symbol(&_text)) to early_memblock_init().
3, Reserve initrd memory in the first place.
It is nearly the same as the S390, then.

>
> > > > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> > > > new file mode 100644
> > > > index 000000000000..8dfe1d9b55f7
> > > > --- /dev/null
> > > > +++ b/arch/loongarch/kernel/setup.c
> > > > +
> > > > +static int usermem __initdata;
> > > > +
> > > > +static int __init early_parse_mem(char *p)
> > > > +{
> > > > + phys_addr_t start, size;
> > > > +
> > > > + /*
> > > > + * If a user specifies memory size, we
> > > > + * blow away any automatically generated
> > > > + * size.
> > > > + */
> > > > + if (usermem == 0) {
> > > > + usermem = 1;
> > > > + memblock_remove(memblock_start_of_DRAM(),
> > > > + memblock_end_of_DRAM() - memblock_start_of_DRAM());
> > > > + }
> > > > + start = 0;
> > > > + size = memparse(p, &p);
> > > > + if (*p == '@')
> > > > + start = memparse(p + 1, &p);
> > > > +
> > > > + memblock_add(start, size);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +early_param("mem", early_parse_mem);
> > > > +
> > > > +static int __init early_parse_memmap(char *p)
> > > > +{
> > > > + char *oldp;
> > > > + u64 start_at, mem_size;
> > > > +
> > > > + if (!p)
> > > > + return -EINVAL;
> > > > +
> > > > + if (!strncmp(p, "exactmap", 8)) {
> > > > + pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
> > > > + return 0;
> > > > + }
> > > > +
> > > > + oldp = p;
> > > > + mem_size = memparse(p, &p);
> > > > + if (p == oldp)
> > > > + return -EINVAL;
> > > > +
> > > > + if (*p == '@') {
> > > > + start_at = memparse(p+1, &p);
> > > > + memblock_add(start_at, mem_size);
> > > > + } else if (*p == '#') {
> > > > + pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
> > > > + return -EINVAL;
> > > > + } else if (*p == '$') {
> > > > + start_at = memparse(p+1, &p);
> > > > + memblock_add(start_at, mem_size);
> > > > + memblock_reserve(start_at, mem_size);
> > > > + } else {
> > > > + pr_err("\"memmap\" invalid format!\n");
> > > > + return -EINVAL;
> > > > + }
> > > > +
> > > > + if (*p == '\0') {
> > > > + usermem = 1;
> > > > + return 0;
> > > > + } else
> > > > + return -EINVAL;
> > > > +}
> > > > +early_param("memmap", early_parse_memmap);
> > >
> > > The memmap= processing is a hack indented to workaround bugs in firmware
> > > related to the memory detection. Please don't copy if over unless there is
> > > really strong reason.
> >
> > Hmmm, I have read the documents, most archs only support mem=limit,
> > but MIPS support mem=limit@base. memmap not only supports
> > memmap=limit@base, but also a lot of advanced syntax. LoongArch needs
> > both limit and limit@base syntax. So can we make our code to support
> > only mem=limit and memmap=limit@base, and remove all other syntax
> > here?
>
> The documentation describes what was there historically and both these
> options tend not to play well with complex memory layouts.
>
> If you must have them it's better to use x86 as an example rather than
> MIPS, just take into the account that on x86 memory always starts from 0,
> so they never needed to have a different base.
>
> For what use-cases LoongArch needs options?
The use-case of limit@base syntax is kdump, because our kernel is not
relocatable. I'll use X86 as an example.

Huacai

>
> --
> Sincerely yours,
> Mike.

2022-03-04 17:55:09

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Ard & Arnd,

On Thu, Mar 3, 2022 at 5:54 PM Ard Biesheuvel <[email protected]> wrote:
>
> On Thu, 3 Mar 2022 at 07:26, Huacai Chen <[email protected]> wrote:
> >
> > Hi, Ard & Arnd,
> >
> > On Wed, Mar 2, 2022 at 5:20 PM Huacai Chen <[email protected]> wrote:
> > >
> > > Hi, Ard,
> > >
> > > On Wed, Mar 2, 2022 at 4:58 PM Ard Biesheuvel <[email protected]> wrote:
> > > >
> > > > On Wed, 2 Mar 2022 at 09:56, Huacai Chen <[email protected]> wrote:
> > > > >
> > > > > Hi, Arnd & Ard,
> > > > >
> > > > > On Tue, Mar 1, 2022 at 6:19 PM Arnd Bergmann <[email protected]> wrote:
> > > > > >
> > > > > > On Tue, Mar 1, 2022 at 5:17 AM Huacai Chen <[email protected]> wrote:
> > > > > > > On Mon, Feb 28, 2022 at 7:35 PM Ard Biesheuvel <[email protected]> wrote:
> > > > > > > > On Mon, 28 Feb 2022 at 12:24, Arnd Bergmann <[email protected]> wrote:
> > > > > > > > > On Mon, Feb 28, 2022 at 11:42 AM Huacai Chen <[email protected]> wrote:
> > > > > > > > > Can't you just use the UEFI protocol for kernel entry regardless
> > > > > > > > > of the bootloader? It seems odd to use a different protocol for loading
> > > > > > > > > grub and the kernel, especially if that means you end up having to
> > > > > > > > > support both protocols inside of u-boot and grub, in order to chain-load
> > > > > > > > > a uefi application like grub.
> > > > > > > > >
> > > > > > > >
> > > > > > > > I think this would make sense. Now that the EFI stub has generic
> > > > > > > > support for loading the initrd via a UEFI specific protocol (of which
> > > > > > > > u-boot already carries an implementation), booting via UEFI only would
> > > > > > > > mean that no Linux boot protocol would need to be defined outside of
> > > > > > > > the kernel at all (i.e., where to load the kernel, where to put the
> > > > > > > > command line, where to put the initrd, other arch specific rules etc
> > > > > > > > etc) UEFI already supports both ACPI and DT boot
> > > > > > >
> > > > > > > After one night thinking, I agree with Ard that we can use RISCV-style
> > > > > > > fdt to support the raw elf kernel at present, and add efistub support
> > > > > > > after new UEFI SPEC released.
> > > > > >
> > > > > > I think that is the opposite of what Ard and I discussed above.
> > > > > Hmm, I thought that new UEFI SPEC is a requirement of efistub, maybe I'm wrong?
> > > > >
> > > > > >
> > > > > > > If I'm right, it seems that RISC-V passes a0 (hartid) and a1 (fdt
> > > > > > > pointer, which contains cmdline, initrd, etc.) to the raw elf kernel.
> > > > > > > And in my opinion, the main drawback of current LoongArch method
> > > > > > > (a0=argc a1=argv a2=bootparamsinterface pointer) is it uses a
> > > > > > > non-standard method to pass kernel args and initrd. So, can the below
> > > > > > > new solution be acceptable?
> > > > > > >
> > > > > > > a0=bootparamsinterface pointer (the same as a2 in current method)
> > > > > > > a1=fdt pointer (contains cmdline, initrd, etc., like RISC-V, I think
> > > > > > > this is the standard method)
> > > > > >
> > > > > > It would seem more logical to me to keep those details as part of the
> > > > > > interface between the EFI stub and the kernel, rather than the
> > > > > > documented boot interface.
> > > > > >
> > > > > > You said that there is already grub support using the UEFI
> > > > > > loader, so I assume you have a working draft of the boot
> > > > > > protocol. Are there still open questions about the interface
> > > > > > definition for that preventing you from using it as the only
> > > > > > way to enter the kernel from a bootloader?
> > > > > Things become simple if we only consider efistub rather than raw elf.
> > > > > But there are still some problems:
> > > > > 1, We want the first patch series as minimal as possible, efistub
> > > > > support will add a lot of code.
> > > > > 2, EFISTUB hides the interface between bootloader and raw kernel, but
> > > > > the interface does actually exist (efistub itself is also a
> > > > > bootloader, though it binds with the raw kernel). In the current
> > > > > implementation (a0=argc a1=argv a2=bootparaminterface), we should
> > > > > select EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER which is marked as
> > > > > deprecated. Is this acceptable? If not, we still need to change the
> > > > > bootloader-kernel interface, maybe use the method in my previous
> > > > > email?
> > > >
> > > > Why do you need this?
> > > Because in the current implementation (a0=argc a1=argv
> > > a2=bootparaminterface), initrd should be passed by cmdline
> > > (initrd=xxxx). If without that option, efi_load_initrd_cmdline() will
> > > not call handle_cmdline_files().
> > It seems I'm wrong. EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER controls
> > "initrd=xxxx" from BIOS to EFISTUB, but has nothing to do with
> > a0/a1/a2 usage (which controls the "initrd=xxxx" from efistub to raw
> > kernel). The real reason is our UEFI BIOS has an old codebase without
> > LoadFile2 support.
> >
>
> The problem with initrd= is that it can only load the initrd from the
> same EFI block device that the kernel was loaded from, which is highly
> restrictive, and doesn't work with bootloaders that call LoadImage()
> on a kernel image loaded into memory. This is why x86 supports passing
> the initrd in memory, and provide the base/size via struct bootparams,
> and arm64 supports the same using DT.
>
> The LoadImage2 protocol based method intends to provide a generic
> alternative to this, as it uses a pure EFI abstraction, and therefore
> does not rely on struct bootparams or DT at all.
>
> So the LoadImage2() based method is preferred, but if your
> architecture implements DT support already, there is nothing
> preventing you from passing initrd information directly to the kernel
> via the /chosen node.
>
> > Then, my new questions are:
> > 1, Is EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER an unacceptable option
> > for a new Arch? If yes, we should backport LoadFile2 support to our
> > BIOS.
>
> See above.
>
> > 2, We now all agree that EFISTUB is the standard and maybe the only
> > way in future. But, can we do the efistub work in the second series,
> > in order to make the first series as minimal as possible? (I will
> > update the commit message to make it clear that a0/a1/a2 usage is only
> > an internal interface between efistub and raw kernel).
> >
>
> I think it would be better to drop the UEFI and ACPI pieces for now,
> and resubmit it once the dust has settled around this.
FDT support is our future goal, at present we only have ACPI firmware
and kernel. I mentioned FDT just wants to replace a0/a1 to pass
cmdline, not means we already have FDT support.

So, let's keep the existing a0/a1/a2 usage as an internal interface
for now, then backport LoadFile2 and add efistub support.

Huacai

2022-03-08 02:50:26

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi,

On Fri, Mar 04, 2022 at 08:43:03PM +0800, Huacai Chen wrote:
> Hi, Mike,
>
> On Fri, Mar 4, 2022 at 6:49 PM Mike Rapoport <[email protected]> wrote:
> >
> > Hi,
> >
> >
> > So ideally, the physical memory detection and registration should follow
> > something like:
> >
> > * memblock_reserve() the memory used by firmware, kernel and initrd
> > * detect NUMA topology
> > * add memory regions along with their node ids to memblock.
> >
> > s390::setup_arch() is a good example of doing early reservations:
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/s390/kernel/setup.c#n988
> I have a fast reading of S390, and I think we can do some adjust:
> 1, call memblock_set_node(0, ULONG_MAX, &memblock.memory, 0) in
> early_memblock_init().
> 2, move memblock_reserve(PHYS_OFFSET, 0x200000) and
> memblock_reserve(__pa_symbol(&_text), __pa_symbol(&_end) -
> __pa_symbol(&_text)) to early_memblock_init().
> 3, Reserve initrd memory in the first place.
> It is nearly the same as the S390, then.

It does not have to look like the same as s390 :)
The important thing is to reserve all the memory before memblock
allocations are possible.

> > > > > +early_param("memmap", early_parse_memmap);
> > > >
> > > > The memmap= processing is a hack indented to workaround bugs in firmware
> > > > related to the memory detection. Please don't copy if over unless there is
> > > > really strong reason.
> > >
> > > Hmmm, I have read the documents, most archs only support mem=limit,
> > > but MIPS support mem=limit@base. memmap not only supports
> > > memmap=limit@base, but also a lot of advanced syntax. LoongArch needs
> > > both limit and limit@base syntax. So can we make our code to support
> > > only mem=limit and memmap=limit@base, and remove all other syntax
> > > here?
> >
> > The documentation describes what was there historically and both these
> > options tend not to play well with complex memory layouts.
> >
> > If you must have them it's better to use x86 as an example rather than
> > MIPS, just take into the account that on x86 memory always starts from 0,
> > so they never needed to have a different base.
> >
> > For what use-cases LoongArch needs options?
>
> The use-case of limit@base syntax is kdump, because our kernel is not
> relocatable. I'll use X86 as an example.

I missed that mem= can be used several times, so with MIPS implementation
it's possible to define something like "mem=limit0@base0 mem=limit1@base1"
and this will create two contiguous memory regions.

> Huacai

--
Sincerely yours,
Mike.

2022-03-08 20:35:21

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH V6 09/22] LoongArch: Add boot and setup routines

Hi, Mike,

On Tue, Mar 8, 2022 at 1:37 AM Mike Rapoport <[email protected]> wrote:
>
> Hi,
>
> On Fri, Mar 04, 2022 at 08:43:03PM +0800, Huacai Chen wrote:
> > Hi, Mike,
> >
> > On Fri, Mar 4, 2022 at 6:49 PM Mike Rapoport <[email protected]> wrote:
> > >
> > > Hi,
> > >
> > >
> > > So ideally, the physical memory detection and registration should follow
> > > something like:
> > >
> > > * memblock_reserve() the memory used by firmware, kernel and initrd
> > > * detect NUMA topology
> > > * add memory regions along with their node ids to memblock.
> > >
> > > s390::setup_arch() is a good example of doing early reservations:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/s390/kernel/setup.c#n988
> > I have a fast reading of S390, and I think we can do some adjust:
> > 1, call memblock_set_node(0, ULONG_MAX, &memblock.memory, 0) in
> > early_memblock_init().
> > 2, move memblock_reserve(PHYS_OFFSET, 0x200000) and
> > memblock_reserve(__pa_symbol(&_text), __pa_symbol(&_end) -
> > __pa_symbol(&_text)) to early_memblock_init().
> > 3, Reserve initrd memory in the first place.
> > It is nearly the same as the S390, then.
>
> It does not have to look like the same as s390 :)
> The important thing is to reserve all the memory before memblock
> allocations are possible.
New version is here, it's not completely the same as S390, but very similar:
https://lore.kernel.org/linux-arch/[email protected]/T/#Z2e.:..:20220306112850.811504-10-chenhuacai::40loongson.cn:1arch:loongarch:kernel:mem.c

Firmware is not in SYSRAM regions, so we don't need to reserve them.
The first 2MB and the kernel region are reserved in
early_memblock_init(), before any allocations.

Initrd information is passed by cmdline, and initrd is now reserved
immediately after cmdline has parsed, by merging
init_initrd/finalize_initrd as you suggested:
https://lore.kernel.org/linux-arch/[email protected]/T/#Z2e.:..:20220306112850.811504-10-chenhuacai::40loongson.cn:1arch:loongarch:kernel:setup.c

>
> > > > > > +early_param("memmap", early_parse_memmap);
> > > > >
> > > > > The memmap= processing is a hack indented to workaround bugs in firmware
> > > > > related to the memory detection. Please don't copy if over unless there is
> > > > > really strong reason.
> > > >
> > > > Hmmm, I have read the documents, most archs only support mem=limit,
> > > > but MIPS support mem=limit@base. memmap not only supports
> > > > memmap=limit@base, but also a lot of advanced syntax. LoongArch needs
> > > > both limit and limit@base syntax. So can we make our code to support
> > > > only mem=limit and memmap=limit@base, and remove all other syntax
> > > > here?
> > >
> > > The documentation describes what was there historically and both these
> > > options tend not to play well with complex memory layouts.
> > >
> > > If you must have them it's better to use x86 as an example rather than
> > > MIPS, just take into the account that on x86 memory always starts from 0,
> > > so they never needed to have a different base.
> > >
> > > For what use-cases LoongArch needs options?
> >
> > The use-case of limit@base syntax is kdump, because our kernel is not
> > relocatable. I'll use X86 as an example.
>
> I missed that mem= can be used several times, so with MIPS implementation
> it's possible to define something like "mem=limit0@base0 mem=limit1@base1"
> and this will create two contiguous memory regions.
The new version is here:
https://lore.kernel.org/linux-arch/[email protected]/T/#Z2e.:..:20220306112850.811504-10-chenhuacai::40loongson.cn:1arch:loongarch:kernel:setup.c
If I use the MIPS implementation, then memmap= is useless and can be
removed, but the MIPS implementation is not obey the rules in kernel
documents.

Huacai
>
> > Huacai
>
> --
> Sincerely yours,
> Mike.