Запускается свежая Ubuntu или Arch на ноутбуке, экран мигает, через секунду уже виден логин-менеджер, всё работает. Любопытство берёт верх, рука открывает терминал, набирается dmesg, и среди невинных строчек загорается красным целая россыпь страшных сообщений вроде ACPI BIOS Error (bug): Could not resolve symbol. Внутри неприятно ёкает. Слово Error в логах ядра редко кого радует, особенно когда рядом стоит уточнение "bug". На самом деле в девяноста девяти случаях из ста это ровно та ситуация, когда правильная реакция - закрыть терминал и пойти пить кофе. Разбираем почему.
Что вообще такое ACPI и почему он постоянно генерирует подозрительные сообщения в логах
Чтобы понять природу этих ошибок, придётся на пару минут нырнуть в архитектуру современного ПК. Аббревиатура ACPI расшифровывается как Advanced Configuration and Power Interface, и это стандарт, описывающий, как операционная система общается с железом по поводу питания, температурных датчиков, портов, кнопок и десятков других мелочей. Без него ноутбук не знал бы, как уйти в сон при закрытии крышки, а процессор не понижал бы частоту в простое.
Самое интересное, что ACPI не просто набор регистров. Производитель материнской платы пишет внутри BIOS целые программы на специальном языке ASL (ACPI Source Language), компилирует их в байт-код AML (ACPI Machine Language) и кладёт в специальные таблицы. Главная такая таблица называется DSDT, рядом с ней лежат дополнительные SSDT. Когда Linux загружается, ядро запускает встроенный интерпретатор и буквально исполняет этот байт-код, обращаясь к функциям BIOS как к API.
Извлечь и посмотреть эти таблицы можно прямо на работающей системе:
# Установка инструментов
sudo apt install acpica-tools
# Создаём рабочую папку и сохраняем все таблицы
mkdir ~/acpi-dump && cd ~/acpi-dump
sudo acpidump -b
# Дизассемблируем DSDT в читаемый вид
iasl -d dsdt.dat
# Открываем результат в любом редакторе
less dsdt.dsl
Внутри dsdt.dsl лежит код, написанный для конкретной материнской платы. И именно там обычно прячутся причины тех самых ошибок, которые ядро затем добросовестно протоколирует.
Почему ядро Linux пишет про bug в чужом коде и насколько это сообщение точное
Формулировка вполне честная. Когда интерпретатор ядра обнаруживает, что AML-код обращается к несуществующему символу или нарушает спецификацию ACPI, он именно так и пишет. По смыслу это сообщение в адрес производителя железа, а не пользователя. На форумах эту мысль отлично сформулировал один из старожилов Linux-сообщества, заметив, что подобные ошибки представляют собой проблемы, которые производители не решили из-за неполной реализации протоколов ACPI, и ядро лишь указывает на места, где спецификация нарушена.
Типичный кусок такого сообщения выглядит примерно так:
[ 0.226451] ACPI BIOS Error (bug): Could not resolve symbol [\_SB.UBTC.RUCC],
AE_NOT_FOUND (20211217/psargs-330)
[ 0.226461] ACPI Error: Aborting method \_SB.PC00.XHCI.RHUB.HS03._PLD
due to previous error (AE_NOT_FOUND) (20211217/psparse-529)
Что здесь написано простыми словами. Метод _PLD (Physical Location of Device) для одного из USB-портов попытался обратиться к объекту _SB.UBTC.RUCC, которого в таблицах нет. Метод _PLD отвечает за описание физического расположения USB-порта, информация эта используется крайне редко, в основном для красивого отображения в утилитах вроде GNOME Settings. Если объект не найден, метод просто отменяется, и операционная система остаётся без декоративной информации о том, на какой именно стороне корпуса находится конкретный разъём. Никаких реальных функций не теряется.
Похожая картина с ошибками вокруг _CPC, которые встречаются особенно часто на ноутбуках:
[ 1.188193] ACPI BIOS Error (bug): Could not resolve symbol [\_PR.PR00._CPC],
AE_NOT_FOUND (20200925/psargs-330)
[ 1.188201] ACPI Error: Aborting method \_PR.PR01._CPC
due to previous error (AE_NOT_FOUND) (20200925/psparse-529)
Метод _CPC (Collaborative Processor Performance Control) описывает возможности управления частотой процессора. Если он не определён для PR00, но при этом ссылается оттуда из PR01, ядро пишет ошибку и просто игнорирует этот участок. Процессор в результате управляется через стандартный cpufreq, и владелец ноутбука даже не замечает, что что-то пошло не так.
Как отличить безобидное сообщение от настоящей проблемы и не пропустить реальный сбой
Есть простой алгоритм для проверки. Если ноутбук работает нормально, спит и просыпается, заряжается, переключает яркость, а вентилятор крутится в нужный момент, то ошибки в dmesg при загрузке практически наверняка декоративны. Если же что-то отчётливо сломано, например, не работает датчик температуры или клавиша Fn не регулирует громкость, тогда стоит копнуть глубже.
Базовая проверка занимает пару минут:
# Все строки от ACPI с уровнем error
sudo dmesg --level=err | grep -i acpi
# Конкретно сообщения BIOS Error
sudo dmesg | grep "ACPI BIOS Error"
# Убедиться, что критических предупреждений нет
sudo journalctl -p 0..3 -b
Последняя команда показывает только сообщения текущей загрузки с уровнями от emergency до error, отсекая обычный шум. Если там пусто или присутствуют только ACPI-сообщения, то речь о косметической проблеме. Если же там есть упоминания о Hardware Error, MCE, Machine Check Exception или DMA fault, то это уже совсем другая история, и она требует серьёзного внимания.
Полезно также сравнить, что говорит ядро о проблемных подсистемах:
# Состояние термальных зон
cat /sys/class/thermal/thermal_zone*/temp
# Состояние батареи
cat /sys/class/power_supply/BAT*/status
upower -i /org/freedesktop/UPower/devices/battery_BAT0
# Информация о CPU и частотах
cpupower frequency-info
Если все эти команды возвращают разумные числа и значения, то ACPI работает достаточно корректно для повседневного использования, а сообщения в логах остаются всего лишь шумом.
Откуда вообще берутся эти баги в коде материнской платы и при чём тут Microsoft
Самое забавное в природе ACPI-багов то, что Linux попадает в зону их влияния не по своей вине. Стандарт ACPI был спроектирован совместно Intel, Microsoft, Toshiba, HP и Phoenix в конце девяностых, и десятилетиями производители тестировали свои AML-таблицы только под Windows. Если код работал в Windows, на проблемы под другими операционными системами часто закрывали глаза. Linux-разработчики годами обнаруживали, что DSDT-код напичкан условными ветками вроде "если запросчик представился как Windows версии такой-то, идём по одному пути, иначе по другому".
Проверить, насколько глубоко Windows прорастает в DSDT конкретного компьютера, легко:
# После acpidump и iasl -d dsdt.dat
grep -c "Windows" dsdt.dsl
# Посмотреть, какие именно версии упоминаются
grep -E "Windows [0-9]" dsdt.dsl | head -20
В типичной DSDT современного ноутбука встречается несколько десятков вхождений строки Windows с разными годами и версиями. Это означает, что AML-код буквально содержит пути выполнения, специально оптимизированные под определённые версии Windows. Для Linux в большинстве случаев предусмотрен путь по умолчанию, который иногда проходит мимо инициализации некоторых второстепенных объектов. Отсюда и вылезают те самые "Could not resolve symbol".
Существует обходной приём через параметр ядра, который заставляет ядро притвориться той или иной версией Windows:
# Добавить в /etc/default/grub в строку GRUB_CMDLINE_LINUX
acpi_osi="Windows 2020"
# Или использовать только один интерфейс
acpi_osi=! acpi_osi="Windows 2020"
# После правки обязательно пересобрать конфиг
sudo update-grub
Эта опция меняет поведение AML-кода, заставляя его пройти по пути, написанному для современной Windows. Иногда это убирает ошибки, иногда добавляет новые, иногда не меняет ничего. Универсального рецепта нет, и для рядового пользователя возни с этим параметром обычно больше, чем пользы.
Когда сообщения действительно мешают и как их аккуратно скрыть из глаз пользователя
Бывает, что ошибки в dmesg сами по себе не приносят вреда, но мешают замечать настоящие проблемы среди всего этого шума. В таких случаях логичный шаг - не лечить чужой BIOS, а просто фильтровать вывод и приглушить уровень логирования при загрузке.
Самый чистый способ убрать спам ACPI с экрана загрузки лежит через параметры GRUB:
sudo nano /etc/default/grub
Внутри файла находится строка с параметрами ядра по умолчанию:
# Было
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
# Стало
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3"
Параметр loglevel=3 говорит ядру выводить на экран только сообщения уровня error и выше, скрывая info и notice. После сохранения файла нужно пересобрать конфигурацию загрузчика:
sudo update-grub # Debian, Ubuntu, Mint
sudo grub-mkconfig -o /boot/grub/grub.cfg # Arch и большинство других
После перезагрузки экран загрузки станет чище. Сами сообщения никуда не денутся, они просто не будут лезть в глаза. Их по-прежнему можно увидеть командой dmesg, причём с любым уровнем фильтрации:
# Только ошибки
dmesg --level=err
# Только предупреждения
dmesg --level=warn
# Все ACPI-сообщения с цветной подсветкой
dmesg --color=always | grep -i acpi
Для тех, кто хочет полностью отключить отображение error-сообщений конкретно от ACPI, существует хитрый параметр ядра:
# В /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi.debug_layer=0 acpi.debug_level=0"
Это глушит уровень детализации ACPI-подсистемы до минимума. Однако такой подход скорее косметический и не рекомендуется для рабочих машин, потому что заодно скрывает действительно полезные диагностические сообщения, которые могут пригодиться, если что-то реально сломается.
Что делать, если из-за ACPI-багов реально страдает работа железа а не только логи
Иногда ошибки сопровождаются настоящими симптомами. Не работает спящий режим, кулер крутится на полных оборотах независимо от температуры, не определяется аккумулятор, мерцает подсветка клавиатуры. Тогда последовательность действий следующая.
Шаг первый - проверить наличие обновлений BIOS. Производители иногда чинят AML-баги в новых ревизиях прошивки, и одно обновление способно убрать целый каскад ошибок:
# Текущая версия BIOS
sudo dmidecode -s bios-version
sudo dmidecode -s bios-release-date
# Информация о материнской плате
sudo dmidecode -t baseboard
С этими данными можно идти на сайт производителя и сравнивать с актуальной версией. На современных машинах с поддержкой fwupd обновление прошивки делается прямо из терминала:
sudo fwupdmgr refresh
sudo fwupdmgr get-updates
sudo fwupdmgr update
Шаг второй - попробовать другой ядерный параметр acpi_osi, как описано выше. Иногда конкретная строка с именем Windows конкретной версии разблокирует нужный путь в AML-коде.
Шаг третий, для самых упорных - подменить DSDT собственной исправленной версией. Способ существует, описан в документации ядра и работает через initrd:
# Извлекаем текущую DSDT
sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
# Дизассемблируем
iasl -d dsdt.dat
# Правим dsdt.dsl, исправляя проблемные места
nano dsdt.dsl
# Не забываем увеличить OEM-версию в шапке файла
# Иначе ядро не подхватит изменённую таблицу
# Компилируем обратно
iasl -tc dsdt.dsl
# Готовим CPIO-архив для initrd
mkdir -p kernel/firmware/acpi
cp dsdt.aml kernel/firmware/acpi/
find kernel | cpio -H newc --create > acpi_override
# Подкладываем в загрузку
sudo cp acpi_override /boot/
Дальше нужно настроить загрузчик так, чтобы он подгружал acpi_override как initrd до основного. Вариант рабочий, но крайне трудоёмкий. Документация ядра прямо называет этот метод отладочной техникой, не подходящей для боевого использования, потому что после каждого обновления BIOS вся работа по правке таблицы пойдёт насмарку. Производитель такую конфигурацию не поддержит, и дистрибутив тоже. Так что путь подмены DSDT остаётся уделом энтузиастов, которым нужно вернуть к жизни конкретную старую модель ноутбука с особенно безалаберной прошивкой.
Реальная статистика какие ACPI-ошибки встречаются у домашних пользователей чаще всего
По наблюдениям из тысяч обсуждений на форумах вырисовывается узнаваемая картина. Около половины всех жалоб на ACPI BIOS Error связаны с символом _CPC и встречаются на машинах с процессорами Intel десятого поколения и старше, где BIOS описывает CPU-производительность не до конца. Другая большая группа, около четверти случаев - ошибки вокруг USB-портов и метода _PLD, чаще всего на материнских платах с большим количеством разъёмов. Сюда же относятся ошибки про _SB.PCI0.XHC.RHUB и подобные. Остальные четверть приходится на самые разные углы, от термозон и батарей до световых индикаторов и сенсоров крышки.
Проверить свою машину на распространённые шаблоны можно так:
# Подсчёт самых частых незакрытых символов
sudo dmesg | grep "Could not resolve symbol" | \
sed 's/.*\[\\\(.*\)\].*/\1/' | sort | uniq -c | sort -rn
# Какие методы чаще всего прерываются
sudo dmesg | grep "Aborting method" | \
awk '{print $NF}' | sort | uniq -c | sort -rn
Полученная картина обычно показывает, что один и тот же символ упоминается десятки раз, потому что один и тот же кривой кусок AML-кода вызывается из разных мест таблицы. Это типично и не означает катастрофы, просто иллюстрирует, что один-единственный пропущенный объект в DSDT превращается в видимую лавину однотипных строк в логе.
Когда стоит игнорировать а когда всё-таки разбираться, итоговый алгоритм для обычного пользователя
Можно свести всё это к простой логике принятия решения. Открывается терминал, проверяется уровень тревожности:
# Шаг 1. Что говорит загрузка
sudo journalctl -p err -b | grep -v ACPI
# Шаг 2. Работают ли температурные датчики
sensors
# Шаг 3. Работает ли спящий режим
sudo systemctl status systemd-suspend.service
# Шаг 4. Видна ли батарея и адекватен ли её процент
acpi -V
Если первый шаг не выдал ничего страшного помимо ACPI-сообщений, второй показал разумные температуры, третий не ругается, четвёртый видит батарею и её состояние, то жить с этим спамом в dmesg можно совершенно спокойно. Достаточно один раз поправить loglevel в GRUB, чтобы экран загрузки не отвлекал, и забыть про существование DSDT навсегда.
Если же что-то из проверок не прошло, начинается путь обновления BIOS, экспериментов с параметрами acpi_osi и, в самом крайнем случае, ручной подмены AML-таблиц. Но это редкие ситуации, обычно затрагивающие старые ноутбуки нишевых производителей или, наоборот, самые свежие модели, которые ещё не обкатаны под Linux.
Что хочется запомнить про этот класс сообщений и как смотреть на них без лишней тревоги
Главная мысль укладывается в одно предложение. ACPI BIOS Error в логах Linux это в подавляющем большинстве случаев не баг ядра и не неисправность железа, а отчёт ядра о том, что AML-код в прошивке материнской платы написан с отклонениями от спецификации. Linux добросовестно фиксирует каждое такое отклонение, потому что разработчики ядра считают честность важнее тишины. Windows на той же машине просто молча выполняет тот же код, потому что многие из этих отклонений писались специально под её исполнитель.
Внутреннее устройство ACPI с его языком ASL, байт-кодом AML и многотысячными таблицами DSDT выглядит настолько архаично, что в современный год кажется культурным памятником эпохи, когда никто не думал, что эти прошивки переживут двадцать лет и две смены архитектур. Разбираться в этом любопытно один раз, чтобы понимать природу шума в логах. Жить с этим стоит спокойно, фильтруя вывод и обращая внимание только на действительно важные сообщения. Машина продолжает работать, аккумулятор продолжает заряжаться, кулер продолжает вращаться по графику. Всё остальное это семантический шум на пути от инженеров производителя материнской платы до глаз внимательного пользователя Linux.