1 poin oleh GN⁺ 2025-01-10 | 1 komentar | Bagikan ke WhatsApp
  • SerenityOS selama ini berjalan baik terutama di QEMU, tetapi di laptop nyata hambatan muncul satu per satu mulai dari booting, debugging, hingga akses penyimpanan, sehingga celah dukungan perangkat keras pun terlihat jelas
  • Target eksperimennya adalah Dell 3100 Chromebook dengan Intel Celeron N4020, RAM 4GB DDR4, eMMC 32GB, dan layar TN 1366×768, tetapi debugging dalam casing tertutup berbasis Cr50 yang semula diharapkan ternyata gagal pada board ini
  • Setelah jalur Cr50 buntu, sebuah Pi Pico berbasis RP2040 dipasang di dalam perangkat dan dihubungkan langsung ke UART serta flash SPI, lalu dengan CircuitPython dan serprog dibuatlah perangkat debugging dan flashing sementara bernama PicoCCD
  • Log boot awal sulit diperoleh karena UART MMIO 16550 di balik PCI tidak mudah dipakai secara langsung, sehingga IO port 0x80 yang dicatat oleh ChromeOS EC dimanfaatkan sebagai kanal keluaran sementara yang lambat
  • Dukungan eMMC akhirnya mencapai sebagian sesi grafis setelah melewati perbedaan inisialisasi SD/MMC, kontrol daya SDHCI yang hilang, dan penonaktifan perintah khusus SD, tetapi performa, stabilitas, dan perapian patch masih tersisa

Memilih Dell 3100 Chromebook sebagai target perangkat keras nyata

  • Saat ingin terlibat lebih dalam dengan SerenityOS, kelemahan pertama yang paling terlihat adalah bahwa ia bisa berjalan di QEMU tetapi dukungan perangkat keras nyata masih kurang
  • Dukungan UEFI sudah dikerjakan oleh spholz, jadi tidak disentuh, dan dalam pekerjaan ini cukup jika kernel branch master bisa boot lewat GRUB pada runtime TianoCore UEFI
  • Ingin menghindari cara debugging OS pada perangkat yang sama dengan mesin pengembangan utama, dan memilih perangkat keras yang relatif baru agar setidaknya masih layak dipakai sehari-hari
  • Setelah mencari Chromebook murah di Allegro, akhirnya membeli Dell 3100 seharga 95PLN, sekitar 25EUR
    • Intel Celeron N4020, 2 core, tanpa Hyper-Threading
    • DDR4 4GB
    • eMMC onboard 32GB
    • layar TN 1366×768 yang digerakkan oleh IGP UHD600
    • 2x USB-A, 2x USB-C, jack 3,5mm
    • keyboard yang terasa lebih baik daripada laptop bisnis kelas atas Dell
  • Perangkat ini selanjutnya disebut dengan hostname octopus

Harapan debugging berbasis Cr50 dan kegagalannya

  • Alasan besar memilih Chromebook adalah karena chip keamanan Cr50 dan embedded controller-nya menyediakan fitur yang berguna untuk debugging dalam casing tertutup
  • Hampir semua Chromebook setelah 2018 bisa memakai satu port USB-C untuk debugging dengan kabel SuzyQ, dan Cr50 biasanya mengekspos tiga perangkat ttyUSB
    • konsol Cr50 internal
    • konsol AP, yaitu port serial Chromebook
    • konsol cros_ec, yaitu embedded controller
  • Tujuannya adalah mengakses konsol serial tanpa harus membiarkan kabel menjuntai saat laptop terbuka, dan jika mempertimbangkan emulasi input keyboard serta kontrol status daya dari cros_ec, bentuk KVM sederhana pun tampak mungkin
  • Pada octopus yang sebenarnya, Cr50 CCD tidak berfungsi
    • Kabel SuzyQ baru dibuat dan hasil solder diverifikasi pada Chromebook lain, tetapi tetap gagal
    • octopus ternyata termasuk sedikit laptop di mana Dell tidak memasang sebagian resistor pada board sehingga CCD tidak berfungsi
    • Ada yang melaporkan sebagian keberhasilan dalam kondisi arah port tertentu dan charger terhubung, tetapi pada perangkat ini sama sekali tidak berfungsi
  • Informasi yang kemudian ditemukan menyebut resistor yang hilang seharusnya hanya memengaruhi flashing SPI, bukan bridge USB itu sendiri, sehingga alasan pasti mengapa debug Cr50 sama sekali tidak bekerja masih belum jelas

