SantyagoSantyago
Avatar

Witaj!
Blog archiwalny. Już niebawem nowy serwis!

YouTube RSS Facebook GitHub

W poprzednim tygodniu opisywałem świetną płytkę NodeMCU v2 opartą na układzie ESP8266. Oprócz podstawowych zagadnień, pokazywałem również jak wgrać nowy firmware w postaci binarnej jaki przygotowali twórcy. Najnowsza dostępna wersja w dniu tego wpisu to NodeMCU 0.9.6 datowana na 2015/02/16. Możemy jednak wgrać nowszy build, a nawet skorzystać z rozwojowego drzewa dev120.

Wszystko czego będziemy potrzebowali to Linuksa, trochę czasu i odrobinę miejsca na dysku twardym (około 4GB)

ESP Open SDK

W pierwszej kolejności sklonujmy repozytorium ESP Open SDK i przygotujmy zestaw odpowiednich narzędzi:

  1. cd ~
  2. mkdir nodemcu
  3. cd nodemcu/
  4. git clone https://github.com/pfalcon/esp-open-sdk.git
  5. cd esp-open-sdk/
  6. make STANDALONE=y

W zależności od posiadanego sprzętu i prędkości sieci, proces ten może zając od kilku do kilkunastu minut. Po tym czasie powinien wyświetlić się komunikat o pomyślnym wykonaniu zadania z prośbą o modyfikację zmiennej środowiskowej PATH. W moim przypadku będzie to:

  1. export PATH=/home/korneliusz/nodemcu/esp-open-sdk/xtensa-lx106-elf/bin:$PATH

Sprawdźmy jeszcze, czy wszystko jest poprawnie, wydając poniższe polecenie:

  1. xtensa-lx106-elf-gcc -v

Using built-in specs.
COLLECT_GCC=xtensa-lx106-elf-gcc
COLLECT_LTO_WRAPPER=/home/korneliusz/nodemcu/esp-open-sdk/xtensa-lx106-elf/libexec/gcc/xtensa-lx106-elf/4.8.2/lto-wrapper
Target: xtensa-lx106-elf
Configured with: /home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/src/gcc-4.8.2/configure --build=x86_64-build_unknown-linux-gnu --host=x86_64-build_unknown-linux-gnu --target=xtensa-lx106-elf --prefix=/home/korneliusz/nodemcu/esp-open-sdk/xtensa-lx106-elf --with-local-prefix=/home/korneliusz/nodemcu/esp-open-sdk/xtensa-lx106-elf/xtensa-lx106-elf/sysroot --disable-libmudflap --with-sysroot=/home/korneliusz/nodemcu/esp-open-sdk/xtensa-lx106-elf/xtensa-lx106-elf/sysroot --with-newlib --enable-threads=no --disable-shared --with-pkgversion='crosstool-NG 1.20.0' --disable-__cxa_atexit --with-gmp=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --with-mpfr=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --with-mpc=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --with-isl=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --with-cloog=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --with-libelf=/home/korneliusz/nodemcu/esp-open-sdk/crosstool-NG/.build/xtensa-lx106-elf/buildtools --enable-lto --enable-target-optspace --disable-libgomp --disable-libmudflap --disable-nls --disable-multilib --enable-languages=c,c++
Thread model: single
gcc version 4.8.2 (crosstool-NG 1.20.0):

Możemy przystąpić do budowania firmware

Ponownie klonujemy repozytorium - tym razem zawierające firmware:

  1. cd ~/nodemcu/
  2. git clone https://github.com/nodemcu/nodemcu-firmware.git
  3. cd nodemcu-firmware.git/

Na tym etapie musimy zdecydować, czy interesuje nas gałąź master (obecnie z wersją 0.9.6), czy może chcemy spróbować deweloperskiej wersji 1.2.0. Jeśli tak, wybieramy gałąź dev120. Jeśli nie, pomijamy poniższe polecenie:

  1. git checkout dev120

Na koniec nie pozostaje nam już nic innego, jak wydanie polecenia kompilacji:

  1. make

Kiedy kompilacja dobiegnie końca, możemy przygotować nasz firmware i wgrać go NodeMCU v2 (oczywiście podłączająć go do USB):

  1. tools/esptool.py elf2image app/.output/eagle/debug/image/eagle.app.v6.out
  1. tools/esptool.py write_flash -fm dio -fs 32m -ff 40m 0x00000 app/.output/eagle/debug/image/eagle.app.v6.out-0x00000.bin 0x10000 app/.output/eagle/debug/image/eagle.app.v6.out-0x10000.bin

