После двух перемещений в памяти регистрируется сбой системы - PullRequest
0 голосов
/ 02 октября 2018

Я пытаюсь выполнить некоторые записи в регистрах памяти из пространства пользователя (через пользовательский драйвер).Я хочу написать три 64-битных целых числа, и я инициализировал переменные "value_1, value_2 и value_3" для типа uint64_t.

Я должен использовать инструкцию gcc inline mov и работаю над ARM 64-bitархитектура на заказной версии Linux для встроенной системы.

Это мой код:

  asm ( "MOV %[reg1], %[val1]\t\n"
        "MOV %[reg2], %[val2]\t\n"
        "MOV %[reg3], %[val3]\t\n"

        :[reg1] "=&r" (*register_1),[arg2] "=&r" (*register_2), [arg3] "=&r" (*register_3)
        :[val1] "r"(value_1),[val2] "r" (value_2), [val3] "r" (value_3)
     );

Проблема странная ... Если я выполняю только два MOV, код работает.Если я выполню все три MOV, произойдет сбой всей системы, и мне придется перезагрузить всю систему.

даже незнакомец ... Если я положу "printf" хотя бы на нанососпит с 0 наносекундами между вторым и третьим MOV, код работает!

Я оглянулся, пытаясь найтиРешение и я также использую Clobber памяти:

asm ( "MOV %[reg1], %[val1]\t\n"
      "MOV %[reg2], %[val2]\t\n"
      "MOV %[reg3], %[val3]\t\n"

      :[reg1] "=&r" (*register_1),[arg2] "=&r" (*register_2), [arg3] "=&r" (*register_3)
      :[val1] "r"(value_1),[val2] "r" (value_2), [val3] "r" (value_3)
      :"memory"
   );

... не работает!

Я также использовал макрос барьер памяти между вторым и третьим MOV или вконец трех MOV:

asm volatile("": : :"memory")

.. не работает!

Кроме того, я попытался записать непосредственно в регистр с помощью указателей, и у меня было то же поведение: послево-вторых, напишите сбой системы ...

Кто-нибудь может предложить мне решение .. или скажите, неправильно ли я использую встроенный MOV gcc или барьер памяти?

----> БОЛЬШЕ ДЕТАЛЕЙ <----- </p>

Это мое основное:

int main() 
{ 
    int  dev_fd;
    volatile void * base_addr = NULL;
    volatile uint64_t * reg1_addr = NULL;

    volatile uint32_t * reg2_addr = NULL;

    volatile uint32_t * reg3_addr = NULL;

    dev_fd = open(MY_DEVICE, O_RDWR);
    if (dev_fd < 0)
    {
            perror("Open call failed");
            return -1;
    }

    base_addr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, xsmll_dev_fd, 0);
    if (base_addr == MAP_FAILED)
    {
            perror("mmap operation failed");
            return -1;
    }
    printf("BASE ADDRESS VIRT: 0x%p\n", base_addr);

    /* Preparing the registers */
    reg1_addr = base_addr + REG1_OFF;
    reg2_addr = base_addr + REG2_OFF;
    reg3_addr = base_addr + REG3_OFF;

     uint64_t val_1 = 0xEEEEEEEE;
    uint64_t val_2 = 0x00030010;
    uint64_t val_3 = 0x01;

    asm ( "str %[val1], %[reg1]\t\n"
          "str %[val2], %[reg2]\t\n"
          "str %[val3], %[reg3]\t\n"

      :[reg1] "=&m" (*reg1_addr),[reg2] "=&m" (*reg2_addr), [reg3] "=&m" (*reg3_addr)
      :[val1] "r"(val_1),[val2] "r" (val_2), [val3] "r" (val_3)
      );

    printf("--- END ---\n");
    close(dev_fd);
    return 0;
  }

Это вывод компилятора относительно оператора asm(linaro..я кросс-компиляция):

  400bfc:       f90013a0        str     x0, [x29,#32]
  400c00:       f94027a3        ldr     x3, [x29,#72]
  400c04:       f94023a4        ldr     x4, [x29,#64]
  400c08:       f9402ba5        ldr     x5, [x29,#80]
  400c0c:       f9401ba0        ldr     x0, [x29,#48]
  400c10:       f94017a1        ldr     x1, [x29,#40]
  400c14:       f94013a2        ldr     x2, [x29,#32]
  400c18:       f9000060        str     x0, [x3]
  400c1c:       f9000081        str     x1, [x4]
  400c20:       f90000a2        str     x2, [x5]

Спасибо!

1 Ответ

0 голосов
/ 02 октября 2018

Я пытался с * reg1_addr = val_1;и у меня та же проблема.

Тогда этот код не проблема.Избегание asm - это просто более чистый способ получения эквивалентного машинного кода без использования встроенного ассемблера.Ваша проблема, скорее всего, заключается в том, что вы выбрали регистры и значения или драйвер ядра.

Или вам нужно, чтобы значения были в регистрах ЦП перед записью первого местоположения mmaped, чтобы избежать загрузки чего-либо из стека между хранилищами?Это единственная причина, по которой я могу подумать, что вам понадобится встроенный asm, где сгенерированные компилятором хранилища могут быть не эквивалентны.


Ответ на оригинальный вопрос :

Выходное ограничение "=&r" означает регистр CPU .Таким образом, ваши инструкции inline-asm будут выполняться в таком порядке, собираясь во что-то вроде

mov x0, x5
mov x1, x6
mov x2, x7

И после этого сгенерированный компилятором код будет сохранять значения обратно в память в некотором неопределенном порядке.Этот порядок зависит от способа генерации кода для окружающего C. Вероятно, поэтому изменение окружающего кода меняет поведение.

Одним из решений может быть ограничение "=&m" с инструкциями str, поэтому ваш ассемблер на самом деле сохраняет в памяти.str %[val1], %[reg1] потому что инструкции STR принимают режим адресации как 2-й операнд, хотя это и место назначения.


Почему вы не можете использовать volatile uint64_t* = register_1;, как обычный человек, для хранения хранилища сообщений компилятораИнструкции, которые нельзя изменить или оптимизировать?MMIO - это именно то, для чего volatile предназначен.

Разве в Linux нет макросов или функций для загрузки / сохранения MMIO?


Если у вас возникли проблемы с встроенным ассемблером,Шаг 1 в отладке должен состоять в том, чтобы посмотреть на фактическую asm, генерируемую компилятором, когда он заполнил шаблон asm, и окружающий код.

Затем пошаговые инструкции по коду (с GDB stepi, может быть, в режиме layout reg.

...