PicoCCD yang dibuat dengan Pi Pico

  • Setelah jalur Cr50 tertutup, diperiksa apakah board Pi Pico biasa bisa dimasukkan ke ruang kosong di dalam perangkat, dan ternyata muat dengan cukup baik
  • Mengacu pada skematik laptop serupa, tetapi tidak ada skematik yang benar-benar cocok dengan octopus
    • Satu port debug besar pada board adalah JTAG dan test point terkait Intel, jadi tidak sesuai tujuan
    • Yang lain adalah Google Servo, tetapi Google merilis beberapa probe debug dengan nama Servo dan dokumentasinya terbatas, sehingga sulit dicari
    • Sebagai rujukan, digunakan dokumentasi Servo
  • Dengan menyalakan applet UART milik Glasgow dan deteksi frekuensi, lalu menyelidiki langsung pad UART TX yang dicurigai saat Linux berulang kali mencetak ke /dev/ttyS1
    • Pad TX ditemukan dalam beberapa menit
    • RX lebih sulit karena memerlukan transmisi aktif dan menyentuh jalur yang salah bisa mereset board, yang memang terjadi dua kali
    • Setelah itu, pin RX/TX untuk EC juga ditemukan dalam sekitar 10 menit
  • Kabel yang disolder dipasang tetap dengan epoksi curing UV, dan selama 6 bulan tidak ada masalah koneksi
  • Periferal SPI pada RP2040 juga dimanfaatkan dengan menyolder 6 kabel ke chip flash, lalu trace menuju pin write-protect dipotong dan dihubungkan ke GND agar akses tulis bisa diperoleh tanpa izin Cr50
  • Untuk perangkat lunak dipilih CircuitPython
    • karena skrip dan data bisa diunggah lewat perangkat USB mass storage
    • pekerjaan menjembatani UART ke perangkat USB cdc_acm cukup sederhana
    • karena flash SPI juga terhubung, dibutuhkan pula fungsi flashing
  • Sebagai alat flashing EEPROM/SPI open source yang umum dipakai, digunakan flashrom, dan serprog yang mem-proxy SPI lewat UART terasa cocok untuk tujuan ini
    • Sudah ada implementasi C pico-serprog dari stacksmashing, tetapi harus mem-flash ulang Pico setiap kali ingin mem-flash BIOS, dan itu tidak cocok
    • Sebagai gantinya, serprog diimplementasikan dengan CircuitPython dan banyak merujuk pada applet serprog Glasgow
  • Kode hasilnya dirapikan menjadi solusi debugging dalam casing tertutup yang dibuat cepat, yaitu PicoCCD, dan repositorinya ada di PicoCCD di Forgejo
  • WeirdTreeThing juga menulis kode C RP2040 dengan tujuan serupa, dan ada pula versi PicoCCD miliknya

Mendapatkan log boot SerenityOS

  • Alpine Linux dipasang untuk keperluan debugging, lalu disiapkan utilitas dasar untuk membawa kernel SerenityOS yang dibangun dari luar
  • Setelah itu strukturnya berkembang hingga memiliki pengunduhan artefak otomatis dari mesin build, menimpa kernel setelah dekompresi, dan entri GRUB yang mengekstrak .tar ruang pengguna
  • Waktu iterasi sampai pengujian setelah perubahan pada saat penulisan sekitar 20 detik, yang cukup baik untuk ukuran hacking bare metal
  • Entri boot GRUB pertama pada dasarnya adalah multiboot /Kernel serial_debug, tetapi tidak ada keluaran baik di layar maupun port serial
  • Untuk masalah keluaran layar, ditemukan bahwa menambahkan insmod all_video ke entri boot GRUB adalah arah yang benar; itu belum menyelesaikan semuanya, tetapi membantu
  • Tidak adanya keluaran serial adalah masalah yang lebih besar
    • Log coreboot masih masuk sampai beberapa detik sebelumnya
    • UART pada perangkat ini bukan 16550 port-mapped tradisional, melainkan 16550A berbasis MMIO
    • Log Linux menunjukkan ttyS0 dan ttyS1 sebagai 16550A pada alamat MMIO
    • Pada lspci, perangkat itu terlihat sebagai PCI device Intel Celeron/Pentium Silver Processor Serial IO UART Host Controller

