Pull to refresh
149
-4
Max Filippov @jcmvbkbc

low level freak

Send message

Странно говорить о каких-то свойствах языков и не упоминать версии их стандартов. Я уверен, что ни _Bool, ни _Complex ни _Generic из C11 никакая из версий c++ тоже не поддерживает.

Ну камон, это же просто иллюстрация из вики :)

Так я говорю не об иллюстрации, она как раз ок. Я говорю, что текст под ней -- не очень соответствует действительности.

А почему uclinux не глянули? У него футпринт ниже + формат бинарников как раз заточен под загрузку в системы без MMU

nommu ядро я как раз использовал, потому что других вариантов просто нет. А почему bFLT мне не подошёл в той же статье написано (TL;DR: формат неудобный, с сильными встроенными ограничениями, выгоды от его использования нет). Вместо этого я добавил в тулчейн поддержку FDPIC и получил выполнение кода откуда угодно (в т.ч. из флэша прямо из образа файловой системы) и работающую динамическую линковку.

Давайте для общего понимания вкратце разберемся, как происходит загрузка программ в Windows/Linux:

Система создаёт процесс и загружает в память программы секции из ELF/PE. Обычные программы для своей работы используют 3 секции: .text (код), .data (не-инициализированный сегмент памяти для глобальных переменных), .bss (сегмент памяти для инициализированных переменных).

На картинке перед этим абзацем видны program header и сегменты и section header и секции. Program header -- это то, как выглядит ELF-файл с точки зрения загрузчика. Section header -- это то, как выглядит ELF-файл с точки зрения линковщика. Загрузчик оперирует сегментами -- непрерывными областями памяти с одинаковым доступом к ним. Сегмент может содержать одну или больше секций, но загрузчику до этого нет дела.

т. н. динамический линкер, который уже на этапе загрузки программы или библиотеки патчит программу так, чтобы её можно было загрузить в любой участок памяти.

Некоторые программы не рассчитаны на загрузку в произвольный участок памяти, потому что это position-dependent executable. (Т.е. они могут при этом работать, и это, кстати, ваш случай, но только лишь в силу удачного стечения обстоятельств). Некоторые программы содержат код релокации в себе и не зависят от динамического линковщика, например static position-independent executable. (Я об этом писал подробнее здесь, в разделе "форматы исполняемых файлов").

Например, для Tensilica дополнительно вставляется барьер памяти memw перед считыванием переменной (и это можно понять), а также перед записью результата (а вот это понять сложнее)

Это поведение можно отключить добавив опцию компилятора -mno-serialize-volatile

Барьер перед записью в память гарантирует, что запись не будет переупорядочена с другими более ранними чтениями и записями. Вообще это поведение унаследовано из времён, когда о явных барьерах ещё не принято было задумываться и считалось, что volatile -- наше всё. Полагаю, что в некоторых культурах программирования это до сих пор верно. При компиляции ядра linux это поведение отключено и явные барьеры должны быть в правильных местах.

ISA ядер Extensa — официально под NDA

Официальная документация на ISA доступна на сайте Cadence, без регистрации и смс: https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf

Лучше так:

Ещё лучше так:

При этом производительность ПО запущенного в эмуляторе примерно в 10 раз ниже, по сравнению с производительностью при запуске такого ПО на «родной» архитектуре

Потому что "использование возможностей … модуля KVM" и "запускать софт, написанный, например, под ARM, на ПК с x86" -- это два взаимоисключающих сценария.

Фуникция "proc_set_size" устанавливает размер файла в /proc. Мб кто-то сможет объяснить для чего он там?)

Для того, чтобы дать возможность обращаться к файлу из /proc с ненулевого смещения. Например, вам может быть не нужен весь мусор из огромного /proc/kcore, hexdump -Cv -s 1 -n 1 /proc/kcore отработает без проблем, а для /proc/cpuinfo этот фокус не пройдёт. С вашим модулем эта функциональность будет работать неправильно, поскольку ни функция чтения, ни функция записи не используют off/offset как смещение в буфере.

В ESP32 есть MMU

