^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * linux/arch/arm/kernel/process.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 1996-2000 Russell King - Converted to ARM.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Original Copyright (C) 1995 Linus Torvalds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <stdarg.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/export.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/sched/debug.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/sched/task.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/sched/task_stack.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/stddef.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/user.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/elfcore.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/tick.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/utsname.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/random.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/hw_breakpoint.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <asm/processor.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <asm/thread_notify.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <asm/stacktrace.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #include <asm/system_misc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #include <asm/mach/time.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #include <asm/tls.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #include <asm/vdso.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #include "signal.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include <linux/stackprotector.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) unsigned long __stack_chk_guard __read_mostly;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) EXPORT_SYMBOL(__stack_chk_guard);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static const char *processor_modes[] __maybe_unused = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "MON_32" , "ABT_32" ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) "UK8_32" , "UK9_32" , "HYP_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static const char *isa_modes[] __maybe_unused = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) "ARM" , "Thumb" , "Jazelle", "ThumbEE"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * This is our default idle handler.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) void (*arm_pm_idle)(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * Called from the core idle loop.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) void arch_cpu_idle(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) if (arm_pm_idle)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) arm_pm_idle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) cpu_do_idle();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) raw_local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) void arch_cpu_idle_prepare(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) local_fiq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) void arch_cpu_idle_enter(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) idle_notifier_call_chain(IDLE_START);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) ledtrig_cpu(CPU_LED_IDLE_START);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) #ifdef CONFIG_PL310_ERRATA_769419
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) wmb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) void arch_cpu_idle_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) ledtrig_cpu(CPU_LED_IDLE_END);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) idle_notifier_call_chain(IDLE_END);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) void __show_regs(struct pt_regs *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) char buf[64];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) #ifndef CONFIG_CPU_V7M
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) unsigned int domain, fs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) #ifdef CONFIG_CPU_SW_DOMAIN_PAN
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) * Get the domain register for the parent context. In user
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) * mode, we don't save the DACR, so lets use what it should
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) * be. For other modes, we place it after the pt_regs struct.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (user_mode(regs)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) domain = DACR_UACCESS_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) fs = get_fs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) domain = to_svc_pt_regs(regs)->dacr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) fs = to_svc_pt_regs(regs)->addr_limit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) domain = get_domain();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) fs = get_fs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) show_regs_print_info(KERN_DEFAULT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) printk("PC is at %pS\n", (void *)instruction_pointer(regs));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) printk("LR is at %pS\n", (void *)regs->ARM_lr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) printk("sp : %08lx ip : %08lx fp : %08lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) regs->ARM_r10, regs->ARM_r9,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) regs->ARM_r8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) regs->ARM_r7, regs->ARM_r6,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) regs->ARM_r5, regs->ARM_r4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) regs->ARM_r3, regs->ARM_r2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) regs->ARM_r1, regs->ARM_r0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) flags = regs->ARM_cpsr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) buf[4] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) #ifndef CONFIG_CPU_V7M
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) const char *segment;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) if ((domain & domain_mask(DOMAIN_USER)) ==
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) segment = "none";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) else if (fs == KERNEL_DS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) segment = "kernel";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) segment = "user";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) buf, interrupts_enabled(regs) ? "n" : "ff",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) fast_interrupts_enabled(regs) ? "n" : "ff",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) processor_modes[processor_mode(regs)],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) isa_modes[isa_mode(regs)], segment);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) printk("xPSR: %08lx\n", regs->ARM_cpsr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) #ifdef CONFIG_CPU_CP15
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) unsigned int ctrl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) buf[0] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) #ifdef CONFIG_CPU_CP15_MMU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) unsigned int transbase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) asm("mrc p15, 0, %0, c2, c0\n\t"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) : "=r" (transbase));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) transbase, domain);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) printk("Control: %08x%s\n", ctrl, buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) void show_regs(struct pt_regs * regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) __show_regs(regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) dump_stack();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) ATOMIC_NOTIFIER_HEAD(thread_notify_head);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) EXPORT_SYMBOL_GPL(thread_notify_head);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) * Free current thread data structures etc..
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) void exit_thread(struct task_struct *tsk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) thread_notify(THREAD_NOTIFY_EXIT, task_thread_info(tsk));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) void flush_thread(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) struct thread_info *thread = current_thread_info();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) struct task_struct *tsk = current;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) flush_ptrace_hw_breakpoint(tsk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) memset(thread->used_cp, 0, sizeof(thread->used_cp));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) memset(&thread->fpstate, 0, sizeof(union fp_state));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) flush_tls();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) thread_notify(THREAD_NOTIFY_FLUSH, thread);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) void release_thread(struct task_struct *dead_task)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) int copy_thread(unsigned long clone_flags, unsigned long stack_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) unsigned long stk_sz, struct task_struct *p, unsigned long tls)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) struct thread_info *thread = task_thread_info(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) struct pt_regs *childregs = task_pt_regs(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) #ifdef CONFIG_CPU_USE_DOMAINS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) * Copy the initial value of the domain access control register
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) * from the current thread: thread->addr_limit will have been
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) * copied from the current thread via setup_thread_stack() in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) * kernel/fork.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) thread->cpu_domain = get_domain();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) if (likely(!(p->flags & PF_KTHREAD))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) *childregs = *current_pt_regs();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) childregs->ARM_r0 = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) if (stack_start)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) childregs->ARM_sp = stack_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) memset(childregs, 0, sizeof(struct pt_regs));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) thread->cpu_context.r4 = stk_sz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) thread->cpu_context.r5 = stack_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) childregs->ARM_cpsr = SVC_MODE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) thread->cpu_context.pc = (unsigned long)ret_from_fork;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) thread->cpu_context.sp = (unsigned long)childregs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) clear_ptrace_hw_breakpoint(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) if (clone_flags & CLONE_SETTLS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) thread->tp_value[0] = tls;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) thread->tp_value[1] = get_tpuser();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) thread_notify(THREAD_NOTIFY_COPY, thread);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) #ifdef CONFIG_STACKPROTECTOR_PER_TASK
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) thread->stack_canary = p->stack_canary;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) * Fill in the task's elfregs structure for a core dump.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) elf_core_copy_regs(elfregs, task_pt_regs(t));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) unsigned long get_wchan(struct task_struct *p)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) struct stackframe frame;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) unsigned long stack_page;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) int count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) if (!p || p == current || p->state == TASK_RUNNING)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) frame.fp = thread_saved_fp(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) frame.sp = thread_saved_sp(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) frame.lr = 0; /* recovered from the stack */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) frame.pc = thread_saved_pc(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) stack_page = (unsigned long)task_stack_page(p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) if (frame.sp < stack_page ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) frame.sp >= stack_page + THREAD_SIZE ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) unwind_frame(&frame) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) if (!in_sched_functions(frame.pc))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) return frame.pc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) } while (count ++ < 16);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) #ifdef CONFIG_MMU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) #ifdef CONFIG_KUSER_HELPERS
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) * The vectors page is always readable from user space for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) * atomic helpers. Insert it into the gate_vma so that it is visible
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) * through ptrace and /proc/<pid>/mem.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) static struct vm_area_struct gate_vma;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) static int __init gate_vma_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) vma_init(&gate_vma, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) gate_vma.vm_page_prot = PAGE_READONLY_EXEC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) gate_vma.vm_start = 0xffff0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) gate_vma.vm_end = 0xffff0000 + PAGE_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) gate_vma.vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) arch_initcall(gate_vma_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return &gate_vma;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) int in_gate_area(struct mm_struct *mm, unsigned long addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) int in_gate_area_no_mm(unsigned long addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) return in_gate_area(NULL, addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) #define is_gate_vma(vma) ((vma) == &gate_vma)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) #define is_gate_vma(vma) 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) const char *arch_vma_name(struct vm_area_struct *vma)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) return is_gate_vma(vma) ? "[vectors]" : NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) /* If possible, provide a placement hint at a random offset from the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) * stack for the sigpage and vdso pages.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) static unsigned long sigpage_addr(const struct mm_struct *mm,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) unsigned int npages)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) unsigned long offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) unsigned long first;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) unsigned long last;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) unsigned long addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) unsigned int slots;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) first = PAGE_ALIGN(mm->start_stack);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) last = TASK_SIZE - (npages << PAGE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) /* No room after stack? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) if (first > last)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) /* Just enough room? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (first == last)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) return first;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) slots = ((last - first) >> PAGE_SHIFT) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) offset = get_random_int() % slots;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) addr = first + (offset << PAGE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) return addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) static struct page *signal_page;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) extern struct page *get_signal_page(void);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) static int sigpage_mremap(const struct vm_special_mapping *sm,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) struct vm_area_struct *new_vma)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) current->mm->context.sigpage = new_vma->vm_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) static const struct vm_special_mapping sigpage_mapping = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) .name = "[sigpage]",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) .pages = &signal_page,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) .mremap = sigpage_mremap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) struct mm_struct *mm = current->mm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) struct vm_area_struct *vma;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) unsigned long npages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) unsigned long addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) unsigned long hint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if (!signal_page)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) signal_page = get_signal_page();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) if (!signal_page)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) npages = 1; /* for sigpage */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) npages += vdso_total_pages;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) if (mmap_write_lock_killable(mm))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) return -EINTR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) hint = sigpage_addr(mm, npages);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) addr = get_unmapped_area(NULL, hint, npages << PAGE_SHIFT, 0, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) if (IS_ERR_VALUE(addr)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) ret = addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) goto up_fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) vma = _install_special_mapping(mm, addr, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) &sigpage_mapping);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) if (IS_ERR(vma)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) ret = PTR_ERR(vma);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) goto up_fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) mm->context.sigpage = addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) /* Unlike the sigpage, failure to install the vdso is unlikely
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) * to be fatal to the process, so no error check needed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) * here.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) arm_install_vdso(mm, addr + PAGE_SIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) up_fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) mmap_write_unlock(mm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) #endif