โมดูลเคอร์เนลที่โหลดได้

ในฐานะที่เป็นส่วนหนึ่งของข้อกำหนดเคอร์เนลโมดูลที่เปิดตัวใน Android 8.0 เคอร์เนลระบบบนชิป (SoC) ทั้งหมดต้องรองรับโมดูลเคอร์เนลที่โหลดได้

ตัวเลือกการกำหนดค่าเคอร์เนล

หากต้องการรองรับโมดูลเคอร์เนลที่โหลดได้ android-base.config ในเคอร์เนลทั่วไปทั้งหมดจะมีตัวเลือก kernel-config ต่อไปนี้ (หรือเทียบเท่าสำหรับเวอร์ชันเคอร์เนล)

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

เคอร์เนลของอุปกรณ์ทั้งหมดต้องเปิดใช้ตัวเลือกเหล่านี้ นอกจากนี้ โมดูลเคอร์เนลควรรองรับการยกเลิกการโหลดและโหลดซ้ำทุกครั้งที่เป็นไปได้

การลงนามโมดูล

ระบบไม่รองรับการรับรองโมดูลสําหรับโมดูลผู้ให้บริการ GKI ในอุปกรณ์ที่ต้องรองรับการบูตที่ยืนยันแล้ว Android กำหนดให้โมดูลเคอร์เนลต้องอยู่ในพาร์ติชันที่เปิดใช้ dm-verity ซึ่งทำให้ไม่ต้องลงนามในโมดูลแต่ละรายการเพื่อตรวจสอบความถูกต้อง Android 13 ได้เปิดตัวแนวคิดของโมดูล GKI โมดูล GKI ใช้โครงสร้างพื้นฐานการลงนามของเวลาสร้างเคอร์เนลเพื่อแยกความแตกต่างระหว่าง GKI กับโมดูลอื่นๆ ขณะรันไทม์ ระบบจะอนุญาตให้โหลดโมดูลที่ไม่ได้เซ็นชื่อตราบใดที่โมดูลดังกล่าวใช้เฉพาะสัญลักษณ์ที่ปรากฏในรายการที่อนุญาต หรือมาจากโมดูลอื่นๆ ที่ไม่ได้เซ็นชื่อ การกำหนดค่าเคอร์เนล GKI เปิดใช้ CONFIG_MODULE_SIG_ALL=y เพื่อให้การลงนามโมดูล GKI ในระหว่างการสร้าง GKI โดยใช้คู่คีย์เวลาสร้างของเคิร์นัลสะดวกขึ้น คุณต้องเพิ่ม # CONFIG_MODULE_SIG_ALL is not set เป็นส่วนหนึ่งของข้อมูลโค้ดสำหรับกำหนดค่าเคอร์เนลเพื่อหลีกเลี่ยงการเซ็นโมดูลที่ไม่ใช่ GKI ในระหว่างการสร้างเคอร์เนลของอุปกรณ์

ตำแหน่งไฟล์

แม้ว่า Android 7.x และเวอร์ชันที่ต่ำกว่าจะไม่บังคับใช้ข้อกำหนดเกี่ยวกับโมดูลเคอร์เนล (และรองรับ insmod และ rmmod) แต่ Android 8.x ขึ้นไปแนะนำให้ใช้โมดูลเคอร์เนลในระบบนิเวศ ตารางต่อไปนี้แสดงการรองรับอุปกรณ์ต่อพ่วงเฉพาะบอร์ดที่เป็นไปได้ซึ่งจำเป็นในโหมดการบูต Android 3 โหมด

โหมดเปิดเครื่อง พื้นที่เก็บข้อมูล จอแสดงผล ปุ่มกด แบตเตอรี่ PMIC หน้าจอสัมผัส NFC, Wi-Fi,
บลูทูธ
เซ็นเซอร์ กล้อง
การกู้คืน
ที่ชาร์จ
Android

