การศึกษาลินุกซ์เคอร์เนล ด้วย Raspberry Pi: ทำความรู้จักกับ device tree

By | March 27, 2017

Device Tree คืออะไร

คนที่เคยเล่น Raspbian OS image คงเคยเห็นไฟล์ .dtb ที่อยู่บน sd card และต้องใช้คู่กับลีนุกซ์เคอร์เนลใน ขั้นตอนการบูตระบบ แต่อาจจะยังไม่รู้ว่ามันคืออะไร

boot partition on raspbian sdcard

ไฟล์ .dtb นี้คือ Device Tree Blob (DTB) หรือ Flattened Device Tree (FDT) ใช้สำหรับการระบุรายละเอียดของฮาร์ดแวร์ของบอร์ดใหักับลินุกซ์เคอร์เนลในช่วงการบูต บน wikipedia อธิบายแบบไม่ยาวมากไว้ว่า

“The device tree is a data structure for describing hardware, which originated from Open Firmware. The data structure can hold any kind of data as internally it is a tree of named nodes and properties. Nodes contain properties and child nodes, while properties are name–value pairs.”

แปลเป็นไทยคงประมาณว่า “เป็นไฟล์ไบนารี่ที่เก็บรายละเอียดโครงสร้างฮาดร์แวร์ของบอร์ดไว้ โดยนำรูปแบบมาจาก Open Firmware โครงสร้างข้อมูลที่บรรจุอยู่ภายในไฟล์สามารถเป็นชนิดใดก็ได้ในรูปแบบแผนภูมิต้นไม้ของโหนด (node) และคุณสมบัติ (properties) แต่ละโหนดจะเก็บโหนดลูก (child nodes) และคุณสมบัติ (properties) เอาไว้ ส่วนคุณสมบัติ (properties) จะเก็บข้อมูลแบบคู่แพร์ของชื่อและค่าเอาไว้”

ตัวอย่างของ device tree source file (.dts)

ซ้อสโค้ดด้านล่างแสดงตัวอย่าง(บางส่วน) ของ device tree ซ้อสโค้ด ของบอร์ดราสเบอรี่พาย โมเดล B

ความจำเป็นที่ต้องใช้ device tree

หนึ่งในปัญหาหลักของการบูตของ ARM platform ก็คือความหลากหลายในการสร้างบอร์ดต่างๆออกมาสู่ท้องตลาด ทำให้ต้องมีโค้ดส่วนการตั้งค่าเริ่มต้นเฉพาะ(board initialization) เนื่องจากแม้จะเลือกซีพียูชิป เบอร์เดียวกัน แต่เมื่อไปออกแบบเป็นบอร์ด สามารถมีความแตกต่างกันได้ตั้งแต่ระดับ architecture , core , โมดูลต่างๆบนตัวชิป (Soc) จนไปถึง อุปกรณ์ต่อพ่วงบนบอร์ด (board peripheral)

ARM platform with SoC

Variant of board design

ยกตัวอย่างให้เห็นชัดเจนขึ้น เช่นความยืดหยุ่นในการเลือกใช้ขาของชิป ซึ่งอาจทำงานได้มากกว่าหนึ่งหน้าที่ต่อหนึ่งขาสัญญาณ (pin mux configuration) หากมีผู้ผลิตสองบริษัทนำชิปตัวเดียวกันไปออกแบบเป็นบอร์ดของตัวเอง ก็อาจจะเลือกใช้การใช้งานของขาไม่เหมือนกัน ผลของเรื่องนี้ก็คือ บอร์ดที่ใช้ชิปเดียวกันจากผู้ผลิตบอร์ดทั้งสองก็จะต้องมีโค้ดส่วนการตั้งค่าเริ่มต้นเฉพาะของตัวเองขึ้นมา (one board file per board) เรื่องฟังดูแล้วก็อาจจะไม่เป็นปัญหาสำหรับนักพัฒนาเท่าไหร่นัก แต่สำหรับผู้ดูแลซอสโค้ดของลีนุกซ์มันจะเป็นปัญหาในระยะยาว เพราะนับวันก็จะมีบอร์ดใหม่ๆ เพิ่มขึ้นมาเรื่อยๆ

บางคนอาจจะสงสัยว่าถ้าอย่างนั้นทำไมไม่ทำให้เคอร์เนลตรวจสอบฮาร์ดแวร์ที่มีอยู่ก่อนแล้วจึงค่อยทำการเรียกดีไวซ์ไดร์เวอร์ที่ถูกต้องมาทำงาน ฟังดูง่ายดี แต่ปัญหาของเรื่องนี้ก็คืออุปกรณ์ส่วนใหญ่ที่อยู่บนบอร์ดไม่สามารถทำการตรวจสอบได้โดยซีพียู ลองคิดดูว่าขาของชิปที่เป็นได้ทั้ง i2c , gpio หรือ mdio แล้วซีพียูจะรู้ได้อย่างไรว่าผู้ผลิตบอร์ดเอาไปต่อกับอะไร นี่ยังไม่รวมโมดูลต่างๆ(SoC) ของชิปว่าเคอร์เนลต้องเปิดหรือปิดการใช้งานส่วนไหนขึ้นมาบ้าง