I praktycznie tyle. Sprawdźmy jeszcze za pomocą minicoma, czy rezultat jest zgodny z oczekiwaniami:

  1. minicom -D /dev/ttyUSB0 -b 9600

Po restarcie powinniśmy ujrzeć świeży firmware:

Custom firmware

Przed kompilacją i wgraniem, możemy zmodyfikować konfigurację naszego firmwareu edytując plik app/include/user_modules.h.

Właśnie tutaj zdecydujemy o dostępności poszczególnych modułów, oszczędzając tym samym cenne kilobajty.

Reklama

Jakiś czas temu stałem się posiadaczem karty AVerMedia Live Gamer HD Lite do przechwytywania sygnału HDMI w rozdzielczości FullHD z prędkością 30fps. Głównie mam ją zamiar wykorzystywać do nagrywania materiałów z mini komputerów z układami SoC (Odroid/Marsboard/Cubieboard itp.), gdzie nagrywanie obrazu bezpośrednio z uruchomionego systemu jest praktycznie niemożliwe.

Na upartego problem mnie nie dotyczył, ponieważ zawsze mogę przełączyć się na Windowsa i tam nagrać materiał bezpośrednio z karty graficznej czy dowolnego urządzenia, ale problem pojawił się, gdy chciałem przechwycić w miarę płynny film z pulpitu Linuksa pod większym obciążeniem. Dodatkowo, każdy kto próbował nagrać rozgrywkę gry wykorzystującej OpenGL i tryb pełnoekranowy wie, że to droga przez mękę, która w większości przypadków kończy się niepowodzeniem lub fatalną ilością klatek na sekundę. Gdyby AVerMedia Live Gamer HD / Lite był obsługiwany pod Linuksem, nie było by w ogóle tematu, a tak trzeba sobie jakoś radzić na około. Z pomocą przychodzi nam wirtualizacja.

Wymagania

Do tej sztuczki będzie nam potrzebny procesor z obsługą AMD-VI/VT-D oraz jądro 2.15 z włączoną obsługą IOMMU / KVM oraz nałożoną łatką acs override. Oczywiście nie obejdzie się bez 64-bitowego Windowsa 7 lub 8 i QEMU 2.0. Jeśli macie pecha tak jak ja i posiadacie na płycie głównej kontroler SATA Marvel 88SE91xx to napotkacie problem z dostępem DMA przy włączonej opcji iommu.

  1. ata8: SATA max UDMA/133 abar m2048@0xfa310000 port 0xfa310180 irq 48
  2. ata8: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
  3. ata8.00: qc timeout (cmd 0xec)
  4. ata8.00: failed to IDENTIFY (I/O error, err_mask=0x4)
  5. ata8: SATA link up 6.0 Gbps (SStatus 133 SControl 300)

Problem można rozwiązać wyłączając ten kontroler w BIOS-ie (jeśli z niego nie korzystacie, ja niestety mam podłączony pod niego trzeci dysk twardy) lub nakładając łatki dma-alias-v4. Dlaczego o tym wspominam? Po pierwsze, możesz posiadać taki kontroler na swojej płycie. Po drugie, problem ten może pojawić się z innymi specyficznymi urządzeniami. Zalecam zatem sprawdzić na początek działanie iommu tylko z nałożoną łatką acs override. Osobiście nie doświadczyłem negatywnych skutków działania tej łaty na innych systemach, nawet kiedy nie jest potrzebna.

Uruchomienie jądra z obsługą IOMMU i ACS

Do parametrów tak skompilowanego jądra dołączamy następujące opcje:

intel_iommu=on pci-stub.ids=1af2:a001 pcie_acs_override=downstream

Jeśli posiadasz procesor AMD to intel_iommu zamieniamy na amd_iomu. Użyty identyfikator 1af2:a001 to VendorID oraz ProductID naszej karty. Zweryfikować to można za pomocą polecenia lspci:

  1. # lspci
  2. 02:00.0 Unassigned class [ff00]: Device 1af2:a001

