11

问题

我正在为 ARM Cortex-M3 处理器开发自定义操作系统。要与我的内核交互,用户线程必须生成 SuperVisor Call (SVC) 指令(以前称为 SWI,用于软件中断)。该指令在ARM ARM中的定义是:

在此处输入图像描述

这意味着该指令需要立即参数,而不是寄存器值。

这让我很难以可读的方式构建我的界面。它需要如下代码:

asm volatile( "svc #0");

当我更喜欢类似的东西时

svc(SVC_YIELD);

但是,我无法构造这个函数,因为 SVC 指令需要一个立即参数,而当值通过寄存器传入时,我无法提供该参数。

内核:

作为后台,svc指令在内核中解码如下

#define SVC_YIELD   0
// Other SVC codes

// Called by the SVC interrupt handler (not shown)
void handleSVC(char code)
{
  switch (code) {

    case SVC_YIELD:
      svc_yield();
      break;
    // Other cases follow

这个案例陈述正在迅速失控,但我认为没有办法解决这个问题。欢迎任何建议。

我试过的

带有寄存器参数的 SVC

我最初认为

__attribute__((naked)) svc(char code)
{
    asm volatile ("scv r0"); 
}

但这当然不起作用,因为 SVC 需要一个寄存器参数。

蛮力

解决问题的蛮力尝试如下所示:

void svc(char code)
  switch (code) {
    case 0:
      asm volatile("svc #0");
      break;
    case 1:
      asm volatile("svc #1");
      break;
    /* 253 cases omitted */
    case 255:
      asm volatile("svc #255");
      break;
  }
}

但这有一种令人讨厌的代码气味。当然,这可以做得更好。

动态生成指令编码

最后一次尝试是在 RAM 中生成指令(其余代码从只读闪存运行)然后运行它:

void svc(char code)
{
  asm volatile (
      "orr r0, 0xDF00  \n\t" // Bitwise-OR the code with the SVC encoding
      "push {r1, r0}   \n\t" // Store the instruction to RAM (on the stack)
      "mov r0, sp      \n\t" // Copy the stack pointer to an ordinary register
      "add r0, #1      \n\t" // Add 1 to the address to specify THUMB mode
      "bx r0           \n\t" // Branch to newly created instruction
      "pop {r1, r0}    \n\t" // Restore the stack
      "bx lr           \n\t" // Return to caller
      );
}

但这也感觉不对。另外,它不起作用-我在这里做错了;也许我的指令没有正确对齐,或者我没有设置处理器以允许在这个位置从 RAM 运行代码。

我该怎么办?

我必须处理最后一个选项。但是,我仍然觉得我应该能够做类似的事情:

__attribute__((naked)) svc(char code)
{
    asm volatile ("scv %1"
         : /* No outputs */
         : "i" (code)    // Imaginary directive specifying an immediate argument
                         // as opposed to conventional "r"
          ); 
}

但我在文档中没有找到任何这样的选项,我无法解释如何实现这样的功能,所以它可能不存在。我该怎么做?

4

5 回答 5

24

您想使用约束来强制将操作数分配为 8 位立即数。对于 ARM,这就是约束I。所以你要

#define SVC(code) asm volatile ("svc %0" : : "I" (code) )

有关所有约束的摘要,请参阅 GCC 文档——您需要查看特定于处理器的说明以查看特定平台的约束。在某些情况下,您可能需要查看.mdgcc 源中架构的(机器描述)文件以获取完整信息。

这里还有一些很好的 ARM 特定的 gcc 文档。在“输入和输出操作数”标题下的几页下,它提供了所有 ARM 约束的表

于 2012-07-07T20:42:15.807 回答
2

使用宏怎么样:

#define SVC(i)  asm volatile("svc #"#i)
于 2012-07-07T18:15:49.433 回答
2

正如 Chris Dodd 在宏的评论中所指出的那样,它并不完全有效,但这确实:

#define STRINGIFY0(v) #v
#define STRINGIFY(v) STRINGIFY0(v)
#define SVC(i)  asm volatile("svc #" STRINGIFY(i))

但是请注意,如果您将枚举值传递给它,它将不起作用,只有一个 #defined 。

因此,克里斯上面的答案是最好的,因为它使用立即值,这是至少对于拇指指令所必需的。

于 2014-06-06T10:53:20.430 回答
1

我的解决方案(“动态生成指令编码”):

#define INSTR_CODE_SVC      (0xDF00)
#define INSTR_CODE_BX_LR    (0x4770)

void svc_call(uint32_t svc_num)
{
    uint16_t instrs[2];

    instrs[0] = (uint16_t)(INSTR_CODE_SVC | svc_num);
    instrs[1] = (uint16_t)(INSTR_CODE_BX_LR);

    // PC = instrs (or 1 -> thumb mode)
    ((void(*)(void))((uint32_t)instrs | 1))();
}

它的工作原理比 switch-case 变体要好得多,后者需要 ~2kb ROM 来支持 256 个 svc。这个函数不必放在RAM部分,FLASH就可以了。如果 svc_num 应该是运行时变量,则可以使用它。

于 2018-03-09T06:59:15.200 回答
0

正如在这个问题中所讨论的, 的操作数SVC是固定的,即预处理器应该知道它,它与立即数据处理操作数不同。

gcc 手册中写道

'I' - 在数据处理指令中作为立即操作数有效的整数。即,将 0 到 255 范围内的整数旋转 2 的倍数。

因此,此处使用宏的答案是首选,并且不保证 Chris Dodd 的答案有效,具体取决于 gcc 版本和优化级别。参见另一个问题的讨论。

于 2016-05-17T11:07:49.130 回答