From nobody Thu Apr 2 14:13:09 2026 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F390D337BA6 for ; Sat, 28 Mar 2026 04:29:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=114.242.206.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774672171; cv=none; b=av6agpMlpRh7reIp8Y66lXbrm7voF3bUj/kYelOQ3VhfgeULyPzwgvx3GPAgU5crufY/FpMSsYEO0rrNtURP4mkaUI5LmgdqobT5YAQfLWoVrXOWT/da/Y1uI/e47jgroc2bfWNzefMC1M1Sl90QpIvNawPJ0ntUfcwmh3AGt/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774672171; c=relaxed/simple; bh=sL2oOMZ5oVS1ouIdDJ5qLzY4EaJkwfIfjze0HdhjJZE=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=EL9UTsgS4RCca9VYka0yxy1JOYTsvZRefuLEO0ReV9ddXn0aTYd+Qzrf87NSKn2GEEYquBqLJ5LDsricevjL91MTcWneaLoSgs3T4iDvZHfT8YuHqfgZln0mbZ5X90BYqVyFIr03DbQlarkOhIYvPiq+yp6Vp5OTB1a9IG3lWGU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn; spf=pass smtp.mailfrom=loongson.cn; arc=none smtp.client-ip=114.242.206.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=loongson.cn Received: from loongson.cn (unknown [223.64.68.17]) by gateway (Coremail) with SMTP id _____8BxmsIgWcdpIoQfAA--.24722S3; Sat, 28 Mar 2026 12:29:20 +0800 (CST) Received: from kernelserver (unknown [223.64.68.17]) by front1 (Coremail) with SMTP id qMiowJAxVcAbWcdpDUZfAA--.30335S2; Sat, 28 Mar 2026 12:29:18 +0800 (CST) From: Huacai Chen To: Huacai Chen Cc: loongarch@lists.linux.dev, Xuefeng Li , Guo Ren , Xuerui Wang , Jiaxun Yang , linux-kernel@vger.kernel.org, Huacai Chen Subject: [PATCH] LoongArch: Add HIGHMEM (PKMAP and FIX_KMAP) support Date: Sat, 28 Mar 2026 12:29:08 +0800 Message-ID: <20260328042908.3184541-1-chenhuacai@loongson.cn> X-Mailer: git-send-email 2.52.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: qMiowJAxVcAbWcdpDUZfAA--.30335S2 X-CM-SenderInfo: hfkh0x5xdftxo6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj93XoW3Kw1kZw1xKw4DCw4fCF48GrX_yoWDAF1UpF nrCwn5Wr4rG34IyrZxtas5urn8Jan7Kr12qFy2va4UZ3W2qr1DZr1kWrZxXFy8Xa95JFWf WryfGw1agFs8XwcCm3ZEXasCq-sJn29KB7ZKAUJUUUUr529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUvEb4IE77IF4wAFF20E14v26r1j6r4UM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Jr0_JF4l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Jr0_Gr1l84ACjcxK6I8E87Iv67AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_ Gr0_Gr1UM2kKe7AKxVWUXVWUAwAS0I0E0xvYzxvE52x082IY62kv0487Mc804VCY07AIYI kI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUXVWU AwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI4 8JMxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMxCIbckI1I0E14v26r1Y 6r17MI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7 AF67AKxVWUtVW8ZwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE 2Ix0cI8IcVCY1x0267AKxVWUJVW8JwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcV C2z280aVAFwI0_Jr0_Gr1lIxAIcVC2z280aVCY1x0267AKxVWUJVW8JbIYCTnIWIevJa73 UjIFyTuYvjxU2MKZDUUUU Content-Type: text/plain; charset="utf-8" Add HIGHMEM (High Memory) support for LoongArch, mostly needed by 32BIT kernel because the size of kernel virtual memory space is only 512MB and the size of usable physical memory is only 256MB in this case. HIGHMEM adds permanent kernel mapping (PKMAP) and fixed kernel mapping (FIX_KMAP), which increase usable physical memory up to 2.25GB (2304MB). We can just use the generic copy_user_highpage(), so remove the custom version. Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 5 +++ arch/loongarch/include/asm/fixmap.h | 14 +++++++ arch/loongarch/include/asm/highmem.h | 43 ++++++++++++++++++++ arch/loongarch/include/asm/page.h | 4 -- arch/loongarch/include/asm/pgtable.h | 12 ++++++ arch/loongarch/mm/Makefile | 1 + arch/loongarch/mm/highmem.c | 12 ++++++ arch/loongarch/mm/init.c | 61 +++++++++++++++++++++------- arch/loongarch/mm/pgtable.c | 27 ++++++++++++ 9 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 arch/loongarch/include/asm/highmem.h create mode 100644 arch/loongarch/mm/highmem.c diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 2cdabdde3588..b7f827237f71 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -348,6 +348,11 @@ config RUSTC_HAS_ANNOTATE_TABLEJUMP =20 source "kernel/Kconfig.hz" =20 +config HIGHMEM + bool "High Memory Support" + depends on 32BIT + select KMAP_LOCAL + choice prompt "Page Table Layout" default 16KB_2LEVEL if 32BIT diff --git a/arch/loongarch/include/asm/fixmap.h b/arch/loongarch/include/a= sm/fixmap.h index d2e55ae55bb9..dce2da6ba787 100644 --- a/arch/loongarch/include/asm/fixmap.h +++ b/arch/loongarch/include/asm/fixmap.h @@ -8,10 +8,19 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H =20 +#ifdef CONFIG_HIGHMEM +#include +#include +#endif + #define NR_FIX_BTMAPS 64 =20 enum fixed_addresses { FIX_HOLE, +#ifdef CONFIG_HIGHMEM + FIX_KMAP_BEGIN, + FIX_KMAP_END =3D FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1, +#endif FIX_EARLYCON_MEM_BASE, __end_of_fixed_addresses }; @@ -25,4 +34,9 @@ extern void __set_fixmap(enum fixed_addresses idx, =20 #include =20 +/* + * Called from pagetable_init() + */ +extern void fixrange_init(unsigned long start, unsigned long end, pgd_t *p= gd_base); + #endif diff --git a/arch/loongarch/include/asm/highmem.h b/arch/loongarch/include/= asm/highmem.h new file mode 100644 index 000000000000..e6d7a662d340 --- /dev/null +++ b/arch/loongarch/include/asm/highmem.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * highmem.h: virtual kernel memory mappings for high memory + * + * Used in CONFIG_HIGHMEM systems for memory pages which + * are not addressable by direct kernel virtual addresses. + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#ifdef __KERNEL__ + +#include + +#ifndef __ASSEMBLER__ + +extern pte_t *pkmap_page_table; + +#define ARCH_HAS_KMAP_FLUSH_TLB +void kmap_flush_tlb(unsigned long addr); + +#endif /* !__ASSEMBLER__ */ + +/* + * Right now we initialize only a single pte table. It can be extended + * easily, subsequent pte tables have to be allocated in one physical + * chunk of RAM. + */ +#define LAST_PKMAP 1024 +#define LAST_PKMAP_MASK (LAST_PKMAP - 1) +#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT) +#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) + +#define flush_cache_kmaps() do {} while (0) + +#define arch_kmap_local_post_map(vaddr, pteval) local_flush_tlb_one(vaddr) +#define arch_kmap_local_post_unmap(vaddr) local_flush_tlb_one(vaddr) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_HIGHMEM_H */ diff --git a/arch/loongarch/include/asm/page.h b/arch/loongarch/include/asm= /page.h index 327bf0bc92bf..8121c0f136da 100644 --- a/arch/loongarch/include/asm/page.h +++ b/arch/loongarch/include/asm/page.h @@ -36,10 +36,6 @@ extern unsigned long shm_align_mask; =20 struct page; struct vm_area_struct; -void copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr, struct vm_area_struct *vma); - -#define __HAVE_ARCH_COPY_USER_HIGHPAGE =20 typedef struct { unsigned long pte; } pte_t; #define pte_val(x) ((x).pte) diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/= asm/pgtable.h index c33b3bcb733e..cd5e56bfbe7f 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -23,6 +23,10 @@ #include #endif =20 +#ifdef CONFIG_HIGHMEM +#include +#endif + #if CONFIG_PGTABLE_LEVELS =3D=3D 2 #define PGDIR_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PTRLOG)) #elif CONFIG_PGTABLE_LEVELS =3D=3D 3 @@ -86,7 +90,15 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(= unsigned long)]; #ifdef CONFIG_32BIT =20 #define VMALLOC_START (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE)) + +#ifdef CONFIG_HIGHMEM +#define VMALLOC_END (PKMAP_BASE - (2 * PAGE_SIZE)) +#else #define VMALLOC_END (FIXADDR_START - (2 * PAGE_SIZE)) +#endif + +#define PKMAP_BASE (PKMAP_END - (PAGE_SIZE * LAST_PKMAP)) +#define PKMAP_END ((FIXADDR_START) & ~((LAST_PKMAP << PAGE_SHIFT)-1)) =20 #endif =20 diff --git a/arch/loongarch/mm/Makefile b/arch/loongarch/mm/Makefile index 278be2c8fc36..2aae3773de77 100644 --- a/arch/loongarch/mm/Makefile +++ b/arch/loongarch/mm/Makefile @@ -7,6 +7,7 @@ obj-y +=3D init.o cache.o tlb.o tlbex.o extable.o \ fault.o ioremap.o maccess.o mmap.o pgtable.o \ page.o pageattr.o =20 +obj-$(CONFIG_HIGHMEM) +=3D highmem.o obj-$(CONFIG_HUGETLB_PAGE) +=3D hugetlbpage.o obj-$(CONFIG_KASAN) +=3D kasan_init.o =20 diff --git a/arch/loongarch/mm/highmem.c b/arch/loongarch/mm/highmem.c new file mode 100644 index 000000000000..8a5789ee6842 --- /dev/null +++ b/arch/loongarch/mm/highmem.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +void kmap_flush_tlb(unsigned long addr) +{ + flush_tlb_one(addr); +} +EXPORT_SYMBOL(kmap_flush_tlb); diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index c331bf69d2ec..bf51f4a1b086 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -39,20 +39,6 @@ unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_al= igned_bss; EXPORT_SYMBOL(empty_zero_page); =20 -void copy_user_highpage(struct page *to, struct page *from, - unsigned long vaddr, struct vm_area_struct *vma) -{ - void *vfrom, *vto; - - vfrom =3D kmap_local_page(from); - vto =3D kmap_local_page(to); - copy_page(vto, vfrom); - kunmap_local(vfrom); - kunmap_local(vto); - /* Make sure this page is cleared on other CPU's too before using it */ - smp_wmb(); -} - int __ref page_is_ram(unsigned long pfn) { unsigned long addr =3D PFN_PHYS(pfn); @@ -66,6 +52,9 @@ void __init arch_zone_limits_init(unsigned long *max_zone= _pfns) max_zone_pfns[ZONE_DMA32] =3D MAX_DMA32_PFN; #endif max_zone_pfns[ZONE_NORMAL] =3D max_low_pfn; +#ifdef CONFIG_HIGHMEM + max_zone_pfns[ZONE_HIGHMEM] =3D max_pfn; +#endif } =20 void __ref free_initmem(void) @@ -73,6 +62,50 @@ void __ref free_initmem(void) free_initmem_default(POISON_FREE_INITMEM); } =20 +#ifdef CONFIG_HIGHMEM + +void __init fixrange_init(unsigned long start, unsigned long end, pgd_t *p= gd_base) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + int i, j, k; + int ptrs_per_pgd; + unsigned long vaddr; + + vaddr =3D start; + i =3D pgd_index(vaddr); + j =3D pud_index(vaddr); + k =3D pmd_index(vaddr); + pgd =3D pgd_base + i; + ptrs_per_pgd =3D min((1 << (BITS_PER_LONG - PGDIR_SHIFT)), PTRS_PER_PGD); + + for ( ; (i < ptrs_per_pgd) && (vaddr < end); pgd++, i++) { + pud =3D (pud_t *)pgd; + for ( ; (j < PTRS_PER_PUD) && (vaddr < end); pud++, j++) { + pmd =3D (pmd_t *)pud; + for (; (k < PTRS_PER_PMD) && (vaddr < end); pmd++, k++) { + if (pmd_none(*pmd)) { + pte =3D (pte_t *) memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); + if (!pte) + panic("%s: Failed to allocate %lu bytes align=3D%lx\n", + __func__, PAGE_SIZE, PAGE_SIZE); + + kernel_pte_init(pte); + set_pmd(pmd, __pmd((unsigned long)pte)); + BUG_ON(pte !=3D pte_offset_kernel(pmd, 0)); + } + vaddr +=3D PMD_SIZE; + } + k =3D 0; + } + j =3D 0; + } +} + +#endif + #ifdef CONFIG_MEMORY_HOTPLUG int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *param= s) { diff --git a/arch/loongarch/mm/pgtable.c b/arch/loongarch/mm/pgtable.c index 352d9b2e02ab..4ee188e38fed 100644 --- a/arch/loongarch/mm/pgtable.c +++ b/arch/loongarch/mm/pgtable.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,15 @@ void set_pmd_at(struct mm_struct *mm, unsigned long ad= dr, =20 void __init pagetable_init(void) { +#ifdef CONFIG_HIGHMEM + unsigned long vaddr; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; +#endif + /* Initialize the entire pgd. */ pgd_init(swapper_pg_dir); pgd_init(invalid_pg_dir); @@ -153,4 +163,21 @@ void __init pagetable_init(void) #ifndef __PAGETABLE_PMD_FOLDED pmd_init(invalid_pmd_table); #endif + +#ifdef CONFIG_HIGHMEM + /* Permanent kmaps */ + vaddr =3D PKMAP_BASE; + fixrange_init(vaddr & PMD_MASK, vaddr + PAGE_SIZE * LAST_PKMAP, swapper_p= g_dir); + + pgd =3D swapper_pg_dir + pgd_index(vaddr); + p4d =3D p4d_offset(pgd, vaddr); + pud =3D pud_offset(p4d, vaddr); + pmd =3D pmd_offset(pud, vaddr); + pte =3D pte_offset_kernel(pmd, vaddr); + pkmap_page_table =3D pte; + + /* Fixed mappings */ + vaddr =3D __fix_to_virt(__end_of_fixed_addresses - 1); + fixrange_init(vaddr & PMD_MASK, vaddr + FIXADDR_SIZE, swapper_pg_dir); +#endif } --=20 2.52.0