W ten sposób "odłączamy" ją z systemu hosta. Sprawdźmy zatem, czy po ponownym uruchomieniu wszystko jest jak należy:

  1. dmesg | grep dmar
  2. [    0.049835] dmar: Host address width 36
  3. [    0.049839] dmar: DRHD base: 0x000000fed91000 flags: 0x1
  4. [    0.049848] dmar: IOMMU 0: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
  5. [    0.049851] dmar: RMRR base: 0x000000bf4cc000 end: 0x000000bf4eefff
  1. # dmesg | grep -i iommu
  2. [    0.000000] Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA
  3. [    0.000000] Intel-IOMMU: enabled
  4. [    0.049848] dmar: IOMMU 0: reg_base_addr fed91000 ver 1:0 cap c9008020660262 ecap f0105a
  5. [    0.049921] IOAPIC id 0 under DRHD base  0xfed91000 IOMMU 0
  6. [    0.243132] IOMMU 0 0xfed91000: using Queued invalidation
  7. [    0.243135] IOMMU: Setting RMRR:
  8. [    0.243144] IOMMU: Setting identity map for device 0000:00:1a.0 [0xbf4cc000 - 0xbf4eefff]
  9. [    0.243163] IOMMU: Setting identity map for device 0000:00:1d.0 [0xbf4cc000 - 0xbf4eefff]
  10. [    0.243173] IOMMU: Prepare 0-16MiB unity mapping for LPC
  11. [    0.243180] IOMMU: Setting identity map for device 0000:00:1f.0 [0x0 - 0xffffff]
  1. # dmesg | grep pci-stub
  2. [   14.574603] pci-stub: add 1AF2:A001 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
  3. [   14.574615] pci-stub 0000:02:00.0: claimed by stub

Podłączenie karty AVerMedia Live Gamer HD

Istnieje kilka sposobów podłączenia tej karty. Zalecana metoda to połączenie karty graficznej kablem HDMI do wejścia Input, a monitora do wyjścia Output.

Ja podłączyłem monitor bezpośrednio do karty graficznej kablem DVI, a na obu systemach ustawiłem tryb pracy klonowania. W ten sposób mam ten sam obraz na obu wyjściach, pozostawiając wciąż wolne HDMI w monitorze.

Bindowanie karty do vfio-pci

W następnej kolejności musimy przypisać naszą kartę do vfio-pci. Dla ułatwienia możemy posłużyć się skryptem avermedia.sh

  1. #!/bin/bash
  2.  
  3. modprobe vfio-pci
  4.  
  5. dev=0000:02:00.0
  6. vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
  7. device=$(cat /sys/bus/pci/devices/$dev/device)
  8.  
  9. if [ -e /sys/bus/pci/devices/$dev/driver ]; then
  10.     echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
  11. fi
  12.  
  13. echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id

Zwróć uwagę na zmienną dev zawierającą ciąg 0000:02:00.0  - powinna zwierać identyfikator Twojej szyny PCI do której wpięta jest karta AVerMedia (sprawdzaliśmy to poleceniem lspci)

Uruchamiamy powyższy skrypt:

  1. # chmod 755 +x avermedia.sh
  2. # avermedia.sh

Dla ułatwienia, możemy dodać ten skrypt do rc.local, aby wykonywał się automatycznie przy starcie systemu.

Uruchomienie QEMU

Zakładam, że nie masz jeszcze zainstalowanego Windowsa, więc tworzymy sobie na początek 20GB dysk twardy,

  1. # dd if=/dev/zero of=windows.img bs=1M seek=20000 count=0

Następnie instalujemy Widnowsa:

  1. #qemu-system-x86_64
  2.     -enable-kvm
  3.     -M q35
  4.     -m 2048
  5.     -cpu host
  6.     -smp 2,sockets=1,cores=2,threads=1
  7.     -bios /usr/share/qemu/bios.bin
  8.     -device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root.1
  9.     -device vfio-pci,host=02:00.0,bus=root.1,addr=00.0,multifunction=on
  10.     -drive file=windows.img,id=disk,format=raw
  11.     -device ide-hd,bus=ide.0,drive=disk
  12.     -drive file=cdwin.iso,id=isocd
  13.     -device ide-cd,bus=ide.1,drive=isocd
  14.     -display sdl -sdl

Dobór parametrów pamięci, ilość dostępnych rdzeni i wątków procesora pozostawiam Wam, ponieważ jest to kwestia indywidualna, zależna od procesora i ilości pamięci RAM jaką posiadamy. Tutaj ponownie sprawdźcie, czy parametr -device vfio-pci,host posiada odpowiedni identyfikator szyny PCI hosta.