Solusi sementara 16550 UART dan port 0x80

  • Secara tradisional, perangkat eksternal pada IBM PC dipetakan ke port I/O x86 dan diakses dengan instruksi seperti outb dan inb
  • Banyak perangkat kemudian berpindah ke MMIO, tetapi port serial tetap mempertahankan cara lama karena kompetisi kecepatan tinggi tidak penting, sehingga berguna sebagai port debug
  • Dalam lingkungan umum, menulis sesuatu seperti outb 0x3f8, 0x41 akan membuat sisi lain menerima A, dan inisialisasinya juga singkat sehingga mudah diimplementasikan dalam proyek kecil
  • UART milik octopus adalah perangkat MMIO di balik PCI, dan pada tahap sangat awal boot SerenityOS sulit berharap PCI sudah diinisialisasi
    • SerenityOS memang memiliki implementasi bus PCI, tetapi tahap boot-nya terlalu dini
    • PCISerialDevice yang ada juga belum pernah dipakai dalam konteks MMIO
    • Membuat driver ini tanpa keluaran debug jelas bukan situasi ideal
  • Embedded controller pada perangkat ChromeOS mencatat semua penulisan ke IO port 0x80
    • Port ini secara tradisional dipakai untuk melaporkan status POST
    • Indikator kode boot 7-segmen pada motherboard bekerja dengan mendekode port 80
  • Hipotesis ini diuji dengan skrip yang menulis byte ke /dev/port di Linux, dan byte tersebut bisa dibaca pada konsol cros_ec
  • Dengan menaruh kode seperti IO::out8(0x80, 1); di sekitar titik awal SerenityOS yaitu Kernel/Arch/init.cpp, lokasi kemajuan bisa dilacak hingga menyempit ke titik crash di Memory::MemoryManager::initialize(0);
  • Setelah itu dicoba pendekatan mengubah alamat rutinitas tulis serial dari 0x3f8 menjadi 0x80
    • Awalnya banyak byte keluar, tetapi cros_ec tidak mampu me-relay-nya secara stabil sehingga terjadi overflow
    • Lebih jauh lagi, keluaran log cros_ec sendiri juga ikut rusak
    • Tidak seperti chip serial sungguhan, ia tidak memiliki buffer besar
  • Ini diakali dengan menyisipkan banyak keadaan tunggu berbasis nop di antara setiap penulisan
    • Jika semua pesan boot dicetak, waktu boot yang tadinya beberapa detik menjadi beberapa menit
    • Meski begitu, biaya ini masih dianggap bisa diterima untuk debugging bare metal
  • Untuk mem-parsing otomatis baris log cros_ec dan mendekodenya ke ASCII, digunakan one-liner Bash yang menggabungkan picocom, watch, grep, sed, cut, dan xxd

Framebuffer dan keluaran grafis pertama

  • Setelah mendapatkan log boot, selama beberapa hari masalahnya dicoba dipahami dengan membaca codebase langsung, tetapi akhirnya bantuan komunitas diminta
  • spholz memberi tahu tentang SerenityOS PR #24435 yang saat itu sedang terbuka, dan ketika dibangun dengan branch itu, generic framebuffer pun bekerja
  • Di layar muncul hasil yang terlihat seperti berhasil padahal pekerjaannya sebenarnya gagal, dan setelah itu masalah penyimpanan mulai tampak serius

