Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/arch/loongarch/kernel/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  alternative.c

  Sprache: C
 

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/inst.h>
#include <asm/sections.h>

int __read_mostly alternatives_patched;

EXPORT_SYMBOL_GPL(alternatives_patched);

#define MAX_PATCH_SIZE (((u8)(-1)) / LOONGARCH_INSN_SIZE)

static int __initdata_or_module debug_alternative;

static int __init debug_alt(char *str)
{
 debug_alternative = 1;
 return 1;
}
__setup("debug-alternative", debug_alt);

#define DPRINTK(fmt, args...)      \
do {         \
 if (debug_alternative)      \
  printk(KERN_DEBUG "%s: " fmt "\n", __func__, ##args); \
while (0)

#define DUMP_WORDS(buf, count, fmt, args...)    \
do {         \
 if (unlikely(debug_alternative)) {    \
  int _j;       \
  union loongarch_instruction *_buf = buf;  \
         \
  if (!(count))      \
   break;      \
         \
  printk(KERN_DEBUG fmt, ##args);    \
  for (_j = 0; _j < count - 1; _j++)   \
   printk(KERN_CONT "<%08x> ", _buf[_j].word); \
  printk(KERN_CONT "<%08x>\n", _buf[_j].word);  \
 }        \
while (0)

/* Use this to add nops to a buffer, then text_poke the whole buffer. */
static void __init_or_module add_nops(union loongarch_instruction *insn, int count)
{
 while (count--) {
  insn->word = INSN_NOP;
  insn++;
 }
}

/* Is the jump addr in local .altinstructions */
static inline bool in_alt_jump(unsigned long jump, void *start, void *end)
{
 return jump >= (unsigned long)start && jump < (unsigned long)end;
}

static void __init_or_module recompute_jump(union loongarch_instruction *buf,
  union loongarch_instruction *dest, union loongarch_instruction *src,
  void *start, void *end)
{
 unsigned int si, si_l, si_h;
 unsigned long cur_pc, jump_addr, pc;
 long offset;

 cur_pc = (unsigned long)src;
 pc = (unsigned long)dest;

 si_l = src->reg0i26_format.immediate_l;
 si_h = src->reg0i26_format.immediate_h;
 switch (src->reg0i26_format.opcode) {
 case b_op:
 case bl_op:
  jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 27);
  if (in_alt_jump(jump_addr, start, end))
   return;
  offset = jump_addr - pc;
  BUG_ON(offset < -SZ_128M || offset >= SZ_128M);
  offset >>= 2;
  buf->reg0i26_format.immediate_h = offset >> 16;
  buf->reg0i26_format.immediate_l = offset;
  return;
 }

 si_l = src->reg1i21_format.immediate_l;
 si_h = src->reg1i21_format.immediate_h;
 switch (src->reg1i21_format.opcode) {
 case bceqz_op: /* bceqz_op = bcnez_op */
  BUG_ON(buf->reg1i21_format.rj & BIT(4));
  fallthrough;
 case beqz_op:
 case bnez_op:
  jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
  if (in_alt_jump(jump_addr, start, end))
   return;
  offset = jump_addr - pc;
  BUG_ON(offset < -SZ_4M || offset >= SZ_4M);
  offset >>= 2;
  buf->reg1i21_format.immediate_h = offset >> 16;
  buf->reg1i21_format.immediate_l = offset;
  return;
 }

 si = src->reg2i16_format.immediate;
 switch (src->reg2i16_format.opcode) {
 case beq_op:
 case bne_op:
 case blt_op:
 case bge_op:
 case bltu_op:
 case bgeu_op:
  jump_addr = cur_pc + sign_extend64(si << 2, 17);
  if (in_alt_jump(jump_addr, start, end))
   return;
  offset = jump_addr - pc;
  BUG_ON(offset < -SZ_128K || offset >= SZ_128K);
  offset >>= 2;
  buf->reg2i16_format.immediate = offset;
  return;
 }
}

static int __init_or_module copy_alt_insns(union loongarch_instruction *buf,
 union loongarch_instruction *dest, union loongarch_instruction *src, int nr)
{
 int i;

 for (i = 0; i < nr; i++) {
  buf[i].word = src[i].word;

  if (is_pc_ins(&src[i])) {
   pr_err("Not support pcrel instruction at present!");
   return -EINVAL;
  }

  if (is_branch_ins(&src[i]) &&
      src[i].reg2i16_format.opcode != jirl_op) {
   recompute_jump(&buf[i], &dest[i], &src[i], src, src + nr);
  }
 }

 return 0;
}

/*
 * text_poke_early - Update instructions on a live kernel at boot time
 *
 * When you use this code to patch more than one byte of an instruction
 * you need to make sure that other CPUs cannot execute this code in parallel.
 * Also no thread must be currently preempted in the middle of these
 * instructions. And on the local CPU you need to be protected again NMI or MCE
 * handlers seeing an inconsistent instruction while you patch.
 */

static void *__init_or_module text_poke_early(union loongarch_instruction *insn,
         union loongarch_instruction *buf, unsigned int nr)
{
 int i;
 unsigned long flags;

 local_irq_save(flags);

 for (i = 0; i < nr; i++)
  insn[i].word = buf[i].word;

 local_irq_restore(flags);

 wbflush();
 flush_icache_range((unsigned long)insn, (unsigned long)(insn + nr));

 return insn;
}

/*
 * Replace instructions with better alternatives for this CPU type. This runs
 * before SMP is initialized to avoid SMP problems with self modifying code.
 * This implies that asymmetric systems where APs have less capabilities than
 * the boot processor are not handled. Tough. Make sure you disable such
 * features by hand.
 */

void __init_or_module apply_alternatives(struct alt_instr *start, struct alt_instr *end)
{
 struct alt_instr *a;
 unsigned int nr_instr, nr_repl, nr_insnbuf;
 union loongarch_instruction *instr, *replacement;
 union loongarch_instruction insnbuf[MAX_PATCH_SIZE];

 DPRINTK("alt table %px, -> %px", start, end);
 /*
 * The scan order should be from start to end. A later scanned
 * alternative code can overwrite previously scanned alternative code.
 * Some kernel functions (e.g. memcpy, memset, etc) use this order to
 * patch code.
 *
 * So be careful if you want to change the scan order to any other
 * order.
 */

 for (a = start; a < end; a++) {
  nr_insnbuf = 0;

  instr = (void *)&a->instr_offset + a->instr_offset;
  replacement = (void *)&a->replace_offset + a->replace_offset;

  BUG_ON(a->instrlen > sizeof(insnbuf));
  BUG_ON(a->instrlen & 0x3);
  BUG_ON(a->replacementlen & 0x3);

  nr_instr = a->instrlen / LOONGARCH_INSN_SIZE;
  nr_repl = a->replacementlen / LOONGARCH_INSN_SIZE;

  if (!cpu_has(a->feature)) {
   DPRINTK("feat not exist: %d, old: (%px len: %d), repl: (%px, len: %d)",
    a->feature, instr, a->instrlen,
    replacement, a->replacementlen);

   continue;
  }

  DPRINTK("feat: %d, old: (%px len: %d), repl: (%px, len: %d)",
   a->feature, instr, a->instrlen,
   replacement, a->replacementlen);

  DUMP_WORDS(instr, nr_instr, "%px: old_insn: ", instr);
  DUMP_WORDS(replacement, nr_repl, "%px: rpl_insn: ", replacement);

  copy_alt_insns(insnbuf, instr, replacement, nr_repl);
  nr_insnbuf = nr_repl;

  if (nr_instr > nr_repl) {
   add_nops(insnbuf + nr_repl, nr_instr - nr_repl);
   nr_insnbuf += nr_instr - nr_repl;
  }
  DUMP_WORDS(insnbuf, nr_insnbuf, "%px: final_insn: ", instr);

  text_poke_early(instr, insnbuf, nr_insnbuf);
 }
}

void __init alternative_instructions(void)
{
 apply_alternatives(__alt_instructions, __alt_instructions_end);

 alternatives_patched = 1;
}

Messung V0.5 in Prozent
C=94 H=89 G=91

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-04-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.