Received: by 2002:a19:771d:0:0:0:0:0 with SMTP id s29csp1241968lfc; Wed, 1 Jun 2022 12:50:16 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxsaam5grwh4RbcYXCfCdgdbWd+r/fe32yYaAaiECs0lhCTRZNHkjq23RHbcF4uTxGObNgz X-Received: by 2002:a17:90b:4ac8:b0:1e3:4f6a:2d04 with SMTP id mh8-20020a17090b4ac800b001e34f6a2d04mr8058282pjb.233.1654113015926; Wed, 01 Jun 2022 12:50:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1654113015; cv=none; d=google.com; s=arc-20160816; b=C7AkL82kW8aFITLPbeR0E9zFMPrKIlOoBbcqS38cxSUunQh2O49wOROaruyW3WRHbQ VtkiISDzo6UhoafGIv8GT8tfSOON+LAgMqgndO2CWi9YVO8P6OKaxNd42SJSV6JVNlV+ tiq+MRikSG0RwIQcA2rmlWwc0FtJzmJXEfxqNkLAj6U7wWNoyGbYXSIA30ncgrbQF3cU g+XBtTaum21d2VLTkqXbl9wZ51D736LrthNugOX1xhj6Wdi+E2Kezs4PZmdHGr57C/S9 bePjouB/5vPFmFv720SVxPdAgh3HReZPnq8SJA+vgOO4pL+789QLZhTwgtzEjgcjnr6/ RLuA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:subject:cc:to:from :date:references:in-reply-to:message-id:mime-version:user-agent :feedback-id:dkim-signature:dkim-signature; bh=mcZ6+UC/cuQKJS4rLzZOU8zx12GPElo6+LdJnB0ZUtQ=; b=LxOopZG7wRZLl/be5XXXuZBhPi1CGve7RuduNPNxOZfX0+BBcfbQT+vaOHZ2oShyZx 1eW1rv3S2ffMKnWb+jZsp5YKdU66JYqdOMW2Sh4Q0yXBVMyCqN5Ym9kIqTanvISBEqlG VObha4meEI+0Ra7QN+1KOu97PWyXeinWALGgqGWpJXmkwRbX5aL7jF7K0JvjZPDjl4xu 5pzcssd2TkV3RwjSA6MQ0iCDUknVXVbA3VP4zStMXIE9jYEjCNVrpZdtCEl/U2JkRrk8 z8o8OyjuZQTFlh4lftDWS+E83lwbza4P6FwKCvpcXQDXPllFsJ+SR0/9LxsfC2lz7sBD Z/qQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@flygoat.com header.s=fm2 header.b=SOHcSNMy; dkim=pass header.i=@messagingengine.com header.s=fm1 header.b=FN4gSRAJ; spf=softfail (google.com: domain of transitioning linux-kernel-owner@vger.kernel.org does not designate 23.128.96.19 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=flygoat.com Return-Path: Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net. [23.128.96.19]) by mx.google.com with ESMTPS id gg17-20020a17090b0a1100b001e0a486d9desi6805033pjb.116.2022.06.01.12.50.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Jun 2022 12:50:15 -0700 (PDT) Received-SPF: softfail (google.com: domain of transitioning linux-kernel-owner@vger.kernel.org does not designate 23.128.96.19 as permitted sender) client-ip=23.128.96.19; Authentication-Results: mx.google.com; dkim=pass header.i=@flygoat.com header.s=fm2 header.b=SOHcSNMy; dkim=pass header.i=@messagingengine.com header.s=fm1 header.b=FN4gSRAJ; spf=softfail (google.com: domain of transitioning linux-kernel-owner@vger.kernel.org does not designate 23.128.96.19 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=flygoat.com Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 69B6C1F2321; Wed, 1 Jun 2022 12:13:02 -0700 (PDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1352852AbiFAMae (ORCPT + 99 others); Wed, 1 Jun 2022 08:30:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41714 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1352847AbiFAMaR (ORCPT ); Wed, 1 Jun 2022 08:30:17 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 82B5162A35; Wed, 1 Jun 2022 05:30:13 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id E525A5C02F8; Wed, 1 Jun 2022 08:30:12 -0400 (EDT) Received: from imap44 ([10.202.2.94]) by compute4.internal (MEProxy); Wed, 01 Jun 2022 08:30:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=flygoat.com; h= cc:cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to; s=fm2; t=1654086612; x= 1654173012; bh=mcZ6+UC/cuQKJS4rLzZOU8zx12GPElo6+LdJnB0ZUtQ=; b=S OHcSNMyLxwtUQqtggoYRT8+TjSlvCX/QGLxfTuEgan7UR8hC34/Ic/0ujZQJMJBc FhBctSOfTApb4ZLEXhURO3m4CQn+5MMq1klNOKgxmEc0oC85+qtLUjLWejmZSe/5 9wzP1ir+DtkkZJC4R3NXPiIbCgSnbfoKb+PW2SxvhDdJD7sYQY8KBZrXdt0z7CNV 4cL6PZA6wGy3WKbvnv2lnH9A5/kAb7tC5er1Qd0C6ArPUXEmALgrX+jJBUriKh4R oaczBQhpGYtnLDJDh55TSKxXTJTUvPPo6eIxzd8elFap8RVk/Wb5KpSWDQN+yjXh j09Oh95wvQmDwzOz9sEQg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1654086612; x= 1654173012; bh=mcZ6+UC/cuQKJS4rLzZOU8zx12GPElo6+LdJnB0ZUtQ=; b=F N4gSRAJeHRqTl/2WedL5+Mk5XtPaesWtzfzJSNLuATHmrjLhpPLDztwdkaFht/6r UUpCxzjw2VFULnQfA5fsaFYdWranyIFxFAIflHr0ncsWC8peE8BEXYVhc2WZKySS KMn7Javv7cdpB6L26PS0DnkOTSOeVFObYzWVUd3iNpe+Xv/pCgqG+W1DQ0tDPcyV TmJ1Z8wGmj2B5zTO39/3kYQ0LUvGRn2hIvGXCwGoYPYYue8R5zvh9vQ7V5uufOjs jk7yyBR9qXKHCnAgOGvPLuQeNMAWLCfwk8yiR9LKmLnWIMu1QuhxEGJqTzo4P5Qq WHuScZBD80ogBtOcp5R5A== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvfedrledtgdehudcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpefofgggkfgjfhffhffvvefutgfgsehtqhertderreejnecuhfhrohhmpedflfhi rgiguhhnucgjrghnghdfuceojhhirgiguhhnrdihrghnghesfhhlhihgohgrthdrtghomh eqnecuggftrfgrthhtvghrnhepgeehjeekleeijeffieeujeekueetfeduieeileefvedu vdeugeevhfduieeuveeunecuffhomhgrihhnpehgvghnvgigrdhssgenucevlhhushhtvg hrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehjihgrgihunhdrhigrnhhg sehflhihghhorghtrdgtohhm X-ME-Proxy: Feedback-ID: ifd894703:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id BC2B636A006D; Wed, 1 Jun 2022 08:30:11 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.7.0-alpha0-591-gfe6c3a2700-fm-20220427.001-gfe6c3a27 Mime-Version: 1.0 Message-Id: In-Reply-To: <20220601100005.2989022-13-chenhuacai@loongson.cn> References: <20220601100005.2989022-1-chenhuacai@loongson.cn> <20220601100005.2989022-13-chenhuacai@loongson.cn> Date: Wed, 01 Jun 2022 13:29:51 +0100 From: "Jiaxun Yang" To: "Huacai Chen" , "Arnd Bergmann" , "Andy Lutomirski" , "Thomas Gleixner" , "Peter Zijlstra" , "Andrew Morton" , "David Airlie" , "Jonathan Corbet" , "Linus Torvalds" Cc: linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, "Xuefeng Li" , "Yanteng Si" , "Huacai Chen" , "Guo Ren" , "Xuerui Wang" , "Stephen Rothwell" , "WANG Xuerui" Subject: Re: [PATCH V12 12/24] LoongArch: Add exception/interrupt handling Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RDNS_NONE,SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org =E5=9C=A82022=E5=B9=B46=E6=9C=881=E6=97=A5=E5=85=AD=E6=9C=88 =E4=B8=8A=E5= =8D=8810:59=EF=BC=8CHuacai Chen=E5=86=99=E9=81=93=EF=BC=9A > Add the exception and interrupt handling machanism for basic LoongArch > support. > > Reviewed-by: WANG Xuerui > Signed-off-by: Huacai Chen Reviewed-by: Jiaxun Yang > --- > arch/loongarch/include/asm/branch.h | 21 + > arch/loongarch/include/asm/bug.h | 23 + > arch/loongarch/include/asm/entry-common.h | 13 + > arch/loongarch/include/asm/hardirq.h | 24 + > arch/loongarch/include/asm/hw_irq.h | 17 + > arch/loongarch/include/asm/irq.h | 130 ++++ > arch/loongarch/include/asm/irq_regs.h | 27 + > arch/loongarch/include/asm/irqflags.h | 78 +++ > arch/loongarch/include/asm/kdebug.h | 23 + > arch/loongarch/include/asm/stackframe.h | 212 +++++++ > arch/loongarch/include/asm/stacktrace.h | 74 +++ > arch/loongarch/include/uapi/asm/break.h | 23 + > arch/loongarch/kernel/access-helper.h | 13 + > arch/loongarch/kernel/genex.S | 95 +++ > arch/loongarch/kernel/irq.c | 77 +++ > arch/loongarch/kernel/traps.c | 725 ++++++++++++++++++++++ > 16 files changed, 1575 insertions(+) > create mode 100644 arch/loongarch/include/asm/branch.h > create mode 100644 arch/loongarch/include/asm/bug.h > create mode 100644 arch/loongarch/include/asm/entry-common.h > create mode 100644 arch/loongarch/include/asm/hardirq.h > create mode 100644 arch/loongarch/include/asm/hw_irq.h > create mode 100644 arch/loongarch/include/asm/irq.h > create mode 100644 arch/loongarch/include/asm/irq_regs.h > create mode 100644 arch/loongarch/include/asm/irqflags.h > create mode 100644 arch/loongarch/include/asm/kdebug.h > create mode 100644 arch/loongarch/include/asm/stackframe.h > create mode 100644 arch/loongarch/include/asm/stacktrace.h > create mode 100644 arch/loongarch/include/uapi/asm/break.h > create mode 100644 arch/loongarch/kernel/access-helper.h > create mode 100644 arch/loongarch/kernel/genex.S > create mode 100644 arch/loongarch/kernel/irq.c > create mode 100644 arch/loongarch/kernel/traps.c > > diff --git a/arch/loongarch/include/asm/branch.h=20 > b/arch/loongarch/include/asm/branch.h > new file mode 100644 > index 000000000000..3f33c89f35b4 > --- /dev/null > +++ b/arch/loongarch/include/asm/branch.h > @@ -0,0 +1,21 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_BRANCH_H > +#define _ASM_BRANCH_H > + > +#include > + > +static inline unsigned long exception_era(struct pt_regs *regs) > +{ > + return regs->csr_era; > +} > + > +static inline int compute_return_era(struct pt_regs *regs) > +{ > + regs->csr_era +=3D 4; > + return 0; > +} > + > +#endif /* _ASM_BRANCH_H */ > diff --git a/arch/loongarch/include/asm/bug.h=20 > b/arch/loongarch/include/asm/bug.h > new file mode 100644 > index 000000000000..bda49108a76d > --- /dev/null > +++ b/arch/loongarch/include/asm/bug.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef __ASM_BUG_H > +#define __ASM_BUG_H > + > +#include > + > +#ifdef CONFIG_BUG > + > +#include > + > +static inline void __noreturn BUG(void) > +{ > + __asm__ __volatile__("break %0" : : "i" (BRK_BUG)); > + unreachable(); > +} > + > +#define HAVE_ARCH_BUG > + > +#endif > + > +#include > + > +#endif /* __ASM_BUG_H */ > diff --git a/arch/loongarch/include/asm/entry-common.h=20 > b/arch/loongarch/include/asm/entry-common.h > new file mode 100644 > index 000000000000..0fe2a098ded9 > --- /dev/null > +++ b/arch/loongarch/include/asm/entry-common.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +#ifndef ARCH_LOONGARCH_ENTRY_COMMON_H > +#define ARCH_LOONGARCH_ENTRY_COMMON_H > + > +#include > +#include > + > +static inline bool on_thread_stack(void) > +{ > + return !(((unsigned long)(current->stack) ^ current_stack_pointer) &=20 > ~(THREAD_SIZE - 1)); > +} > + > +#endif > diff --git a/arch/loongarch/include/asm/hardirq.h=20 > b/arch/loongarch/include/asm/hardirq.h > new file mode 100644 > index 000000000000..d32f83938880 > --- /dev/null > +++ b/arch/loongarch/include/asm/hardirq.h > @@ -0,0 +1,24 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_HARDIRQ_H > +#define _ASM_HARDIRQ_H > + > +#include > +#include > +#include > + > +extern void ack_bad_irq(unsigned int irq); > +#define ack_bad_irq ack_bad_irq > + > +#define NR_IPI 2 > + > +typedef struct { > + unsigned int ipi_irqs[NR_IPI]; > + unsigned int __softirq_pending; > +} ____cacheline_aligned irq_cpustat_t; > + > +DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat); > + > +#endif /* _ASM_HARDIRQ_H */ > diff --git a/arch/loongarch/include/asm/hw_irq.h=20 > b/arch/loongarch/include/asm/hw_irq.h > new file mode 100644 > index 000000000000..af4f4e8fbd85 > --- /dev/null > +++ b/arch/loongarch/include/asm/hw_irq.h > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef __ASM_HW_IRQ_H > +#define __ASM_HW_IRQ_H > + > +#include > + > +extern atomic_t irq_err_count; > + > +/* > + * interrupt-retrigger: NOP for now. This may not be appropriate for=20 > all > + * machines, we'll see ... > + */ > + > +#endif /* __ASM_HW_IRQ_H */ > diff --git a/arch/loongarch/include/asm/irq.h=20 > b/arch/loongarch/include/asm/irq.h > new file mode 100644 > index 000000000000..f3f1baf7027c > --- /dev/null > +++ b/arch/loongarch/include/asm/irq.h > @@ -0,0 +1,130 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_IRQ_H > +#define _ASM_IRQ_H > + > +#include > +#include > + > +#define IRQ_STACK_SIZE THREAD_SIZE > +#define IRQ_STACK_START (IRQ_STACK_SIZE - 16) > + > +DECLARE_PER_CPU(unsigned long, irq_stack); > + > +/* > + * The highest address on the IRQ stack contains a dummy frame which = is > + * structured as follows: > + * > + * top ------------ > + * | task sp | <- irq_stack[cpu] + IRQ_STACK_START > + * ------------ > + * | | <- First frame of IRQ context > + * ------------ > + * > + * task sp holds a copy of the task stack pointer where the struct=20 > pt_regs > + * from exception entry can be found. > + */ > + > +static inline bool on_irq_stack(int cpu, unsigned long sp) > +{ > + unsigned long low =3D per_cpu(irq_stack, cpu); > + unsigned long high =3D low + IRQ_STACK_SIZE; > + > + return (low <=3D sp && sp <=3D high); > +} > + > +int get_ipi_irq(void); > +int get_pmc_irq(void); > +int get_timer_irq(void); > +void spurious_interrupt(void); > + > +#define NR_IRQS_LEGACY 16 > + > +#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace > +void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool=20 > exclude_self); > + > +#define MAX_IO_PICS 2 > +#define NR_IRQS (64 + (256 * MAX_IO_PICS)) > + > +#define CORES_PER_EIO_NODE 4 > + > +#define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */ > +#define LOONGSON_CPU_THSENS_VEC 14 /* CPU Thsens */ > +#define LOONGSON_CPU_HT0_VEC 16 /* CPU HT0 irq vector base number */ > +#define LOONGSON_CPU_HT1_VEC 24 /* CPU HT1 irq vector base number */ > + > +/* IRQ number definitions */ > +#define LOONGSON_LPC_IRQ_BASE 0 > +#define LOONGSON_LPC_LAST_IRQ (LOONGSON_LPC_IRQ_BASE + 15) > + > +#define LOONGSON_CPU_IRQ_BASE 16 > +#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 14) > + > +#define LOONGSON_PCH_IRQ_BASE 64 > +#define LOONGSON_PCH_ACPI_IRQ (LOONGSON_PCH_IRQ_BASE + 47) > +#define LOONGSON_PCH_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 64 - 1) > + > +#define LOONGSON_MSI_IRQ_BASE (LOONGSON_PCH_IRQ_BASE + 64) > +#define LOONGSON_MSI_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1) > + > +#define GSI_MIN_LPC_IRQ LOONGSON_LPC_IRQ_BASE > +#define GSI_MAX_LPC_IRQ (LOONGSON_LPC_IRQ_BASE + 16 - 1) > +#define GSI_MIN_CPU_IRQ LOONGSON_CPU_IRQ_BASE > +#define GSI_MAX_CPU_IRQ (LOONGSON_CPU_IRQ_BASE + 48 - 1) > +#define GSI_MIN_PCH_IRQ LOONGSON_PCH_IRQ_BASE > +#define GSI_MAX_PCH_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1) > + > +extern int find_pch_pic(u32 gsi); > +extern int eiointc_get_node(int id); > + > +static inline void eiointc_enable(void) > +{ > + uint64_t misc; > + > + misc =3D iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); > + misc |=3D IOCSR_MISC_FUNC_EXT_IOI_EN; > + iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC); > +} > + > +struct acpi_madt_lio_pic; > +struct acpi_madt_eio_pic; > +struct acpi_madt_ht_pic; > +struct acpi_madt_bio_pic; > +struct acpi_madt_msi_pic; > +struct acpi_madt_lpc_pic; > + > +struct irq_domain *loongarch_cpu_irq_init(void); > + > +struct irq_domain *liointc_acpi_init(struct irq_domain *parent, > + struct acpi_madt_lio_pic *acpi_liointc); > +struct irq_domain *eiointc_acpi_init(struct irq_domain *parent, > + struct acpi_madt_eio_pic *acpi_eiointc); > + > +struct irq_domain *htvec_acpi_init(struct irq_domain *parent, > + struct acpi_madt_ht_pic *acpi_htvec); > +struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent, > + struct acpi_madt_lpc_pic *acpi_pchlpc); > +struct irq_domain *pch_msi_acpi_init(struct irq_domain *parent, > + struct acpi_madt_msi_pic *acpi_pchmsi); > +struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent, > + struct acpi_madt_bio_pic *acpi_pchpic); > + > +extern struct acpi_madt_lio_pic *acpi_liointc; > +extern struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS]; > + > +extern struct acpi_madt_ht_pic *acpi_htintc; > +extern struct acpi_madt_lpc_pic *acpi_pchlpc; > +extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS]; > +extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS]; > + > +extern struct irq_domain *cpu_domain; > +extern struct irq_domain *liointc_domain; > +extern struct irq_domain *pch_lpc_domain; > +extern struct irq_domain *pch_msi_domain[MAX_IO_PICS]; > +extern struct irq_domain *pch_pic_domain[MAX_IO_PICS]; > + > +#include > + > +#endif /* _ASM_IRQ_H */ > diff --git a/arch/loongarch/include/asm/irq_regs.h=20 > b/arch/loongarch/include/asm/irq_regs.h > new file mode 100644 > index 000000000000..3d62d815bf6b > --- /dev/null > +++ b/arch/loongarch/include/asm/irq_regs.h > @@ -0,0 +1,27 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef __ASM_IRQ_REGS_H > +#define __ASM_IRQ_REGS_H > + > +#define ARCH_HAS_OWN_IRQ_REGS > + > +#include > + > +static inline struct pt_regs *get_irq_regs(void) > +{ > + return current_thread_info()->regs; > +} > + > +static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs) > +{ > + struct pt_regs *old_regs; > + > + old_regs =3D get_irq_regs(); > + current_thread_info()->regs =3D new_regs; > + > + return old_regs; > +} > + > +#endif /* __ASM_IRQ_REGS_H */ > diff --git a/arch/loongarch/include/asm/irqflags.h=20 > b/arch/loongarch/include/asm/irqflags.h > new file mode 100644 > index 000000000000..52121cd791fe > --- /dev/null > +++ b/arch/loongarch/include/asm/irqflags.h > @@ -0,0 +1,78 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_IRQFLAGS_H > +#define _ASM_IRQFLAGS_H > + > +#ifndef __ASSEMBLY__ > + > +#include > +#include > +#include > +#include > + > +static inline void arch_local_irq_enable(void) > +{ > + u32 flags =3D CSR_CRMD_IE; > + __asm__ __volatile__( > + "csrxchg %[val], %[mask], %[reg]\n\t" > + : [val] "+r" (flags) > + : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) > + : "memory"); > +} > + > +static inline void arch_local_irq_disable(void) > +{ > + u32 flags =3D 0; > + __asm__ __volatile__( > + "csrxchg %[val], %[mask], %[reg]\n\t" > + : [val] "+r" (flags) > + : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) > + : "memory"); > +} > + > +static inline unsigned long arch_local_irq_save(void) > +{ > + u32 flags =3D 0; > + __asm__ __volatile__( > + "csrxchg %[val], %[mask], %[reg]\n\t" > + : [val] "+r" (flags) > + : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) > + : "memory"); > + return flags; > +} > + > +static inline void arch_local_irq_restore(unsigned long flags) > +{ > + __asm__ __volatile__( > + "csrxchg %[val], %[mask], %[reg]\n\t" > + : [val] "+r" (flags) > + : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) > + : "memory"); > +} > + > +static inline unsigned long arch_local_save_flags(void) > +{ > + u32 flags; > + __asm__ __volatile__( > + "csrrd %[val], %[reg]\n\t" > + : [val] "=3Dr" (flags) > + : [reg] "i" (LOONGARCH_CSR_CRMD) > + : "memory"); > + return flags; > +} > + > +static inline int arch_irqs_disabled_flags(unsigned long flags) > +{ > + return !(flags & CSR_CRMD_IE); > +} > + > +static inline int arch_irqs_disabled(void) > +{ > + return arch_irqs_disabled_flags(arch_local_save_flags()); > +} > + > +#endif /* #ifndef __ASSEMBLY__ */ > + > +#endif /* _ASM_IRQFLAGS_H */ > diff --git a/arch/loongarch/include/asm/kdebug.h=20 > b/arch/loongarch/include/asm/kdebug.h > new file mode 100644 > index 000000000000..d721b4b82fae > --- /dev/null > +++ b/arch/loongarch/include/asm/kdebug.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_LOONGARCH_KDEBUG_H > +#define _ASM_LOONGARCH_KDEBUG_H > + > +#include > + > +enum die_val { > + DIE_OOPS =3D 1, > + DIE_RI, > + DIE_FP, > + DIE_SIMD, > + DIE_TRAP, > + DIE_PAGE_FAULT, > + DIE_BREAK, > + DIE_SSTEPBP, > + DIE_UPROBE, > + DIE_UPROBE_XOL, > +}; > + > +#endif /* _ASM_LOONGARCH_KDEBUG_H */ > diff --git a/arch/loongarch/include/asm/stackframe.h=20 > b/arch/loongarch/include/asm/stackframe.h > new file mode 100644 > index 000000000000..44151b878d00 > --- /dev/null > +++ b/arch/loongarch/include/asm/stackframe.h > @@ -0,0 +1,212 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_STACKFRAME_H > +#define _ASM_STACKFRAME_H > + > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +/* Make the addition of cfi info a little easier. */ > + .macro cfi_rel_offset reg offset=3D0 docfi=3D0 > + .if \docfi > + .cfi_rel_offset \reg, \offset > + .endif > + .endm > + > + .macro cfi_st reg offset=3D0 docfi=3D0 > + cfi_rel_offset \reg, \offset, \docfi > + LONG_S \reg, sp, \offset > + .endm > + > + .macro cfi_restore reg offset=3D0 docfi=3D0 > + .if \docfi > + .cfi_restore \reg > + .endif > + .endm > + > + .macro cfi_ld reg offset=3D0 docfi=3D0 > + LONG_L \reg, sp, \offset > + cfi_restore \reg \offset \docfi > + .endm > + > + .macro BACKUP_T0T1 > + csrwr t0, EXCEPTION_KS0 > + csrwr t1, EXCEPTION_KS1 > + .endm > + > + .macro RELOAD_T0T1 > + csrrd t0, EXCEPTION_KS0 > + csrrd t1, EXCEPTION_KS1 > + .endm > + > + .macro SAVE_TEMP docfi=3D0 > + RELOAD_T0T1 > + cfi_st t0, PT_R12, \docfi > + cfi_st t1, PT_R13, \docfi > + cfi_st t2, PT_R14, \docfi > + cfi_st t3, PT_R15, \docfi > + cfi_st t4, PT_R16, \docfi > + cfi_st t5, PT_R17, \docfi > + cfi_st t6, PT_R18, \docfi > + cfi_st t7, PT_R19, \docfi > + cfi_st t8, PT_R20, \docfi > + .endm > + > + .macro SAVE_STATIC docfi=3D0 > + cfi_st s0, PT_R23, \docfi > + cfi_st s1, PT_R24, \docfi > + cfi_st s2, PT_R25, \docfi > + cfi_st s3, PT_R26, \docfi > + cfi_st s4, PT_R27, \docfi > + cfi_st s5, PT_R28, \docfi > + cfi_st s6, PT_R29, \docfi > + cfi_st s7, PT_R30, \docfi > + cfi_st s8, PT_R31, \docfi > + .endm > + > +/* > + * get_saved_sp returns the SP for the current CPU by looking in the > + * kernelsp array for it. It stores the current sp in t0 and loads the > + * new value in sp. > + */ > + .macro get_saved_sp docfi=3D0 > + la.abs t1, kernelsp > + move t0, sp > + .if \docfi > + .cfi_register sp, t0 > + .endif > + LONG_L sp, t1, 0 > + .endm > + > + .macro set_saved_sp stackp temp temp2 > + la.abs \temp, kernelsp > + LONG_S \stackp, \temp, 0 > + .endm > + > + .macro SAVE_SOME docfi=3D0 > + csrrd t1, LOONGARCH_CSR_PRMD > + andi t1, t1, 0x3 /* extract pplv bit */ > + move t0, sp > + beqz t1, 8f > + /* Called from user mode, new stack. */ > + get_saved_sp docfi=3D\docfi > +8: > + PTR_ADDI sp, sp, -PT_SIZE > + .if \docfi > + .cfi_def_cfa sp, 0 > + .endif > + cfi_st t0, PT_R3, \docfi > + cfi_rel_offset sp, PT_R3, \docfi > + LONG_S zero, sp, PT_R0 > + csrrd t0, LOONGARCH_CSR_PRMD > + LONG_S t0, sp, PT_PRMD > + csrrd t0, LOONGARCH_CSR_CRMD > + LONG_S t0, sp, PT_CRMD > + csrrd t0, LOONGARCH_CSR_EUEN > + LONG_S t0, sp, PT_EUEN > + csrrd t0, LOONGARCH_CSR_ECFG > + LONG_S t0, sp, PT_ECFG > + csrrd t0, LOONGARCH_CSR_ESTAT > + PTR_S t0, sp, PT_ESTAT > + cfi_st ra, PT_R1, \docfi > + cfi_st a0, PT_R4, \docfi > + cfi_st a1, PT_R5, \docfi > + cfi_st a2, PT_R6, \docfi > + cfi_st a3, PT_R7, \docfi > + cfi_st a4, PT_R8, \docfi > + cfi_st a5, PT_R9, \docfi > + cfi_st a6, PT_R10, \docfi > + cfi_st a7, PT_R11, \docfi > + csrrd ra, LOONGARCH_CSR_ERA > + LONG_S ra, sp, PT_ERA > + .if \docfi > + .cfi_rel_offset ra, PT_ERA > + .endif > + cfi_st tp, PT_R2, \docfi > + cfi_st fp, PT_R22, \docfi > + > + /* Set thread_info if we're coming from user mode */ > + csrrd t0, LOONGARCH_CSR_PRMD > + andi t0, t0, 0x3 /* extract pplv bit */ > + beqz t0, 9f > + > + li.d tp, ~_THREAD_MASK > + and tp, tp, sp > + cfi_st u0, PT_R21, \docfi > + csrrd u0, PERCPU_BASE_KS > +9: > + .endm > + > + .macro SAVE_ALL docfi=3D0 > + SAVE_SOME \docfi > + SAVE_TEMP \docfi > + SAVE_STATIC \docfi > + .endm > + > + .macro RESTORE_TEMP docfi=3D0 > + cfi_ld t0, PT_R12, \docfi > + cfi_ld t1, PT_R13, \docfi > + cfi_ld t2, PT_R14, \docfi > + cfi_ld t3, PT_R15, \docfi > + cfi_ld t4, PT_R16, \docfi > + cfi_ld t5, PT_R17, \docfi > + cfi_ld t6, PT_R18, \docfi > + cfi_ld t7, PT_R19, \docfi > + cfi_ld t8, PT_R20, \docfi > + .endm > + > + .macro RESTORE_STATIC docfi=3D0 > + cfi_ld s0, PT_R23, \docfi > + cfi_ld s1, PT_R24, \docfi > + cfi_ld s2, PT_R25, \docfi > + cfi_ld s3, PT_R26, \docfi > + cfi_ld s4, PT_R27, \docfi > + cfi_ld s5, PT_R28, \docfi > + cfi_ld s6, PT_R29, \docfi > + cfi_ld s7, PT_R30, \docfi > + cfi_ld s8, PT_R31, \docfi > + .endm > + > + .macro RESTORE_SOME docfi=3D0 > + LONG_L a0, sp, PT_PRMD > + andi a0, a0, 0x3 /* extract pplv bit */ > + beqz a0, 8f > + cfi_ld u0, PT_R21, \docfi > +8: > + LONG_L a0, sp, PT_ERA > + csrwr a0, LOONGARCH_CSR_ERA > + LONG_L a0, sp, PT_PRMD > + csrwr a0, LOONGARCH_CSR_PRMD > + cfi_ld ra, PT_R1, \docfi > + cfi_ld a0, PT_R4, \docfi > + cfi_ld a1, PT_R5, \docfi > + cfi_ld a2, PT_R6, \docfi > + cfi_ld a3, PT_R7, \docfi > + cfi_ld a4, PT_R8, \docfi > + cfi_ld a5, PT_R9, \docfi > + cfi_ld a6, PT_R10, \docfi > + cfi_ld a7, PT_R11, \docfi > + cfi_ld tp, PT_R2, \docfi > + cfi_ld fp, PT_R22, \docfi > + .endm > + > + .macro RESTORE_SP_AND_RET docfi=3D0 > + cfi_ld sp, PT_R3, \docfi > + ertn > + .endm > + > + .macro RESTORE_ALL_AND_RET docfi=3D0 > + RESTORE_STATIC \docfi > + RESTORE_TEMP \docfi > + RESTORE_SOME \docfi > + RESTORE_SP_AND_RET \docfi > + .endm > + > +#endif /* _ASM_STACKFRAME_H */ > diff --git a/arch/loongarch/include/asm/stacktrace.h=20 > b/arch/loongarch/include/asm/stacktrace.h > new file mode 100644 > index 000000000000..26483e396ad1 > --- /dev/null > +++ b/arch/loongarch/include/asm/stacktrace.h > @@ -0,0 +1,74 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef _ASM_STACKTRACE_H > +#define _ASM_STACKTRACE_H > + > +#include > +#include > +#include > +#include > + > +#define STR_LONG_L __stringify(LONG_L) > +#define STR_LONG_S __stringify(LONG_S) > +#define STR_LONGSIZE __stringify(LONGSIZE) > + > +#define STORE_ONE_REG(r) \ > + STR_LONG_S " $r" __stringify(r)", %1,=20 > "STR_LONGSIZE"*"__stringify(r)"\n\t" > + > +#define CSRRD_ONE_REG(reg) \ > + __stringify(csrrd) " %0, "__stringify(reg)"\n\t" > + > +static __always_inline void prepare_frametrace(struct pt_regs *regs) > +{ > + __asm__ __volatile__( > + /* Save $r1 */ > + STORE_ONE_REG(1) > + /* Use $r1 to save PC */ > + "pcaddi $r1, 0\n\t" > + STR_LONG_S " $r1, %0\n\t" > + /* Restore $r1 */ > + STR_LONG_L " $r1, %1, "STR_LONGSIZE"\n\t" > + STORE_ONE_REG(2) > + STORE_ONE_REG(3) > + STORE_ONE_REG(4) > + STORE_ONE_REG(5) > + STORE_ONE_REG(6) > + STORE_ONE_REG(7) > + STORE_ONE_REG(8) > + STORE_ONE_REG(9) > + STORE_ONE_REG(10) > + STORE_ONE_REG(11) > + STORE_ONE_REG(12) > + STORE_ONE_REG(13) > + STORE_ONE_REG(14) > + STORE_ONE_REG(15) > + STORE_ONE_REG(16) > + STORE_ONE_REG(17) > + STORE_ONE_REG(18) > + STORE_ONE_REG(19) > + STORE_ONE_REG(20) > + STORE_ONE_REG(21) > + STORE_ONE_REG(22) > + STORE_ONE_REG(23) > + STORE_ONE_REG(24) > + STORE_ONE_REG(25) > + STORE_ONE_REG(26) > + STORE_ONE_REG(27) > + STORE_ONE_REG(28) > + STORE_ONE_REG(29) > + STORE_ONE_REG(30) > + STORE_ONE_REG(31) > + : "=3Dm" (regs->csr_era) > + : "r" (regs->regs) > + : "memory"); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_BADV) : "=3Dr"=20 > (regs->csr_badvaddr)); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_CRMD) : "=3Dr"=20 > (regs->csr_crmd)); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_PRMD) : "=3Dr"=20 > (regs->csr_prmd)); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_EUEN) : "=3Dr"=20 > (regs->csr_euen)); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_ECFG) : "=3Dr"=20 > (regs->csr_ecfg)); > + __asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_ESTAT) : "=3Dr"=20 > (regs->csr_estat)); > +} > + > +#endif /* _ASM_STACKTRACE_H */ > diff --git a/arch/loongarch/include/uapi/asm/break.h=20 > b/arch/loongarch/include/uapi/asm/break.h > new file mode 100644 > index 000000000000..bb9b82ba59f2 > --- /dev/null > +++ b/arch/loongarch/include/uapi/asm/break.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#ifndef __UAPI_ASM_BREAK_H > +#define __UAPI_ASM_BREAK_H > + > +#define BRK_DEFAULT 0 /* Used as default */ > +#define BRK_BUG 1 /* Used by BUG() */ > +#define BRK_KDB 2 /* Used in KDB_ENTER() */ > +#define BRK_MATHEMU 3 /* Used by FPU emulator */ > +#define BRK_USERBP 4 /* User bp (used by debuggers) */ > +#define BRK_SSTEPBP 5 /* User bp (used by debuggers) */ > +#define BRK_OVERFLOW 6 /* Overflow check */ > +#define BRK_DIVZERO 7 /* Divide by zero check */ > +#define BRK_RANGE 8 /* Range error check */ > +#define BRK_MULOVFL 9 /* Multiply overflow */ > +#define BRK_KPROBE_BP 10 /* Kprobe break */ > +#define BRK_KPROBE_SSTEPBP 11 /* Kprobe single step break */ > +#define BRK_UPROBE_BP 12 /* See */ > +#define BRK_UPROBE_XOLBP 13 /* See */ > + > +#endif /* __UAPI_ASM_BREAK_H */ > diff --git a/arch/loongarch/kernel/access-helper.h=20 > b/arch/loongarch/kernel/access-helper.h > new file mode 100644 > index 000000000000..4a35ca81bd08 > --- /dev/null > +++ b/arch/loongarch/kernel/access-helper.h > @@ -0,0 +1,13 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +#include > + > +static inline int __get_inst(u32 *i, u32 *p, bool user) > +{ > + return user ? get_user(*i, (u32 __user *)p) : get_kernel_nofault(*i,=20 > p); > +} > + > +static inline int __get_addr(unsigned long *a, unsigned long *p, bool=20 > user) > +{ > + return user ? get_user(*a, (unsigned long __user *)p) :=20 > get_kernel_nofault(*a, p); > +} > diff --git a/arch/loongarch/kernel/genex.S=20 > b/arch/loongarch/kernel/genex.S > new file mode 100644 > index 000000000000..93496852b3cc > --- /dev/null > +++ b/arch/loongarch/kernel/genex.S > @@ -0,0 +1,95 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + * > + * Derived from MIPS: > + * Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle > + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. > + * Copyright (C) 2002, 2007 Maciej W. Rozycki > + * Copyright (C) 2001, 2012 MIPS Technologies, Inc. All rights=20 > reserved. > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > + .align 5 > +SYM_FUNC_START(__arch_cpu_idle) > + /* start of rollback region */ > + LONG_L t0, tp, TI_FLAGS > + nop > + andi t0, t0, _TIF_NEED_RESCHED > + bnez t0, 1f > + nop > + nop > + nop > + idle 0 > + /* end of rollback region */ > +1: jirl zero, ra, 0 > +SYM_FUNC_END(__arch_cpu_idle) > + > +SYM_FUNC_START(handle_vint) > + BACKUP_T0T1 > + SAVE_ALL > + la.abs t1, __arch_cpu_idle > + LONG_L t0, sp, PT_ERA > + /* 32 byte rollback region */ > + ori t0, t0, 0x1f > + xori t0, t0, 0x1f > + bne t0, t1, 1f > + LONG_S t0, sp, PT_ERA > +1: move a0, sp > + move a1, sp > + la.abs t0, do_vint > + jirl ra, t0, 0 > + RESTORE_ALL_AND_RET > +SYM_FUNC_END(handle_vint) > + > +SYM_FUNC_START(except_vec_cex) > + b cache_parity_error > +SYM_FUNC_END(except_vec_cex) > + > + .macro build_prep_badv > + csrrd t0, LOONGARCH_CSR_BADV > + PTR_S t0, sp, PT_BVADDR > + .endm > + > + .macro build_prep_fcsr > + movfcsr2gr a1, fcsr0 > + .endm > + > + .macro build_prep_none > + .endm > + > + .macro BUILD_HANDLER exception handler prep > + .align 5 > + SYM_FUNC_START(handle_\exception) > + BACKUP_T0T1 > + SAVE_ALL > + build_prep_\prep > + move a0, sp > + la.abs t0, do_\handler > + jirl ra, t0, 0 > + RESTORE_ALL_AND_RET > + SYM_FUNC_END(handle_\exception) > + .endm > + > + BUILD_HANDLER ade ade badv > + BUILD_HANDLER ale ale badv > + BUILD_HANDLER bp bp none > + BUILD_HANDLER fpe fpe fcsr > + BUILD_HANDLER fpu fpu none > + BUILD_HANDLER lsx lsx none > + BUILD_HANDLER lasx lasx none > + BUILD_HANDLER lbt lbt none > + BUILD_HANDLER ri ri none > + BUILD_HANDLER watch watch none > + BUILD_HANDLER reserved reserved none /* others */ > + > +SYM_FUNC_START(handle_sys) > + la.abs t0, handle_syscall > + jirl zero, t0, 0 > +SYM_FUNC_END(handle_sys) > diff --git a/arch/loongarch/kernel/irq.c b/arch/loongarch/kernel/irq.c > new file mode 100644 > index 000000000000..9bd07edf0bce > --- /dev/null > +++ b/arch/loongarch/kernel/irq.c > @@ -0,0 +1,77 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +DEFINE_PER_CPU(unsigned long, irq_stack); > + > +struct irq_domain *cpu_domain; > +struct irq_domain *liointc_domain; > +struct irq_domain *pch_lpc_domain; > +struct irq_domain *pch_msi_domain[MAX_IO_PICS]; > +struct irq_domain *pch_pic_domain[MAX_IO_PICS]; > + > +/* > + * 'what should we do if we get a hw irq event on an illegal vector'. > + * each architecture has to answer this themselves. > + */ > +void ack_bad_irq(unsigned int irq) > +{ > + pr_warn("Unexpected IRQ # %d\n", irq); > +} > + > +atomic_t irq_err_count; > + > +asmlinkage void spurious_interrupt(void) > +{ > + atomic_inc(&irq_err_count); > +} > + > +int arch_show_interrupts(struct seq_file *p, int prec) > +{ > + seq_printf(p, "%*s: %10u\n", prec, "ERR",=20 > atomic_read(&irq_err_count)); > + return 0; > +} > + > +void __init init_IRQ(void) > +{ > + int i; > + unsigned int order =3D get_order(IRQ_STACK_SIZE); > + struct page *page; > + > + clear_csr_ecfg(ECFG0_IM); > + clear_csr_estat(ESTATF_IP); > + > + irqchip_init(); > + > + for (i =3D 0; i < NR_IRQS; i++) > + irq_set_noprobe(i); > + > + for_each_possible_cpu(i) { > + page =3D alloc_pages_node(cpu_to_node(i), GFP_KERNEL, order); > + > + per_cpu(irq_stack, i) =3D (unsigned long)page_address(page); > + pr_debug("CPU%d IRQ stack at 0x%lx - 0x%lx\n", i, > + per_cpu(irq_stack, i), per_cpu(irq_stack, i) + IRQ_STACK_SIZE); > + } > + > + set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | ECFGF_IPI |=20 > ECFGF_PMC); > +} > diff --git a/arch/loongarch/kernel/traps.c=20 > b/arch/loongarch/kernel/traps.c > new file mode 100644 > index 000000000000..8be071b265b3 > --- /dev/null > +++ b/arch/loongarch/kernel/traps.c > @@ -0,0 +1,725 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Author: Huacai Chen > + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "access-helper.h" > + > +extern asmlinkage void handle_ade(void); > +extern asmlinkage void handle_ale(void); > +extern asmlinkage void handle_sys(void); > +extern asmlinkage void handle_bp(void); > +extern asmlinkage void handle_ri(void); > +extern asmlinkage void handle_fpu(void); > +extern asmlinkage void handle_fpe(void); > +extern asmlinkage void handle_lbt(void); > +extern asmlinkage void handle_lsx(void); > +extern asmlinkage void handle_lasx(void); > +extern asmlinkage void handle_reserved(void); > +extern asmlinkage void handle_watch(void); > +extern asmlinkage void handle_vint(void); > + > +static void show_backtrace(struct task_struct *task, const struct=20 > pt_regs *regs, > + const char *loglvl, bool user) > +{ > + unsigned long addr; > + unsigned long *sp =3D (unsigned long *)(regs->regs[3] & ~3); > + > + printk("%sCall Trace:", loglvl); > +#ifdef CONFIG_KALLSYMS > + printk("%s\n", loglvl); > +#endif > + while (!kstack_end(sp)) { > + if (__get_addr(&addr, sp++, user)) { > + printk("%s (Bad stack address)", loglvl); > + break; > + } > + if (__kernel_text_address(addr)) > + print_ip_sym(loglvl, addr); > + } > + printk("%s\n", loglvl); > +} > + > +static void show_stacktrace(struct task_struct *task, > + const struct pt_regs *regs, const char *loglvl, bool user) > +{ > + int i; > + const int field =3D 2 * sizeof(unsigned long); > + unsigned long stackdata; > + unsigned long *sp =3D (unsigned long *)regs->regs[3]; > + > + printk("%sStack :", loglvl); > + i =3D 0; > + while ((unsigned long) sp & (PAGE_SIZE - 1)) { > + if (i && ((i % (64 / field)) =3D=3D 0)) { > + pr_cont("\n"); > + printk("%s ", loglvl); > + } > + if (i > 39) { > + pr_cont(" ..."); > + break; > + } > + > + if (__get_addr(&stackdata, sp++, user)) { > + pr_cont(" (Bad stack address)"); > + break; > + } > + > + pr_cont(" %0*lx", field, stackdata); > + i++; > + } > + pr_cont("\n"); > + show_backtrace(task, regs, loglvl, user); > +} > + > +void show_stack(struct task_struct *task, unsigned long *sp, const=20 > char *loglvl) > +{ > + struct pt_regs regs; > + > + regs.csr_crmd =3D 0; > + if (sp) { > + regs.csr_era =3D 0; > + regs.regs[1] =3D 0; > + regs.regs[3] =3D (unsigned long)sp; > + } else { > + if (!task || task =3D=3D current) > + prepare_frametrace(®s); > + else { > + regs.csr_era =3D task->thread.reg01; > + regs.regs[1] =3D 0; > + regs.regs[3] =3D task->thread.reg03; > + regs.regs[22] =3D task->thread.reg22; > + } > + } > + > + show_stacktrace(task, ®s, loglvl, false); > +} > + > +static void show_code(unsigned int *pc, bool user) > +{ > + long i; > + unsigned int insn; > + > + printk("Code:"); > + > + for(i =3D -3 ; i < 6 ; i++) { > + if (__get_inst(&insn, pc + i, user)) { > + pr_cont(" (Bad address in era)\n"); > + break; > + } > + pr_cont("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>')); > + } > + pr_cont("\n"); > +} > + > +static void __show_regs(const struct pt_regs *regs) > +{ > + const int field =3D 2 * sizeof(unsigned long); > + unsigned int excsubcode; > + unsigned int exccode; > + int i; > + > + show_regs_print_info(KERN_DEFAULT); > + > + /* > + * Saved main processor registers > + */ > + for (i =3D 0; i < 32; ) { > + if ((i % 4) =3D=3D 0) > + printk("$%2d :", i); > + pr_cont(" %0*lx", field, regs->regs[i]); > + > + i++; > + if ((i % 4) =3D=3D 0) > + pr_cont("\n"); > + } > + > + /* > + * Saved csr registers > + */ > + printk("era : %0*lx %pS\n", field, regs->csr_era, > + (void *) regs->csr_era); > + printk("ra : %0*lx %pS\n", field, regs->regs[1], > + (void *) regs->regs[1]); > + > + printk("CSR crmd: %08lx ", regs->csr_crmd); > + printk("CSR prmd: %08lx ", regs->csr_prmd); > + printk("CSR euen: %08lx ", regs->csr_euen); > + printk("CSR ecfg: %08lx ", regs->csr_ecfg); > + printk("CSR estat: %08lx ", regs->csr_estat); > + > + pr_cont("\n"); > + > + exccode =3D ((regs->csr_estat) & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHI= FT; > + excsubcode =3D ((regs->csr_estat) & CSR_ESTAT_ESUBCODE) >>=20 > CSR_ESTAT_ESUBCODE_SHIFT; > + printk("ExcCode : %x (SubCode %x)\n", exccode, excsubcode); > + > + if (exccode >=3D EXCCODE_TLBL && exccode <=3D EXCCODE_ALE) > + printk("BadVA : %0*lx\n", field, regs->csr_badvaddr); > + > + printk("PrId : %08x (%s)\n", read_cpucfg(LOONGARCH_CPUCFG0), > + cpu_family_string()); > +} > + > +void show_regs(struct pt_regs *regs) > +{ > + __show_regs((struct pt_regs *)regs); > + dump_stack(); > +} > + > +void show_registers(struct pt_regs *regs) > +{ > + __show_regs(regs); > + print_modules(); > + printk("Process %s (pid: %d, threadinfo=3D%p, task=3D%p)\n", > + current->comm, current->pid, current_thread_info(), current); > + > + show_stacktrace(current, regs, KERN_DEFAULT, user_mode(regs)); > + show_code((void *)regs->csr_era, user_mode(regs)); > + printk("\n"); > +} > + > +static DEFINE_RAW_SPINLOCK(die_lock); > + > +void __noreturn die(const char *str, struct pt_regs *regs) > +{ > + static int die_counter; > + int sig =3D SIGSEGV; > + > + oops_enter(); > + > + if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_nr, > + SIGSEGV) =3D=3D NOTIFY_STOP) > + sig =3D 0; > + > + console_verbose(); > + raw_spin_lock_irq(&die_lock); > + bust_spinlocks(1); > + > + printk("%s[#%d]:\n", str, ++die_counter); > + show_registers(regs); > + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); > + raw_spin_unlock_irq(&die_lock); > + > + oops_exit(); > + > + if (in_interrupt()) > + panic("Fatal exception in interrupt"); > + > + if (panic_on_oops) > + panic("Fatal exception"); > + > + make_task_dead(sig); > +} > + > +static inline void setup_vint_size(unsigned int size) > +{ > + unsigned int vs; > + > + vs =3D ilog2(size/4); > + > + if (vs =3D=3D 0 || vs > 7) > + panic("vint_size %d Not support yet", vs); > + > + csr_xchg32(vs< +} > + > +/* > + * Send SIGFPE according to FCSR Cause bits, which must have already > + * been masked against Enable bits. This is impotant as Inexact can > + * happen together with Overflow or Underflow, and `ptrace' can set > + * any bits. > + */ > +void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr, > + struct task_struct *tsk) > +{ > + int si_code =3D FPE_FLTUNK; > + > + if (fcsr & FPU_CSR_INV_X) > + si_code =3D FPE_FLTINV; > + else if (fcsr & FPU_CSR_DIV_X) > + si_code =3D FPE_FLTDIV; > + else if (fcsr & FPU_CSR_OVF_X) > + si_code =3D FPE_FLTOVF; > + else if (fcsr & FPU_CSR_UDF_X) > + si_code =3D FPE_FLTUND; > + else if (fcsr & FPU_CSR_INE_X) > + si_code =3D FPE_FLTRES; > + > + force_sig_fault(SIGFPE, si_code, fault_addr); > +} > + > +int process_fpemu_return(int sig, void __user *fault_addr, unsigned=20 > long fcsr) > +{ > + int si_code; > + > + switch (sig) { > + case 0: > + return 0; > + > + case SIGFPE: > + force_fcsr_sig(fcsr, fault_addr, current); > + return 1; > + > + case SIGBUS: > + force_sig_fault(SIGBUS, BUS_ADRERR, fault_addr); > + return 1; > + > + case SIGSEGV: > + mmap_read_lock(current->mm); > + if (vma_lookup(current->mm, (unsigned long)fault_addr)) > + si_code =3D SEGV_ACCERR; > + else > + si_code =3D SEGV_MAPERR; > + mmap_read_unlock(current->mm); > + force_sig_fault(SIGSEGV, si_code, fault_addr); > + return 1; > + > + default: > + force_sig(sig); > + return 1; > + } > +} > + > +/* > + * Delayed fp exceptions when doing a lazy ctx switch > + */ > +asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long=20 > fcsr) > +{ > + int sig; > + void __user *fault_addr; > + irqentry_state_t state =3D irqentry_enter(regs); > + > + if (notify_die(DIE_FP, "FP exception", regs, 0,=20 > current->thread.trap_nr, > + SIGFPE) =3D=3D NOTIFY_STOP) > + goto out; > + > + /* Clear FCSR.Cause before enabling interrupts */ > + write_fcsr(LOONGARCH_FCSR0, fcsr & ~mask_fcsr_x(fcsr)); > + local_irq_enable(); > + > + die_if_kernel("FP exception in kernel code", regs); > + > + sig =3D SIGFPE; > + fault_addr =3D (void __user *) regs->csr_era; > + > + /* Send a signal if required. */ > + process_fpemu_return(sig, fault_addr, fcsr); > + > +out: > + local_irq_disable(); > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_ade(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + die_if_kernel("Kernel ade access", regs); > + force_sig_fault(SIGBUS, BUS_ADRERR, (void __user=20 > *)regs->csr_badvaddr); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_ale(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + die_if_kernel("Kernel ale access", regs); > + force_sig_fault(SIGBUS, BUS_ADRALN, (void __user=20 > *)regs->csr_badvaddr); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_bp(struct pt_regs *regs) > +{ > + bool user =3D user_mode(regs); > + unsigned int opcode, bcode; > + unsigned long era =3D exception_era(regs); > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + current->thread.trap_nr =3D read_csr_excode(); > + if (__get_inst(&opcode, (u32 *)era, user)) > + goto out_sigsegv; > + > + bcode =3D (opcode & 0x7fff); > + > + /* > + * notify the kprobe handlers, if instruction is likely to > + * pertain to them. > + */ > + switch (bcode) { > + case BRK_KPROBE_BP: > + if (notify_die(DIE_BREAK, "Kprobe", regs, bcode, > + current->thread.trap_nr, SIGTRAP) =3D=3D NOTIFY_STOP) > + goto out; > + else > + break; > + case BRK_KPROBE_SSTEPBP: > + if (notify_die(DIE_SSTEPBP, "Kprobe_SingleStep", regs, bcode, > + current->thread.trap_nr, SIGTRAP) =3D=3D NOTIFY_STOP) > + goto out; > + else > + break; > + case BRK_UPROBE_BP: > + if (notify_die(DIE_UPROBE, "Uprobe", regs, bcode, > + current->thread.trap_nr, SIGTRAP) =3D=3D NOTIFY_STOP) > + goto out; > + else > + break; > + case BRK_UPROBE_XOLBP: > + if (notify_die(DIE_UPROBE_XOL, "Uprobe_XOL", regs, bcode, > + current->thread.trap_nr, SIGTRAP) =3D=3D NOTIFY_STOP) > + goto out; > + else > + break; > + default: > + if (notify_die(DIE_TRAP, "Break", regs, bcode, > + current->thread.trap_nr, SIGTRAP) =3D=3D NOTIFY_STOP) > + goto out; > + else > + break; > + } > + > + switch (bcode) { > + case BRK_BUG: > + die_if_kernel("Kernel bug detected", regs); > + force_sig(SIGTRAP); > + break; > + case BRK_DIVZERO: > + die_if_kernel("Break instruction in kernel code", regs); > + force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->csr_era); > + break; > + case BRK_OVERFLOW: > + die_if_kernel("Break instruction in kernel code", regs); > + force_sig_fault(SIGFPE, FPE_INTOVF, (void __user *)regs->csr_era); > + break; > + default: > + die_if_kernel("Break instruction in kernel code", regs); > + force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->csr_era); > + break; > + } > + > +out: > + local_irq_disable(); > + irqentry_exit(regs, state); > + return; > + > +out_sigsegv: > + force_sig(SIGSEGV); > + goto out; > +} > + > +asmlinkage void noinstr do_watch(struct pt_regs *regs) > +{ > + pr_warn("Hardware watch point handler not implemented!\n"); > +} > + > +asmlinkage void noinstr do_ri(struct pt_regs *regs) > +{ > + int status =3D -1; > + unsigned int opcode =3D 0; > + unsigned int __user *era =3D (unsigned int __user *)exception_era(re= gs); > + unsigned long old_era =3D regs->csr_era; > + unsigned long old_ra =3D regs->regs[1]; > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + current->thread.trap_nr =3D read_csr_excode(); > + > + if (notify_die(DIE_RI, "RI Fault", regs, 0, current->thread.trap_nr, > + SIGILL) =3D=3D NOTIFY_STOP) > + goto out; > + > + die_if_kernel("Reserved instruction in kernel code", regs); > + > + if (unlikely(compute_return_era(regs) < 0)) > + goto out; > + > + if (unlikely(get_user(opcode, era) < 0)) { > + status =3D SIGSEGV; > + current->thread.error_code =3D 1; > + } > + > + if (status < 0) > + status =3D SIGILL; > + > + if (unlikely(status > 0)) { > + regs->csr_era =3D old_era; /* Undo skip-over. */ > + regs->regs[1] =3D old_ra; > + force_sig(status); > + } > + > +out: > + local_irq_disable(); > + irqentry_exit(regs, state); > +} > + > +static void init_restore_fp(void) > +{ > + if (!used_math()) { > + /* First time FP context user. */ > + init_fpu(); > + } else { > + /* This task has formerly used the FP context */ > + if (!is_fpu_owner()) > + own_fpu_inatomic(1); > + } > + > + BUG_ON(!is_fp_enabled()); > +} > + > +asmlinkage void noinstr do_fpu(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + die_if_kernel("do_fpu invoked from kernel context!", regs); > + > + preempt_disable(); > + init_restore_fp(); > + preempt_enable(); > + > + local_irq_disable(); > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_lsx(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + force_sig(SIGILL); > + local_irq_disable(); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_lasx(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + force_sig(SIGILL); > + local_irq_disable(); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_lbt(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + force_sig(SIGILL); > + local_irq_disable(); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void noinstr do_reserved(struct pt_regs *regs) > +{ > + irqentry_state_t state =3D irqentry_enter(regs); > + > + local_irq_enable(); > + /* > + * Game over - no way to handle this if it ever occurs. Most probably > + * caused by a fatal error after another hardware/software error. > + */ > + pr_err("Caught reserved exception %u on pid:%d [%s] - should not=20 > happen\n", > + read_csr_excode(), current->pid, current->comm); > + die_if_kernel("do_reserved exception", regs); > + force_sig(SIGUNUSED); > + > + local_irq_disable(); > + > + irqentry_exit(regs, state); > +} > + > +asmlinkage void cache_parity_error(void) > +{ > + /* For the moment, report the problem and hang. */ > + pr_err("Cache error exception:\n"); > + pr_err("csr_merrctl =3D=3D %08x\n", csr_read32(LOONGARCH_CSR_MERRCTL= )); > + pr_err("csr_merrera =3D=3D %016llx\n", csr_read64(LOONGARCH_CSR_MERR= ERA)); > + panic("Can't handle the cache error!"); > +} > + > +asmlinkage void noinstr handle_loongarch_irq(struct pt_regs *regs) > +{ > + struct pt_regs *old_regs; > + > + irq_enter_rcu(); > + old_regs =3D set_irq_regs(regs); > + handle_arch_irq(regs); > + set_irq_regs(old_regs); > + irq_exit_rcu(); > +} > + > +asmlinkage void noinstr do_vint(struct pt_regs *regs, unsigned long s= p) > +{ > + register int cpu; > + register unsigned long stack; > + irqentry_state_t state =3D irqentry_enter(regs); > + > + cpu =3D smp_processor_id(); > + > + if (on_irq_stack(cpu, sp)) > + handle_loongarch_irq(regs); > + else { > + stack =3D per_cpu(irq_stack, cpu) + IRQ_STACK_START; > + > + /* Save task's sp on IRQ stack for unwinding */ > + *(unsigned long *)stack =3D sp; > + > + __asm__ __volatile__( > + "move $s0, $sp \n" /* Preserve sp */ > + "move $sp, %[stk] \n" /* Switch stack */ > + "move $a0, %[regs] \n" > + "bl handle_loongarch_irq \n" > + "move $sp, $s0 \n" /* Restore sp */ > + : /* No outputs */ > + : [stk] "r" (stack), [regs] "r" (regs) > + : "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", "$s0", > + "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8", > + "memory"); > + } > + > + irqentry_exit(regs, state); > +} > + > +extern void tlb_init(void); > +extern void cache_error_setup(void); > + > +unsigned long eentry; > +unsigned long tlbrentry; > + > +long exception_handlers[VECSIZE * 128 / sizeof(long)]=20 > __aligned(SZ_64K); > + > +static void configure_exception_vector(void) > +{ > + eentry =3D (unsigned long)exception_handlers; > + tlbrentry =3D (unsigned long)exception_handlers + 80*VECSIZE; > + > + csr_write64(eentry, LOONGARCH_CSR_EENTRY); > + csr_write64(eentry, LOONGARCH_CSR_MERRENTRY); > + csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY); > +} > + > +void per_cpu_trap_init(int cpu) > +{ > + unsigned int i; > + > + setup_vint_size(VECSIZE); > + > + configure_exception_vector(); > + > + if (!cpu_data[cpu].asid_cache) > + cpu_data[cpu].asid_cache =3D asid_first_version(cpu); > + > + mmgrab(&init_mm); > + current->active_mm =3D &init_mm; > + BUG_ON(current->mm); > + enter_lazy_tlb(&init_mm, current); > + > + /* Initialise exception handlers */ > + if (cpu =3D=3D 0) > + for (i =3D 0; i < 64; i++) > + set_handler(i * VECSIZE, handle_reserved, VECSIZE); > + > + tlb_init(); > + cpu_cache_init(); > +} > + > +/* Install CPU exception handler */ > +void set_handler(unsigned long offset, void *addr, unsigned long size) > +{ > + memcpy((void *)(eentry + offset), addr, size); > + local_flush_icache_range(eentry + offset, eentry + offset + size); > +} > + > +static const char panic_null_cerr[] =3D > + "Trying to set NULL cache error exception handler\n"; > + > +/* > + * Install uncached CPU exception handler. > + * This is suitable only for the cache error exception which is the=20 > only > + * exception handler that is being run uncached. > + */ > +void set_merr_handler(unsigned long offset, void *addr, unsigned long=20 > size) > +{ > + unsigned long uncached_eentry =3D TO_UNCACHE(__pa(eentry)); > + > + if (!addr) > + panic(panic_null_cerr); > + > + memcpy((void *)(uncached_eentry + offset), addr, size); > +} > + > +void __init trap_init(void) > +{ > + long i; > + > + /* Set interrupt vector handler */ > + for (i =3D EXCCODE_INT_START; i < EXCCODE_INT_END; i++) > + set_handler(i * VECSIZE, handle_vint, VECSIZE); > + > + set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); > + set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); > + set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); > + set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); > + set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); > + set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); > + set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); > + set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); > + set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); > + set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); > + set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); > + set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); > + > + cache_error_setup(); > + > + local_flush_icache_range(eentry, eentry + 0x400); > +} > --=20 > 2.27.0 --=20 - Jiaxun