eMMC dan masalah inisialisasi SD/MMC

  • Crash StorageManagement yang ada berujung pada assertion bahwa daftar controller kosong setelah inisialisasi SD Host Controller gagal
    • Dalam log terlihat PCI: Failed to initialize SD Host Controller dan ASSERTION FAILED: !m_controllers.is_empty()
    • Akibatnya kernel panic terjadi di StorageManagement::enumerate_storage_devices()
  • octopus memiliki chip eMMC 32GB, dan karena SerenityOS sudah punya sebagian driver SD, tampaknya menambahkan dukungan MMC seharusnya cukup
  • Untuk menggunakan kartu SD/MMC, pada dasarnya dibutuhkan tiga komponen besar
    • Host Controller: pada perangkat modern biasanya SDHCI sesuai spesifikasi SD Association
    • Bus yang menghubungkan ke Host Controller: dalam kasus ini PCI
    • Implementasi protokol komunikasi antara host dan kartu
  • Berdasarkan log crash, SerenityOS sudah memiliki dua komponen pertama, jadi masalah yang tersisa ada di sisi protokol
  • Protokol SD memiliki spesifikasi terbuka, tetapi MMC menjadi standar JEDEC pada 2007 dan akses resminya sejak itu berbayar
  • SD dan MMC memiliki urutan inisialisasi yang berbeda
    • SerenityOS memulai dengan mengirim CMD0 dan menunggu respons, dan ini seharusnya lolos baik pada SD maupun MMC
    • Setelah itu ia mengirim CMD8 untuk mengatur tegangan, tetapi MMC tidak mendukungnya sehingga seharusnya terjadi error
    • Beberapa referensi menyarankan setelah itu kartu di-reset lagi dan dianggap sebagai MMC
    • Referensi lain menunjukkan alur yang lebih menyeluruh dengan kombinasi hasil CMD8 dan CMD58 untuk membedakan versi SD dan tipe kapasitas juga
  • Semua pemeriksaan kompatibilitas tingkat lanjut tidak diimplementasikan; hanya pemeriksaan dasar yang dibuat

Register kontrol daya yang hilang dan perbaikannya

  • Secara ringkas, alur inisialisasi MMC saat itu terdiri dari langkah-langkah berikut
    • setelah reset, setel clock ke 400KHz
    • tunggu 1ms lalu tunggu 74 clock lagi
    • kirim CMD0 dan tunggu respons
    • ulangi CMD1 sampai bit ke-31 pada respons bernilai 1
    • setelah loop selesai, simpan nilainya ke register Operating Conditions
    • lanjutkan algoritme inisialisasi SD dengan melewati pembacaan register khusus SD
    • secara opsional deteksi kompatibilitas High-Speed dan aktifkan salah satu dari beberapa mode HS
  • Kode berhasil mencapai sekitar langkah 4, tetapi setelah itu eMMC tidak lagi merespons permintaan apa pun
  • Setelah beberapa hari tidak menemukan penyebabnya, eMMC mulai merespons ketika kode terkait reset controller dihapus, sehingga masalahnya menyempit ke fungsi reset_host_controller()
  • Keanehan fungsi itu adalah bahwa register host_configuration tidak ditemukan dalam standar
    • Kode sebelumnya rupanya telah mengelompokkan beberapa register secara sewenang-wenang menjadi dua grup host_configuration
    • Inisialisasinya juga belum sepenuhnya selesai, dan grup pertama hanya disetel ke 0 begitu saja
  • Di dalam grup pertama itu ternyata terdapat register Power Control yang mengendalikan regulator daya kartu
    • Pada sebagian perangkat keras, termasuk semua implementasi yang menggunakan eMMC, register ini diperlukan untuk benar-benar menyalakan kartu
    • Pada desain lain yang menghubungkan rel daya langsung ke slot, pengaturan ini bisa diabaikan
  • Sebagai solusi sementara, nilai asli yang seharusnya ada pada host_configuration_0 diambil dan digunakan
  • Inti masalahnya adalah upaya berkomunikasi dengan kartu saat dayanya bahkan belum dinyalakan
  • Setelah itu butuh beberapa jam lagi untuk menemukan dan menonaktifkan perintah tertentu yang hanya valid untuk kartu SD, dan ketika keluaran debug dari controller menjadi lebih bermakna, sisa pekerjaannya berjalan relatif biasa

Status saat ini dan pekerjaan yang tersisa

  • Pada akhirnya SerenityOS berhasil menampilkan sesi grafis yang sangat lambat dan sebagian rusak, tetapi segera hang
  • Masalah sesi grafis ini dan proses mengembalikan framebuffer ke kondisi normal akan dibahas dalam tulisan lanjutan
  • Seluruh pekerjaan ini merupakan proses belajar selama sekitar 6 bulan, dan di sela-selanya ada pekerjaan lain juga
  • Target berikutnya adalah merapikan patch dan mengirimkannya ke upstream tahun ini