есть MMU, да не тот.

void foo(int *ptr) {
  printf("Before the loop\n");
  for (int i = 0; i < 1000; i++) {
    printf("Before check\n");
    if (i == 17)
      *ptr = 1;
    printf("After check\n");
  }
  printf("After the loop the loop\n");
}

* Условие i == 17 станет истинным (причём только 1 раз) в ходе выполнения данного цикла;

* Поэтому запись единицы в *ptr обязательно произойдёт;

* Чтобы не делать лишнюю проверку в цикле, компилятор имеет право сделать любое из следующих преобразований:

void foo(int *ptr) {
  *ptr = 1;
  printf("Before the loop\n");
  for (int i = 0; i < 1000; i++) {
    printf("Before check\n");
    printf("After check\n");
  }
  printf("After the loop the loop\n");
}

Это какое-то слишком сильное упрощение. Не имея определения функции printf компилятор не может этого сделать, как минимум по двум причинам:

  • у printf может быть внутреннее состояние, которое заставит её сделать нелокальный переход на какой-нибудь итерации цикла в функции foo. Если это случится до 17й итерации, то присваивание *ptr до начала выполнения цикла может изменить видимое поведение программы.

  • printf может обращаться к объекту, на который указывает ptr переданный в foo. Если значение объекта изменится не ровно между двумя вызовами printf на 17й итерации, то это может изменить видимое поведение программы.

Неа. Мой интерес в этом проекте, как можно догадаться из последнего раздела, со стороны xtensa, а не со стороны Espressif. Я полагаю, что в risc-v тусовке найдутся свои энтузиасты.

что можно выключить из qemu-system-arm что бы собиралось пошустрее?

По моим наблюдениям сборка QEMU тормозит когда меняется состав исходников и ninja перестраивает Makefile. Если состав исходников не меняется, то вторая и последующие сборки касаются только изменённых файлов.

Последняя ссылка в статье ( http://wiki.osll.ru/doku.php/etc:users:jcmvbkbc:linux-xtensa:esp32s3 ) как раз ведёт на wiki-страницу с подробными инструкциями по сборке и прошивке.

Сейчас это определённо эксперимент, но эксперимент открытый, дающий возможность продолжить его в интересном лично тебе направлении. А если наберётся критическая масса драйверов, то это можно будет реально использовать.

@livsiusна самом деле это написано в конце комментария, на который вы отвечаете.

Тут может быть не сразу понятно в чем дело, но это приведение 32-битного числа в младших 32 битах rax к 64-битному числу.

Правильнее было бы сказать "знаковое расширение 32-битного числа до 64 битов". Потому что если в eax было отрицательное число, то после сложения с 0xffffffff80000000 старшие биты rax станут нулями из-за переноса из 31-го разряда, а после xor -- опять станут единицами, как и положено отрицательному числу.

Трюк имеет смысл, потому что позволяет (изменением битовой маски загружаемой в rdx) выполнять знаковое расширение исходного числа с произвольным количеством битов, в отличие от инструкций movsx, работающих только с байтами/словами.

В коде sys_get_total_weight нет никакой синхронизации списка задач. Обход списка детей процесса должен быть обрамлён вызовами read_lock(&tasklist_lock) / read_unlock(&tasklist_lock).

Интересно,

процессор Quad core Cortex-A72 (ARM v8)

но ARMv7 на картинке
image
Может с 64-битным ядром и софтом было бы повеселее?
увы на PCIe это полезное начинание было упразднено.

Но адресация-то какая была в стандарте PCI, такая и осталась в PCIe. Не делают такого железа — может быть, но проблема не в PCI шине, а в чём-то другом…
Страшная цифра — 256. Хотя, на первый взгляд, и не очень страшная. Да, именно столько, и есть максимальный предел присутствия независимых PCI устройств, на PCI шине.

Здесь что-то не сходится: даже в Conventional PCI конфиг-спейс рассчитан на 256 шин по 32 устройства на каждой.

Information

Rating
Does not participate
Location
Fremont, California, США
Registered
Activity