Uruchomienie i instalacja sterowników

Po uruchomieniu systemu zobaczymy w menadzerze urządzeń naszą kartę:

Po zainstalowaniu sterowników ze strony producenta, urządzenie powinno być w pełni widoczne:

Teraz nie pozostaje nam już nic innego, jak rozpocząć nagrywanie:

Przykład nagrywania rozgrywki z Anomaly Defenders

Kolega bigos zadał bardzo ciekwe pytanie odnośnie bezpiecznego użytkowania polecenia dd. Osoby często nagrywające obrazy systemów na różnych nośnikach są bardziej narażeni na przypadkowe wymazanie zawartości dysku. Przyznam się szczerze, że sam padłem ofiarą własnej nieuwagi podczas "wypalania" obrazu na nośniku SD i to nie raz.

Naturalnie nic nie zastąpi ostrożności, ale to tylko kwestia czasu, kiedy spotka to również Ciebie. Zastanawiając się nad tym problemem, wpadłem na pewne rozwiązanie filtrowania niebezpiecznych parametrów. Założenie jest proste - utworzyć skrypt bash o nazwie dd o odpowiedniej zawartości, a domyślne polecenie dd przemianować na ddunsafe.

  1. ls -o /bin/dd*
  1. -rwxr-xr-x 1 root   284 Jun 29 22:18 /bin/dd
  2. -rwxr-xr-x 1 root 56880 Feb 27  2013 /bin/ddunsafe

Sktypt dd przed wykonaniem polecenia zadba o to, abyśmy nie popełnili drugi raz gafy:

  1. #!/bin/bash
  2.  
  3. dennyArgs=("^of=/dev/sda$" "^of=/dev/sda1$")
  4.  
  5. for ARG in "$@"
  6. do
  7.     for dennyArg in ${dennyArgs[*]}
  8.     do
  9.     if [ "$(echo "$ARG" | egrep ${dennyArg})" ]
  10.     then
  11.         echo "OPS! Parametr ${dennyArg} zablokowany!"
  12.         exit -1
  13.     fi
  14.     done
  15. done
  16.  
  17. ddunsafe "$@"

Do tablicy dennyArgs, możemy dopisać chronione dyski i partycje. Próba dostępu do chronionengo nośnika zakończy się niepowodzeniem i wyświetleniem komunikatu.

  1. dd id=/dev/zero of=/dev/sda1
  1. OPS! Parametr ^of=/dev/sda1$ zablokowany!

Jeśli masz "wyjątkowe szczęście" tak jak ja, to pewnie zdarzyło Ci się przez przypadek usunąć ważny katalog lub pliki, które znaczyły dla Ciebie wiele. Z taką przygodą spotkałem się całkiem niedawno, bo w ubiegły weekend. Najważniejsze to nie panikować i działać szybko, bowiem czas i zwłoka działa na naszą niekorzyść. Dane po usunięciu nie znikają od razu z dysku, tak naprawdę wciąż tam są. Jedyne co usunęliśmy, to wpisy tablicy alokacji plików, które informują system, gdzie konkretnie leży nasz plik - prościej mówiąc - zmazaliśmy mapę do miejsc, gdzie się one zaczynają.

Jedną z nowości zawartych w jądrze 3.9.0 jest zdolność wykorzystywania dysków SSD przez device mappera jako cache dla wolniejszych dysków talerzowych. Idea działania polega na przechowywaniu na mniejszym, ale szybszym dysku SSD kopii plików, do których odwołujemy się najczęściej .

Na niemal identycznej zasadzie działają hybrydowe dyski  twarde - np. Seagate Momentus XT. Istotną różnicą jest jednak to, że powyższe rozwiązanie wykorzystuje dwa napędy zamiast jednego.

Konfiguracja testowa

Do testów posłuży nam konwencjolany dysk twardy Western Digital Caviar Black oraz Samsung SSD 830. Platformę testową stanowi Intel i5-2500 wraz z 16GB pamięci RAM.

Przygotowanie dysku SSD

Jedna z cech tego mechanizmu cache z wykorzystaniem dysku SSD jest rozdzielenie jego powierzchni pomiędzy obszarem przechowywania metadanych, a obszarem cache, który będzie gromadził nasze najczęściej wykorzystywane dane. Naturalnie rozmiar cache powinien być jak największy i nie ma większego kłopotu z wyborem jego rozmiaru. Kłopotliwy może być jednak obszar metadanych, którego rozmiar powinniśmy wyliczyć z poniższego wzoru:

4 MiB + (16 B * liczba_bloków)

Rozmiar partycji przyjmiemy 55 GiB, a jako rozmiar bloków 256 KiB - czyli 512 sektorów po 512 B. A więc:

Liczba bloków = 55 GiB / 256 KiB = 225280 bloków
Metadane = 4 MiB + (16 B * 225280) = 7798784 B = 7616 KiB

Przeliczmy jeszcze nasze wartości na 512 B sektory:

Partycja Cache = 55 GiB / 512 = 115343360 sektorów
Partycja Metadanych = 7616 KiB / 512 = 15232 sektory

Za pomocą programu fdisk przygotujemy teraz dysk SSD na podstawie powyższych wartości:

  1. fdisk -u=sectors -S32 -H32 /dev/sdb

Konfiguracja devmappera

Kiedy mamy już przygotowany dysk SSD, musimy skonfigurować nasz cache. Dla testów ograniczyłem się jednynie do partycji /dev/sda1, do której to będziemy potrzebowli rozmiar w sektorach:

  1. blockdev --getsz /dev/sda1
  2. 125788887

Jako rozmiar bloków przyjęliśmy na początku 256 KiB, a więc 512 sektorów.

  1. dmsetup create ssdcache --table '0 125788887 cache /dev/sdb1 /dev/sdb2 /dev/sda1 512 1 writeback default 0'

Pozostaje nam już tylko zamontować naszą "hybrydę" poniższym poleceniem:

  1. mount /dev/mapper/ssdcache /mnt/sda1

Testy: Kopiowanie pliku (2,5 GiB)

  1. echo 3 > /proc/sys/vm/drop_caches
  2. dd if=/storage/dystrybucje/Slackware/slackware-14.0-install-dvd.iso of=/dev/null

4802704+0 przeczytanych recordów
4802704+0 zapisanych recordów
skopiowane 2458984448 bajtów (2,5 GB), 25,2681 s, 97,3 MB/s

  1. echo 3 > /proc/sys/vm/drop_caches
  2. dd if=/mnt/sda1/slackware-14.0-install-dvd.iso of=/dev/null

4802704+0 przeczytanych recordów
4802704+0 zapisanych recordów
skopiowane 2458984448 bajtów (2,5 GB), 20,3597 s, 121 MB/s

Testy: Kopiowanie źródeł jądra 3.9.0 (1,2 GiB)

  1. echo 3 > /proc/sys/vm/drop_caches
  2. time cp -R /usr/src/linux-3.9/ /dev/shm/

real    0m48.583s
user    0m0.243s
sys     0m2.513s

  1. echo 3 > /proc/sys/vm/drop_caches
  2. time cp -R /mnt/sda1/linux-3.9/ /dev/shm/

real    0m38.133s
user    0m0.253s
sys     0m2.344s

Testy: Kompilacja jądra 3.9.0

  1. cd /usr/src/linux-3.9
  2. echo 3 > /proc/sys/vm/drop_caches
  3. time make -j4

real    6m3.854s
user    19m51.270s
sys     1m29.049s

  1. cd /mnt/sda1/linux-3.9
  2. echo 3 > /proc/sys/vm/drop_caches
  3. time make -j4

real    6m8.074s
user    19m56.077s
sys     1m29.865s

Podsumowanie

Zauważalnych różnic praktycznie nie ma. Jest odrobinę szybciej, jednak należy zastanowić się, czy nie lepszym rozwiązaniem jest postawienie systemu na dysku SSD zamiast stosować cache. Dotyczy się to również testów uruchomieniowych, gdzie programy uruchamiały się praktycznie w tym samym czasie. Przyjrzyjmy się jeszcze na koniec statystykom:

ssdcache: 0 125788887 cache 676/4544 4595 448904 8957 209538 0 607 627 579 0 2 migration_threshold 204800 4 random_threshold 4 sequential_threshold 512

Podczas krótkiej pracy z tym rozwiązaniem można zauważyć, że procent trafień podczas odczytu to zaledwie 1% (4595 trafień i 448904 chybień). Zapis wyszedł trochę lepiej bo, aż porywające 4% (8957 trafień i 209538 chybień).

Czy warto? Moim zdaniem nie - jednak na to pytanie musicie sobie odpowiedzieć sami.