นอกจากความพร้อมใช้งานในโหมดการบูตของ Android แล้ว โมดูลเคอร์เนลยังอาจจัดหมวดหมู่ตามเจ้าของ (ผู้จำหน่าย SoC หรือ ODM) ด้วย หากมีการใช้โมดูลเคอร์เนล ข้อกำหนดสำหรับตำแหน่งของโมดูลในระบบไฟล์มีดังนี้

  • เคอร์เนลทั้งหมดควรรองรับการบูตและการต่อเชื่อมพาร์ติชันในตัว
  • ต้องโหลดโมดูลเคอร์เนลจากพาร์ติชันแบบอ่านอย่างเดียว
  • สำหรับอุปกรณ์ที่ต้องเปิดเครื่องแบบยืนยันแล้ว ระบบควรโหลดโมดูลเคอร์เนลจากพาร์ติชันที่ได้รับการยืนยัน
  • ไม่ควรวางโมดูลเคอร์เนลไว้ใน /system
  • ควรโหลดโมดูล GKI ที่จําเป็นสําหรับอุปกรณ์จาก /system/lib/modules ซึ่งเป็นลิงก์สัญลักษณ์ไปยัง /system_dlkm/lib/modules
  • โมดูลเคอร์เนลจากผู้ให้บริการ SoC ที่จําเป็นสําหรับโหมด Android แบบเต็มหรือโหมดชาร์จควรอยู่ใน /vendor/lib/modules
  • หากมีพาร์ติชัน ODM อยู่ โมดูลเคอร์เนลจาก ODM ที่จําเป็นสําหรับโหมด Android แบบเต็มหรือโหมดชาร์จควรอยู่ใน /odm/lib/modules มิเช่นนั้น โมดูลเหล่านี้ควรอยู่ใน /vendor/lib/modules
  • โมดูลเคอร์เนลจากผู้ให้บริการ SoC และ ODM ที่จําเป็นสําหรับโหมดการกู้คืนควรอยู่ในramfsการกู้คืนที่ /lib/modules
  • โมดูลเคอร์เนลที่จําเป็นสําหรับทั้งโหมดการกู้คืนและ Android แบบเต็มหรือโหมดชาร์จควรอยู่ในทั้งพาร์ติชันการกู้คืน rootfs และพาร์ติชัน /vendor หรือ /odm (ตามที่อธิบายไว้ข้างต้น)
  • โมดูลเคอร์เนลที่ใช้ในโหมดการกู้คืนไม่ควรขึ้นอยู่กับโมดูลที่อยู่ใน /vendor หรือ /odm เท่านั้น เนื่องจากระบบไม่ได้มาสก์พาร์ติชันเหล่านั้นในโหมดการกู้คืน
  • โมดูลเคอร์เนลของผู้ให้บริการ SoC ไม่ควรใช้โมดูลเคอร์เนลของ ODM

ใน Android 7.x และต่ำกว่า ระบบจะไม่เมานต์พาร์ติชัน /vendor และ /odm ในช่วงต้น ใน Android 8.x ขึ้นไป ระบบได้เตรียมการเพื่อทำให้สามารถโหลดโมดูลจากพาร์ติชันเหล่านี้ได้ โดยได้ทำการต่อเชื่อมพาร์ติชันตั้งแต่เนิ่นๆ สำหรับทั้งอุปกรณ์ที่ไม่ใช่ A/B และอุปกรณ์ A/B ซึ่งวิธีนี้ยังช่วยให้มั่นใจว่าระบบจะเมานต์พาร์ติชันทั้งในโหมด Android และโหมดชาร์จ

การสนับสนุนระบบบิลด์ของ Android

ใน BoardConfig.mk บิลด์ Android จะกำหนดตัวแปร BOARD_VENDOR_KERNEL_MODULES ซึ่งแสดงรายการโมดูลเคอร์เนลทั้งหมดที่มีไว้สำหรับภาพผู้ให้บริการ ระบบจะคัดลอกโมดูลที่แสดงในตัวแปรนี้ลงในรูปภาพผู้ให้บริการที่ /lib/modules/ และหลังจากติดตั้งใน Android แล้ว โมดูลจะปรากฏใน /vendor/lib/modules (ตามข้อกำหนดข้างต้น) ตัวอย่างการกำหนดค่าของโมดูลเคอร์เนลของผู้ให้บริการ

vendor_lkm_dir := device/$(vendor)/lkm-4.x
BOARD_VENDOR_KERNEL_MODULES := \
  $(vendor_lkm_dir)/vendor_module_a.ko \
  $(vendor_lkm_dir)/vendor_module_b.ko \
  $(vendor_lkm_dir)/vendor_module_c.ko

ในตัวอย่างนี้ ระบบจะแมปที่เก็บโมดูลเคอร์เนลของผู้ให้บริการที่คอมไพล์ไว้ล่วงหน้าไปยังบิลด์ Android ในตำแหน่งที่ระบุไว้ด้านบน