1 komentar

 
GN⁺ 2025-01-10
Opini Hacker News
  • Saya pernah membaca bahwa menyesuaikan driver NetBSD untuk kernel kustom relatif mudah, jadi mungkin Serenity juga bisa menempuh jalan seperti itu
    Bagi OS baru, driver perangkat adalah hambatan besar

    • Salah satu filosofi Serenity adalah sebisa mungkin membuat semuanya sendiri dari nol, jadi meskipun driver NetBSD mudah disesuaikan dan lisensinya kompatibel, kemungkinan besar mereka akan menulis driver sendiri daripada memilih jalan itu
    • Saya sempat bertanya-tanya apakah untuk OS baru/hobi, sejak awal lebih baik menargetkan single-board computer populer seperti Raspberry Pi
      Karena konfigurasi perangkat kerasnya hampir tetap, membuat atau mengambil driver serta menguji sistem jadi lebih mudah
    • rump kernel/anykernel adalah konsep seperti itu
      Driver bisa dijalankan di user space dengan dukungan dasar yang minimal
      https://en.wikipedia.org/wiki/Rump_kernel
    • Pihak NetBSD bisa dipercaya. libc-nya sangat rapi, jadi saya pernah memakainya beberapa kali di berbagai proyek
      Namun untuk sisi driver, saya kurang tahu
    • Solusinya adalah memilih satu set perangkat keras yang bagus, dan kalau memungkinkan menjadikannya perangkat yang dijual langsung oleh penulis perangkat lunaknya, lalu membuat driver hanya untuk perangkat keras itu
      Sejauh yang terlihat, inilah kurang lebih cara yang sejak awal dilakukan Apple, dan ini satu-satunya contoh besar yang sukses di keluarga Unix untuk konsumen
      System76 juga hampir seperti itu, dan Frame.work juga mirip, tetapi kurang berfokus pada OS itu sendiri
  • Membuatnya berjalan di mesin yang semua kondisinya tidak menguntungkan adalah pekerjaan hacking yang benar-benar hebat, dan tampak sebagai hasil dari upaya luar biasa orang-orang berbakat

  • Membaca tulisan seperti ini membuat saya penasaran bagaimana cara mulai masuk ke dunia driver dan OS
    Kelihatannya terlalu rumit, jadi saya tidak begitu tahu harus mulai dari mana

    • Berkomunikasi dengan perangkat keras modern sebenarnya cukup sederhana, intinya membaca dan menulis memori perangkat keras
      Ini disebut memory-mapped I/O (MMIO). Di aplikasi biasa hal ini tidak bisa dilakukan karena kernel mencegah akses langsung ke memori perangkat keras
      Untuk memulai, Anda butuh bahasa seperti Rust/C++/C/Zig yang bisa menghasilkan machine code untuk CPU target, dan sebaiknya tanpa runtime atau GC. Kalau baru pertama kali memakai bahasa level rendah, saya menyarankan C karena contohnya banyak
      Anda juga perlu mempelajari assembly dasar untuk CPU target, dan beberapa instruksi mungkin tidak tersedia sebagai fungsi bawaan di bahasa level tinggi
      Setelah itu, dengan menulis kernel hello world, Anda akan mempelajari bagaimana CPU memulai kernel, serta bagaimana mode eksekusi dan tingkat privilege dibagi
      Berikutnya, di x86 misalnya, Anda mengatur CPU sesuai keinginan, seperti beralih ke long mode untuk memakai instruksi 64-bit, dan biasanya di tahap ini juga mengatur virtual memory
      Sampai di sini, Anda akan mulai memahami bagaimana CPU berinteraksi dengan OS, cara mengenumerasi perangkat yang tersedia dan menemukan lokasi memori, lalu masih banyak pekerjaan seperti file system dan scheduler
      Perbedaan antara perangkat lunak yang berjalan di atas OS dan kernel OS pada akhirnya adalah mode CPU yang sedang menjalankan kode tersebut, dan pada privilege tertinggi Anda bisa memakai instruksi yang tidak bisa digunakan aplikasi biasa
    • Saya mulai dari LDD. Bukunya sekitar 10 tahun, tetapi masih relevan
      Belakangan saya menemukan materi seperti harta karun yang tersembunyi di dokumentasi FreeBSD, dan di antaranya FreeBSD Architecture Handbook serta FreeBSD Developers' Handbook bisa sangat membantu
      https://lwn.net/Kernel/LDD3/
      https://docs.freebsd.org/en/books/
    • Sekitar 15 tahun lalu saya mengambil kelas pengembangan OS di universitas dan menggunakan Minix
      Minix ditulis dengan sangat rapi, kernelnya juga sekitar 5 ribu baris, dan dibahas di banyak buku ajar
      Saya mengimplementasikan server sederhana dan juga mencoba hacking kernel; karena Minix adalah microkernel, sebagian besar driver bekerja dengan cara seperti itu
      Saya membaca materi kuliah terlebih dahulu dan hampir tidak mengikuti kuliahnya, tetapi tetap mendapat nilai 8 dari 10
      Saya juga mendengar banyak hal baik tentang NetBSD dan SerenityOS, dan Andreas melakukan banyak pengembangan lewat live streaming
      Kalau tahu harus mulai dari mana, sebenarnya jadi mudah
    • Sejujurnya langkah pertama adalah memahami tujuan tiap komponen seperti OS, driver, dan perangkat
      Misalnya, driver perangkat berperan mengekspos antarmuka agar program lain yang berjalan di komputer bisa mengakses dan mengendalikan suatu perangkat
      https://m.youtube.com/watch?v=juGNPLdjLH4 adalah kuliah kilat yang lumayan
      Anda juga bisa membuat perangkat USB sederhana dengan Arduino atau semacamnya untuk bertukar informasi dengan PC. Contoh: https://m.youtube.com/watch?v=yTc2GLXfCOY
      Setelah itu, pahami apa yang dilakukan subsistem yang Anda minati dan bagaimana menjalankannya, lalu tulis kodenya. Perangkat penyimpanan, perangkat grafis, dan sebagainya termasuk di sini
      Raspberry Pi juga bisa menjadi titik awal yang baik untuk eksperimen semacam ini. Contoh: Writing a bare metal operating system for the raspberry pi https://github.com/babbleberry/rpi4-osdev
    • Tutorial ini benar-benar bagus
      https://wiki.osdev.org/Bare_Bones
  • Saya suka konsep SerenityOS dan browser Ladybird, jadi senang melihat kemajuan seperti ini

    • Sayangnya, sekarang keduanya sudah berpisah
      Ladybird bukan hanya menjadi proyek independen, tetapi juga tidak lagi melihat SerenityOS sebagai platform target
      Ladybird sedang semakin banyak melepas lapisan Serenity miliknya sendiri dan menggantinya dengan alternatif yang lebih arus utama
      Sebagai pengguna yang terutama memakai Linux, saya menantikan Ladybird menjadi alternatif nyata di Linux
      Namun sebagai penggemar SerenityOS, sayang rasanya energi dan inovasi yang masuk ke Ladybird keluar dari SerenityOS
  • Jika butuh bantuan untuk hacking Chromebook, tanyakan saja di milis chromium-os-dev
    Sepertinya ada yang bisa membantu membuat CCD berfungsi
    https://groups.google.com/a/chromium.org/g/chromium-os-dev?p...

  • Bootloader Depthcharge juga mendukung boot jaringan lewat TFTP
    Memang harus dibangun sendiri dan di-flash ke SPI, tetapi ini fitur yang sangat bagus saat mengembangkan kernel secara iteratif
    https://chromium.googlesource.com/chromiumos/platform/depthc...

  • Saya kira SerenityOS sudah pernah berjalan di perangkat keras nyata; apakah sejauh ini masih semuanya hanya berjalan di dalam QEMU?

    • Memang benar sebelumnya pernah dijalankan di perangkat keras nyata
      Namun hampir tidak ada driver yang layak disebut, jadi hanya berjalan dalam arti paling dasar, hanya mungkin di perangkat keras tertentu, dan mungkin tidak berjalan terlalu baik
      Upaya kali ini bertujuan membuatnya berjalan dengan andal setidaknya di satu platform perangkat keras nyata
  • Serenity tetap mengesankan, meski kadang saya tidak setuju dengan cara implementasinya

  • Saya datang untuk melihat hal menyeramkan seperti ini
    doas dd seek=$((0x$1)) bs=1 count=1 of=/dev/port < <(xxd -p -r <<< "$2")

    • Sebagai orang awam, saya benar-benar penasaran apa arti kode itu