Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the linux alpha-specific parts of the memory manager.
#
obj-y := init.o fault.o tlbflush.o
+248
View File
@@ -0,0 +1,248 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/alpha/mm/fault.c
*
* Copyright (C) 1995 Linus Torvalds
*/
#include <linux/sched/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/io.h>
#define __EXTERN_INLINE inline
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#undef __EXTERN_INLINE
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/extable.h>
#include <linux/uaccess.h>
#include <linux/perf_event.h>
extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *);
/*
* Force a new ASN for a task.
*/
#ifndef CONFIG_SMP
unsigned long last_asn = ASN_FIRST_VERSION;
#endif
void
__load_new_mm_context(struct mm_struct *next_mm)
{
unsigned long mmc;
struct pcb_struct *pcb;
mmc = __get_new_mm_context(next_mm, smp_processor_id());
next_mm->context[smp_processor_id()] = mmc;
pcb = &current_thread_info()->pcb;
pcb->asn = mmc & HARDWARE_ASN_MASK;
pcb->ptbr = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT;
__reload_thread(pcb);
}
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to handle_mm_fault().
*
* mmcsr:
* 0 = translation not valid
* 1 = access violation
* 2 = fault-on-read
* 3 = fault-on-execute
* 4 = fault-on-write
*
* cause:
* -1 = instruction fetch
* 0 = load
* 1 = store
*
* Registers $9 through $15 are saved in a block just prior to `regs' and
* are saved and restored around the call to allow exception code to
* modify them.
*/
/* Macro for exception fixup code to access integer registers. */
#define dpf_reg(r) \
(((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-17 : \
(r) <= 18 ? (r)+11 : (r)-10])
asmlinkage void
do_page_fault(unsigned long address, unsigned long mmcsr,
long cause, struct pt_regs *regs)
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
const struct exception_table_entry *fixup;
int si_code = SEGV_MAPERR;
vm_fault_t fault;
unsigned int flags = FAULT_FLAG_DEFAULT;
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
(or is suppressed by the PALcode). Support that for older CPUs
by ignoring such an instruction. */
if (cause == 0) {
unsigned int insn;
__get_user(insn, (unsigned int __user *)regs->pc);
if ((insn >> 21 & 0x1f) == 0x1f &&
/* ldq ldl ldt lds ldg ldf ldwu ldbu */
(1ul << (insn >> 26) & 0x30f00001400ul)) {
regs->pc += 4;
return;
}
}
/* If we're in an interrupt context, or have no user context,
we must not take the fault. */
if (!mm || faulthandler_disabled())
goto no_context;
#ifdef CONFIG_ALPHA_LARGE_VMALLOC
if (address >= TASK_SIZE)
goto vmalloc_fault;
#endif
if (user_mode(regs))
flags |= FAULT_FLAG_USER;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
retry:
vma = lock_mm_and_find_vma(mm, address, regs);
if (!vma)
goto bad_area_nosemaphore;
/* Ok, we have a good vm_area for this memory access, so
we can handle it. */
si_code = SEGV_ACCERR;
if (cause < 0) {
if (!(vma->vm_flags & VM_EXEC))
goto bad_area;
} else if (!cause) {
/* Allow reads even for write-only mappings */
if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
goto bad_area;
} else {
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
flags |= FAULT_FLAG_WRITE;
}
/* If for any reason at all we couldn't handle the fault,
make sure we exit gracefully rather than endlessly redo
the fault. */
fault = handle_mm_fault(vma, address, flags, regs);
if (fault_signal_pending(fault, regs)) {
if (!user_mode(regs))
goto no_context;
return;
}
/* The fault is fully completed (including releasing mmap lock) */
if (fault & VM_FAULT_COMPLETED)
return;
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM)
goto out_of_memory;
else if (fault & VM_FAULT_SIGSEGV)
goto bad_area;
else if (fault & VM_FAULT_SIGBUS)
goto do_sigbus;
BUG();
}
if (fault & VM_FAULT_RETRY) {
flags |= FAULT_FLAG_TRIED;
/* No need to mmap_read_unlock(mm) as we would
* have already released it in __lock_page_or_retry
* in mm/filemap.c.
*/
goto retry;
}
mmap_read_unlock(mm);
return;
/* Something tried to access memory that isn't in our memory map.
Fix it, but check if it's kernel or user first. */
bad_area:
mmap_read_unlock(mm);
bad_area_nosemaphore:
if (user_mode(regs))
goto do_sigsegv;
no_context:
/* Are we prepared to handle this fault as an exception? */
if ((fixup = search_exception_tables(regs->pc)) != 0) {
unsigned long newpc;
newpc = fixup_exception(dpf_reg, fixup, regs->pc);
regs->pc = newpc;
return;
}
/* Oops. The kernel tried to access some bad page. We'll have to
terminate things with extreme prejudice. */
printk(KERN_ALERT "Unable to handle kernel paging request at "
"virtual address %016lx\n", address);
die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
make_task_dead(SIGKILL);
/* We ran out of memory, or some other thing happened to us that
made us unable to handle the page fault gracefully. */
out_of_memory:
mmap_read_unlock(mm);
if (!user_mode(regs))
goto no_context;
pagefault_out_of_memory();
return;
do_sigbus:
mmap_read_unlock(mm);
/* Send a sigbus, regardless of whether we were in kernel
or user mode. */
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *) address);
if (!user_mode(regs))
goto no_context;
return;
do_sigsegv:
force_sig_fault(SIGSEGV, si_code, (void __user *) address);
return;
#ifdef CONFIG_ALPHA_LARGE_VMALLOC
vmalloc_fault:
if (user_mode(regs))
goto do_sigsegv;
else {
/* Synchronize this task's top level page-table
with the "reference" page table from init. */
long index = pgd_index(address);
pgd_t *pgd, *pgd_k;
pgd = current->active_mm->pgd + index;
pgd_k = swapper_pg_dir + index;
if (!pgd_present(*pgd) && pgd_present(*pgd_k)) {
pgd_val(*pgd) = pgd_val(*pgd_k);
return;
}
goto no_context;
}
#endif
}
+267
View File
@@ -0,0 +1,267 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/alpha/mm/init.c
*
* Copyright (C) 1995 Linus Torvalds
*/
/* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */
#include <linux/pagemap.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/init.h>
#include <linux/memblock.h> /* max_low_pfn */
#include <linux/vmalloc.h>
#include <linux/gfp.h>
#include <linux/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/hwrpb.h>
#include <asm/dma.h>
#include <asm/mmu_context.h>
#include <asm/console.h>
#include <asm/tlb.h>
#include <asm/setup.h>
#include <asm/sections.h>
#include "../kernel/proto.h"
static struct pcb_struct original_pcb;
pgd_t *
pgd_alloc(struct mm_struct *mm)
{
pgd_t *ret, *init;
ret = __pgd_alloc(mm, 0);
init = pgd_offset(&init_mm, 0UL);
if (ret) {
#ifdef CONFIG_ALPHA_LARGE_VMALLOC
memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD - 1)*sizeof(pgd_t));
#else
pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]);
#endif
/* The last PGD entry is the VPTB self-map. */
pgd_val(ret[PTRS_PER_PGD-1])
= pte_val(mk_pte(virt_to_page(ret), PAGE_KERNEL));
}
return ret;
}
static inline unsigned long
load_PCB(struct pcb_struct *pcb)
{
register unsigned long sp __asm__("$30");
pcb->ksp = sp;
return __reload_thread(pcb);
}
/* Set up initial PCB, VPTB, and other such nicities. */
static inline void
switch_to_system_map(void)
{
unsigned long newptbr;
unsigned long original_pcb_ptr;
/* Initialize the kernel's page tables. Linux puts the vptb in
the last slot of the L1 page table. */
memset(swapper_pg_dir, 0, PAGE_SIZE);
newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
pgd_val(swapper_pg_dir[1023]) =
(newptbr << 32) | pgprot_val(PAGE_KERNEL);
/* Set the vptb. This is often done by the bootloader, but
shouldn't be required. */
if (hwrpb->vptb != 0xfffffffe00000000UL) {
wrvptptr(0xfffffffe00000000UL);
hwrpb->vptb = 0xfffffffe00000000UL;
hwrpb_update_checksum(hwrpb);
}
/* Also set up the real kernel PCB while we're at it. */
init_thread_info.pcb.ptbr = newptbr;
init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */
original_pcb_ptr = load_PCB(&init_thread_info.pcb);
tbia();
/* Save off the contents of the original PCB so that we can
restore the original console's page tables for a clean reboot.
Note that the PCB is supposed to be a physical address, but
since KSEG values also happen to work, folks get confused.
Check this here. */
if (original_pcb_ptr < PAGE_OFFSET) {
original_pcb_ptr = (unsigned long)
phys_to_virt(original_pcb_ptr);
}
original_pcb = *(struct pcb_struct *) original_pcb_ptr;
}
int callback_init_done;
void * __init
callback_init(void * kernel_end)
{
struct crb_struct * crb;
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
void *two_pages;
/* Starting at the HWRPB, locate the CRB. */
crb = (struct crb_struct *)((char *)hwrpb + hwrpb->crb_offset);
if (alpha_using_srm) {
/* Tell the console whither it is to be remapped. */
if (srm_fixup(VMALLOC_START, (unsigned long)hwrpb))
__halt(); /* "We're boned." --Bender */
/* Edit the procedure descriptors for DISPATCH and FIXUP. */
crb->dispatch_va = (struct procdesc_struct *)
(VMALLOC_START + (unsigned long)crb->dispatch_va
- crb->map[0].va);
crb->fixup_va = (struct procdesc_struct *)
(VMALLOC_START + (unsigned long)crb->fixup_va
- crb->map[0].va);
}
switch_to_system_map();
/* Allocate one PGD and one PMD. In the case of SRM, we'll need
these to actually remap the console. There is an assumption
here that only one of each is needed, and this allows for 8MB.
On systems with larger consoles, additional pages will be
allocated as needed during the mapping process.
In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC,
we need to allocate the PGD we use for vmalloc before we start
forking other tasks. */
two_pages = (void *)
(((unsigned long)kernel_end + ~PAGE_MASK) & PAGE_MASK);
kernel_end = two_pages + 2*PAGE_SIZE;
memset(two_pages, 0, 2*PAGE_SIZE);
pgd = pgd_offset_k(VMALLOC_START);
p4d = p4d_offset(pgd, VMALLOC_START);
pud = pud_offset(p4d, VMALLOC_START);
pud_set(pud, (pmd_t *)two_pages);
pmd = pmd_offset(pud, VMALLOC_START);
pmd_set(pmd, (pte_t *)(two_pages + PAGE_SIZE));
if (alpha_using_srm) {
static struct vm_struct console_remap_vm;
unsigned long nr_pages = 0;
unsigned long vaddr;
unsigned long i, j;
/* calculate needed size */
for (i = 0; i < crb->map_entries; ++i)
nr_pages += crb->map[i].count;
/* register the vm area */
console_remap_vm.flags = VM_ALLOC;
console_remap_vm.size = nr_pages << PAGE_SHIFT;
vm_area_register_early(&console_remap_vm, PAGE_SIZE);
vaddr = (unsigned long)console_remap_vm.addr;
/* Set up the third level PTEs and update the virtual
addresses of the CRB entries. */
for (i = 0; i < crb->map_entries; ++i) {
unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT;
crb->map[i].va = vaddr;
for (j = 0; j < crb->map[i].count; ++j) {
/* Newer consoles (especially on larger
systems) may require more pages of
PTEs. Grab additional pages as needed. */
if (pmd != pmd_offset(pud, vaddr)) {
memset(kernel_end, 0, PAGE_SIZE);
pmd = pmd_offset(pud, vaddr);
pmd_set(pmd, (pte_t *)kernel_end);
kernel_end += PAGE_SIZE;
}
set_pte(pte_offset_kernel(pmd, vaddr),
pfn_pte(pfn, PAGE_KERNEL));
pfn++;
vaddr += PAGE_SIZE;
}
}
}
callback_init_done = 1;
return kernel_end;
}
void __init arch_zone_limits_init(unsigned long *max_zone_pfn)
{
unsigned long dma_pfn;
dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
max_pfn = max_low_pfn;
max_zone_pfn[ZONE_DMA] = dma_pfn;
max_zone_pfn[ZONE_NORMAL] = max_pfn;
}
/*
* paging_init() initializes the kernel's ZERO_PGE.
*/
void __init paging_init(void)
{
memset(absolute_pointer(ZERO_PGE), 0, PAGE_SIZE);
}
#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM)
void
srm_paging_stop (void)
{
/* Move the vptb back to where the SRM console expects it. */
swapper_pg_dir[1] = swapper_pg_dir[1023];
tbia();
wrvptptr(0x200000000UL);
hwrpb->vptb = 0x200000000UL;
hwrpb_update_checksum(hwrpb);
/* Reload the page tables that the console had in use. */
load_PCB(&original_pcb);
tbia();
}
#endif
static const pgprot_t protection_map[16] = {
[VM_NONE] = _PAGE_P(_PAGE_FOE | _PAGE_FOW |
_PAGE_FOR),
[VM_READ] = _PAGE_P(_PAGE_FOE | _PAGE_FOW),
[VM_WRITE] = _PAGE_P(_PAGE_FOE),
[VM_WRITE | VM_READ] = _PAGE_P(_PAGE_FOE),
[VM_EXEC] = _PAGE_P(_PAGE_FOW | _PAGE_FOR),
[VM_EXEC | VM_READ] = _PAGE_P(_PAGE_FOW),
[VM_EXEC | VM_WRITE] = _PAGE_P(0),
[VM_EXEC | VM_WRITE | VM_READ] = _PAGE_P(0),
[VM_SHARED] = _PAGE_S(_PAGE_FOE | _PAGE_FOW |
_PAGE_FOR),
[VM_SHARED | VM_READ] = _PAGE_S(_PAGE_FOE | _PAGE_FOW),
[VM_SHARED | VM_WRITE] = _PAGE_S(_PAGE_FOE),
[VM_SHARED | VM_WRITE | VM_READ] = _PAGE_S(_PAGE_FOE),
[VM_SHARED | VM_EXEC] = _PAGE_S(_PAGE_FOW | _PAGE_FOR),
[VM_SHARED | VM_EXEC | VM_READ] = _PAGE_S(_PAGE_FOW),
[VM_SHARED | VM_EXEC | VM_WRITE] = _PAGE_S(0),
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = _PAGE_S(0)
};
DECLARE_VM_GET_PAGE_PROT
+112
View File
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Alpha TLB shootdown helpers
*
* Copyright (C) 2025 Magnus Lindholm <linmag7@gmail.com>
*
* Alpha-specific TLB flush helpers that cannot be expressed purely
* as inline functions.
*
* These helpers provide combined MM context handling (ASN rollover)
* and immediate TLB invalidation for page migration and memory
* compaction paths, where lazy shootdowns are insufficient.
*/
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <asm/tlbflush.h>
#include <asm/pal.h>
#include <asm/mmu_context.h>
#define asn_locked() (cpu_data[smp_processor_id()].asn_lock)
/*
* Migration/compaction helper: combine mm context (ASN) handling with an
* immediate per-page TLB invalidate and (for exec) an instruction barrier.
*
* This mirrors the SMP combined IPI handler semantics, but runs locally on UP.
*/
#ifndef CONFIG_SMP
void migrate_flush_tlb_page(struct vm_area_struct *vma,
unsigned long addr)
{
struct mm_struct *mm = vma->vm_mm;
int tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2;
/*
* First do the mm-context side:
* If we're currently running this mm, reload a fresh context ASN.
* Otherwise, mark context invalid.
*
* On UP, this is mostly about matching the SMP semantics and ensuring
* exec/i-cache tagging assumptions hold when compaction migrates pages.
*/
if (mm == current->active_mm)
flush_tlb_current(mm);
else
flush_tlb_other(mm);
/*
* Then do the immediate translation kill for this VA.
* For exec mappings, order instruction fetch after invalidation.
*/
tbi(tbi_type, addr);
}
#else
struct tlb_mm_and_addr {
struct mm_struct *mm;
unsigned long addr;
int tbi_type; /* 2 = DTB, 3 = ITB+DTB */
};
static void ipi_flush_mm_and_page(void *x)
{
struct tlb_mm_and_addr *d = x;
/* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */
if (d->mm == current->active_mm && !asn_locked())
__load_new_mm_context(d->mm);
else
flush_tlb_other(d->mm);
/* Part 2: immediate per-VA invalidation on this CPU. */
tbi(d->tbi_type, d->addr);
}
void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
struct mm_struct *mm = vma->vm_mm;
struct tlb_mm_and_addr d = {
.mm = mm,
.addr = addr,
.tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2,
};
/*
* One synchronous rendezvous: every CPU runs ipi_flush_mm_and_page().
* This is the "combined" version of flush_tlb_mm + per-page invalidate.
*/
preempt_disable();
on_each_cpu(ipi_flush_mm_and_page, &d, 1);
/*
* mimic flush_tlb_mm()'s mm_users<=1 optimization.
*/
if (atomic_read(&mm->mm_users) <= 1) {
int cpu, this_cpu;
this_cpu = smp_processor_id();
for (cpu = 0; cpu < NR_CPUS; cpu++) {
if (!cpu_online(cpu) || cpu == this_cpu)
continue;
if (READ_ONCE(mm->context[cpu]))
WRITE_ONCE(mm->context[cpu], 0);
}
}
preempt_enable();
}
#endif