รูปภาพการกู้คืนอาจมีชุดย่อยของโมดูลของผู้ให้บริการ บิลด์ Android จะกําหนดตัวแปร BOARD_RECOVERY_KERNEL_MODULES สําหรับข้อบังคับเหล่านี้ ตัวอย่าง

vendor_lkm_dir := device/$(vendor)/lkm-4.x
BOARD_RECOVERY_KERNEL_MODULES := \
  $(vendor_lkm_dir)/vendor_module_a.ko \
  $(vendor_lkm_dir)/vendor_module_b.ko

บิลด์ Android จะจัดการการเรียกใช้ depmod เพื่อสร้างไฟล์ modules.dep ที่จำเป็นใน /vendor/lib/modules และ /lib/modules (recovery ramfs)

การโหลดและการกำหนดเวอร์ชันของโมดูล

โหลดโมดูลเคอร์เนลทั้งหมดในครั้งเดียวจาก init.rc* โดยการเรียกใช้ modprobe -a วิธีนี้จะช่วยหลีกเลี่ยงค่าใช้จ่ายในการเริ่มต้นสภาพแวดล้อมรันไทม์ C ซ้ำๆ สําหรับไบนารี modprobe เหตุการณ์ early-init สามารถแก้ไขเพื่อเรียกใช้ modprobe ดังนี้

on early-init
    exec u:r:vendor_modprobe:s0 -- /vendor/bin/modprobe -a -d \
        /vendor/lib/modules module_a module_b module_c ...

โดยปกติแล้ว โมดูลเคอร์เนลต้องได้รับการคอมไพล์ด้วยเคอร์เนลที่จะใช้กับโมดูล (มิฉะนั้นเคอร์เนลจะไม่โหลดโมดูล) CONFIG_MODVERSIONS ให้วิธีแก้ปัญหาโดยการตรวจหาข้อบกพร่องในอินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) ฟีเจอร์นี้จะคํานวณค่าการตรวจสอบซ้ำแบบวน (CRC) สําหรับโปรโตไทป์ของสัญลักษณ์ที่ส่งออกแต่ละรายการในเคอร์เนล และจัดเก็บค่าไว้เป็นส่วนหนึ่งของเคอร์เนล สําหรับสัญลักษณ์ที่โมดูลเคอร์เนลใช้ ระบบจะจัดเก็บค่าไว้ในโมดูลเคอร์เนลด้วย เมื่อโหลดโมดูล ระบบจะเปรียบเทียบค่าของสัญลักษณ์ที่โมดูลใช้กับค่าในเคอร์เนล หากค่าตรงกัน ระบบจะโหลดโมดูล หากไม่ตรงกัน ระบบจะโหลดไม่สำเร็จ

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

เช่น โครงสร้าง task_struct ในเคอร์เนล (ที่กําหนดไว้ใน include/linux/sched.h) มีช่องหลายช่องที่รวมอยู่แบบมีเงื่อนไขโดยขึ้นอยู่กับการกําหนดค่าเคอร์เนล ช่อง sched_info จะปรากฏก็ต่อเมื่อเปิดใช้ CONFIG_SCHED_INFO เท่านั้น (ซึ่งจะเกิดขึ้นเมื่อเปิดใช้ CONFIG_SCHEDSTATS หรือ CONFIG_TASK_DELAY_ACCT) หากตัวเลือกการกําหนดค่าเหล่านี้เปลี่ยนสถานะ เลย์เอาต์ของโครงสร้าง task_struct จะเปลี่ยนแปลงและอินเทอร์เฟซที่ส่งออกจากเคอร์เนลที่ใช้ task_struct จะเปลี่ยนแปลง (เช่น set_cpus_allowed_ptr ใน kernel/sched/core.c) ความเข้ากันได้กับโมดูลเคอร์เนลที่คอมไพล์ไว้ก่อนหน้านี้ซึ่งใช้อินเทอร์เฟซเหล่านี้จะใช้งานไม่ได้ จึงต้องสร้างโมดูลเหล่านั้นอีกครั้งโดยใช้การกำหนดค่าเคอร์เนลใหม่

ดูรายละเอียดเพิ่มเติมเกี่ยวกับ CONFIG_MODVERSIONS ได้ที่เอกสารประกอบในต้นไม้เคอร์เนลที่ Documentation/kbuild/modules.rst