ด้วยเหตุผลนี้ส่วนการบรรยายลักษณะของฮาร์ดแวร์ (hardware description) จึงจำเป็นต้องอยู่ในรูปแบบสแตติก
การนำ device tree มาใช้จึงเป็นการแยกส่วนของข้อมูลของฮาร์ดแวร์ที่เป็นสแตติก ออกมาจากส่วนที่เป็นโค้ดซึ่งจะทำให้โค้ดส่วนการตั้งค่าเริ่มต้น มีความเป็นไดนามิกมากขึ้น ทำให้ไฟล์เคอร์เนลไบนารี่ไฟล์เดียวสามารถบูตบอร์ดที่ใช้ชิปตัวเดียวกันแต่มีอุปกรณ์รอบข้างต่างกันได้

และเนื่องจาก Device Tree Blob นั้นไม่ขึ้นอยู่กับระบบปฏิบัติการ ทำให้สามารถนำไปใช้กับบูตโหลดเดอร์หรือระบบปฏิบัติการอื่นที่ไม่ใช่ลีนุกซ์ได้อีกด้วย

การนำไปใช้บน ราสเบอรี่พาย (Device tree on Raspberry pi)

ราสเบอรี่พาย เป็นตัวอย่างที่ดีในการนำ device tree ไปใช้ เนื่องจากความสำเร็จในการทำตลาดของบอร์ดรุ่นแรก ทำให้มีบอร์ดรุ่นอื่นๆ ตามมาในซีรี่ย์ ซึ่งถึงแม้จะเป็นบอร์ดในซีรี่ย์เดียวกัน แต่ก็ยังมีความแตกต่างในอุปกรณ์บางส่วน
การนำ device tree มาใช้ในราสเบอรี่พาย ทำให้ sd card boot images สามารถบูตบอร์ดได้มากกว่า 1 โมเดล ซึ่งช่วยตัดปัญหาอื่นๆ เช่น การออกรุ่นใหม่ๆของไฟล์อิมเมจ ง่ายขึ้น , ป้องกันการนำไฟล์ไปใช้ผิดรุ่น , ลดขนาดพื้นที่จัดเก็บ

ข้อเสียของการใช้ device tree

ต้องการความสามารถใหม่ของบูตโหลดเดอร์ เพื่อที่จะทำการโหลด device tree blob และส่งต่อไปให้กับเคอร์เนลทาง ATAGS (คหสต: ตรงส่วนนี้สำหรับเราซึ่งเป็น developer ปลายแถวก็คงไม่ได้เป็นปัญหาอะไรมาก นักเพราะทางลีนุกซ์ kernel main line ต้องทำให้เสร็จเรียบร้อยก่อนที่จะมาถึงมือเรา)

ต้องการความเสถียรของ ABI (Application binary interface) เพราะ (.dtb) ถูกกำหนดให้มีความเข้ากันได้กับรุ่นเก่าด้วย ฟอร์แมตของไฟล์ไบนารี่ที่ใช้จึงต้องมีความแน่นอน ซึ่งในสายการพัฒนาของลินุกซ์นั้นเรื่องนี้ถือว่าเกิดขึ้นยากเพราะมีความเปลี่ยนแปลงตลอดเวลา

ต้องการบูตสคริปที่ทำงานกับ (.dtb) ได้ ซึ่งการจะเปลื่ยนโค้ดส่วนการตั้งค่าบอร์ดจากเดิมที่เป็นสแตติกซึ่งง่ายให้มาเป็นแบบไดนามิกโดยแยกส่วนที่เป็น configurable ออกมาภายนอกนั้น เป็นเรื่องไม่ง่ายเลย

ใช้เวลาในการบูตนานขึ้น เพราะโค้ดส่วนการตั้งค่าบอร์ดต้องมีขั้นตอนการอ่านค่าของ (.dtb) แล้วจึงค่อยทำงาน

ทำให้เคอร์เนลไบนารี่ไฟล์ มีขนาดใหญ่ขึ้น เพราะว่าจะมีโค้ดส่วนที่ไม่ได้ใช้งานรวมอยู่ด้วย สำหรับในการที่ต้องรองรับบอร์ดหลายรุ่น

แหล่งข้อมูลในการศึกษาเกี่ยวกับ device tree เพิ่มเติม

เอกสารเกี่ยวกับสเปคของ device tree https://www.devicetree.org/

เอกสารเกี่ยวกับรายละเอียดเรื่อง device tree ของบอร์ด ราสเบอรรี่ พาย https://www.raspberrypi.org/documentation/configuration/device-tree.md