เคอร์เนลรู้ที่อยู่ฐานหน่วยความจำกายภาพได้อย่างไร

Jan 02 2021

ฉันพยายามทำความเข้าใจ 2 ประเด็นที่เกี่ยวข้องอย่างใกล้ชิด

  1. โค้ดเคอร์เนลที่รัน post-bootloader และก่อนที่จะเปิดใช้งาน MMU จะทำงานในหน่วยความจำเสมือนที่แมปฟิสิคัล / เอกลักษณ์ รหัสนี้ทำให้พกพาได้อย่างไรระหว่าง CPU ที่แตกต่างกันอาจมี DRAM ในช่วงที่อยู่ทางกายภาพที่แตกต่างกัน

  2. สำหรับเคอร์เนลในการจัดการตารางเพจจำเป็นต้องมีการรับรู้ว่ามีทรัพยากรหน่วยความจำกายภาพใดบ้างรวมถึงที่อยู่ฐานหน่วยความจำฟิสิคัลและหน่วยความจำกายภาพที่มีอยู่ดังนั้นจึงไม่กำหนดที่อยู่ทางกายภาพที่อยู่นอกช่วง DRAM

ฉันคิดว่าสิ่งนี้ขึ้นอยู่กับการนำไปใช้งาน แต่การอ้างอิงถึงวิธีที่สถาปัตยกรรมต่างๆจัดการกับปัญหานี้จะได้รับการชื่นชม ความคิดบางอย่างที่ฉันมีจนถึงตอนนี้:

  1. ช่วง DRAM ที่อยู่ทางกายภาพหรืออย่างน้อยก็คือที่อยู่ฐานถูกอบในเวลาคอมไพล์เคอร์เนล นี่หมายความว่าจำเป็นต้องมีการคอมไพล์ใหม่สำหรับ CPU ที่แตกต่างกันแม้ว่าจะมี ISA เดียวกันก็ตาม นี่เป็นแรงบันดาลใจจากคำตอบที่นี่ซึ่งถ้าฉันเข้าใจถูกต้องอธิบายวิธีแก้ปัญหาเดียวกันสำหรับที่อยู่ฐานเคอร์เนล เนื่องจากที่อยู่ฐานเป็นที่รู้จักในเวลาคอมไพล์โค้ดเคอร์เนลจึงอ้างอิงแอดเดรสตามตัวอักษรแทนที่จะเป็นออฟเซ็ตจาก DRAM / เคอร์เนลเบสแอดเดรส

  2. ข้อมูล DRAM จะอ่านและเรียนรู้จากโครงสร้างอุปกรณ์พร้อมแผนที่หน่วยความจำกายภาพที่เหลือ นี้เป็นความประทับใจของฉันอย่างน้อย Xilinx Zynq SoC ของขึ้นอยู่กับการโพสต์ของฟอรั่มเช่นนี้ แม้ว่าโซลูชันนี้จะมีความยืดหยุ่นมากขึ้นและช่วยให้เราสามารถคอมไพล์บูตโหลดเดอร์ใหม่แทนที่จะเป็นเคอร์เนลทั้งหมดไปยังพอร์ต CPU แต่ก็ทำให้ฉันสงสัยว่าเครื่อง X86 ส่วนบุคคลของฉันสามารถตรวจจับได้อย่างไรในขณะทำงาน โค้ดสำหรับจัดการตารางเพจจะอ้างอิงการชดเชยจากที่อยู่ฐาน DRAM และสามารถพกพาได้โดยไม่ต้องคอมไพล์ซ้ำระหว่าง CPU ที่มีช่วงที่อยู่ทางกายภาพ DRAM ที่แตกต่างกัน

คำตอบ

3 HadiBrais Jan 04 2021 at 12:43

DIMM หน่วยความจำฟิสิคัลทั้งหมดที่พร้อมใช้งานในเวลาบูตอาจไม่ได้และโดยทั่วไปจะไม่ถูกแมปกับช่วงที่อยู่หน่วยความจำกายภาพที่อยู่ติดกันเพียงช่วงเดียวดังนั้นจึงไม่มี "ที่อยู่ฐาน" ในการฮาร์ดรีเซ็ตหลังจากที่เฟิร์มแวร์ของ CPU เสร็จสิ้นการทำงานเฟิร์มแวร์ของแพลตฟอร์มซึ่งโดยปกติจะเป็น BIOS หรือ UEFI แบบเดิม เมนบอร์ดที่ระบุจะเข้ากันได้กับชุด CPU ที่ จำกัด ซึ่งโดยทั่วไปจะมีวิธีการเดียวกันในการค้นหาหน่วยความจำกายภาพรวมถึง DIMM และอุปกรณ์หน่วยความจำเฟิร์มแวร์ของแพลตฟอร์ม การใช้งานเฟิร์มแวร์ของแพลตฟอร์มใช้วิธีนี้เพื่อสร้างตารางของรายการคำอธิบายหน่วยความจำโดยแต่ละรายการอธิบายช่วงที่อยู่หน่วยความจำกายภาพ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับลักษณะของโปรเซสเซอร์นี้โปรดดูที่: BIOS เริ่มต้น DRAM ได้อย่างไร? . ตารางนี้จัดเก็บไว้ที่แอดเดรสในหน่วยความจำหลัก (DIMM) ซึ่งทราบว่าสงวนไว้สำหรับวัตถุประสงค์นี้และควรได้รับการสำรองข้อมูลโดยหน่วยความจำจริง (ระบบอาจบูตได้โดยไม่มี DIMM)

การใช้งาน x86 PC BIOS ส่วนใหญ่ตั้งแต่กลางยุค 90 มีINT 15h E820hฟังก์ชันโหมดจริง(15 ชม. คือหมายเลขขัดจังหวะและ E820h เป็นอาร์กิวเมนต์ที่ส่งผ่านในAXรีจิสเตอร์) นี่เป็นฟังก์ชั่น BIOS เฉพาะของผู้จำหน่ายที่เปิดตัวครั้งแรกใน PhoenixBIOS v4.0 (ปี 1992-1994 ฉันไม่สามารถปักหมุดปีที่แน่นอนได้) และนำมาใช้โดยผู้จำหน่าย BIOS รายอื่นในภายหลัง อินเทอร์เฟซนี้ได้รับการขยายโดยข้อกำหนด ACPI 1.0 ที่เผยแพร่ในปี 2539 และการปรับปรุง PhoenixBIOS ในภายหลังที่รองรับ ACPI อินเทอร์เฟซ UEFI ที่สอดคล้องกันคือGetMemoryMap()ซึ่งเป็นบริการเวลาบูต UEFI (หมายความว่าสามารถเรียกได้เฉพาะเวลาบูตตามที่กำหนดไว้ในข้อกำหนด UEFI) เคอร์เนลสามารถใช้หนึ่งในอินเทอร์เฟซเหล่านี้เพื่อรับแผนที่ที่อยู่ซึ่งอธิบายถึงหน่วยความจำบนโหนด NUMA ทั้งหมด อื่น ๆ (เก่า) วิธีการบนแพลตฟอร์ม x86 มีการหารือในการตรวจสอบหน่วยความจำ (x86) ทั้งข้อกำหนด ACPI เริ่มต้นด้วยรุ่น? และข้อกำหนด UEFI เริ่มต้นด้วยเวอร์ชัน? รองรับประเภทช่วงหน่วยความจำ DRAM DIMM และ NVDIMM

ลองพิจารณาตัวอย่างเช่นว่าเคอร์เนล Linux ที่เข้ากันได้กับ ACPI กำหนดช่วงที่อยู่ทางกายภาพใดที่พร้อมใช้งาน (เช่นสำรองโดยหน่วยความจำจริง) และใช้งานได้ (เช่นฟรี) บนแพลตฟอร์ม BIOS ที่รองรับ x86 ACPI เฟิร์มแวร์ BIOS จะโหลด bootloader จากอุปกรณ์จัดเก็บข้อมูลที่สามารถบู๊ตได้ที่ระบุไปยังตำแหน่งหน่วยความจำสำหรับวัตถุประสงค์นี้โดยเฉพาะ หลังจากเฟิร์มแวร์เสร็จสิ้นการเรียกใช้งานเฟิร์มแวร์จะข้ามไปที่ bootloader ซึ่งจะพบภาพเคอร์เนลบนสื่อบันทึกข้อมูลโหลดไปยังหน่วยความจำและโอนการควบคุมไปยังเคอร์เนล bootloader เองจำเป็นต้องทราบแผนที่หน่วยความจำปัจจุบันและจัดสรรหน่วยความจำบางส่วนสำหรับการทำงาน มันพยายามรับแผนที่หน่วยความจำโดยเรียกใช้E820hฟังก์ชันและหากไม่รองรับก็จะใช้อินเทอร์เฟซ BIOS ของพีซีรุ่นเก่า โปรโตคอลบูตเคอร์เนลกำหนดซึ่งช่วงที่หน่วยความจำที่สามารถนำมาใช้โดย bootloader และหน่วยความจำช่วงจะต้องถูกทิ้งไว้พร้อมใช้งานสำหรับเคอร์เนล

bootloader เองไม่ได้แก้ไขแผนที่หน่วยความจำหรือให้แผนที่ไปยังเคอร์เนล แต่เมื่อเคอร์เนลเริ่มทำงานมันจะเรียกใช้E820hฟังก์ชันและส่งผ่านไปยังตัวชี้ 20 บิต (ในES:DI) ไปยังบัฟเฟอร์ที่เคอร์เนลรู้ว่าเป็นอิสระบนแพลตฟอร์ม x86 ตามโปรโตคอลการบูต การเรียกแต่ละครั้งจะส่งกลับตัวบอกช่วงหน่วยความจำที่มีขนาดอย่างน้อย 20 ไบต์ สำหรับข้อมูลเพิ่มเติมโปรดดูข้อมูลจำเพาะ ACPI เวอร์ชันล่าสุด การใช้งาน BIOS ส่วนใหญ่รองรับ ACPI

สมมติว่าเคอร์เนล Linux ที่มีพารามิเตอร์การบูตอัพสตรีม - ดีฟอลต์คุณสามารถใช้คำสั่งdmesg | grep 'BIOS-provided\|e820'เพื่อดูตารางตัวบอกช่วงหน่วยความจำที่ส่งคืน ในระบบของฉันดูเหมือนว่า:

[    0.000000] BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x00000000000917ff] usable
[    0.000000] BIOS-e820: [mem 0x0000000000091800-0x000000000009ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000000e0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000d2982fff] usable
[    0.000000] BIOS-e820: [mem 0x00000000d2983000-0x00000000d2989fff] ACPI NVS
[    0.000000] BIOS-e820: [mem 0x00000000d298a000-0x00000000d2db9fff] usable
[    0.000000] BIOS-e820: [mem 0x00000000d2dba000-0x00000000d323cfff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000d323d000-0x00000000d7eeafff] usable
[    0.000000] BIOS-e820: [mem 0x00000000d7eeb000-0x00000000d7ffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000d8000000-0x00000000d875ffff] usable
[    0.000000] BIOS-e820: [mem 0x00000000d8760000-0x00000000d87fffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000d8800000-0x00000000d8fadfff] usable
[    0.000000] BIOS-e820: [mem 0x00000000d8fae000-0x00000000d8ffffff] ACPI data
[    0.000000] BIOS-e820: [mem 0x00000000d9000000-0x00000000da718fff] usable
[    0.000000] BIOS-e820: [mem 0x00000000da719000-0x00000000da7fffff] ACPI NVS
[    0.000000] BIOS-e820: [mem 0x00000000da800000-0x00000000dbe11fff] usable
[    0.000000] BIOS-e820: [mem 0x00000000dbe12000-0x00000000dbffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000dd000000-0x00000000df1fffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000f8000000-0x00000000fbffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fec00000-0x00000000fec00fff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fed00000-0x00000000fed03fff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fed1c000-0x00000000fed1ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000ff000000-0x00000000ffffffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000041edfffff] usable
[    0.002320] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[    0.002321] e820: remove [mem 0x000a0000-0x000fffff] usable
[    0.002937] e820: update [mem 0xdd000000-0xffffffff] usable ==> reserved
[    0.169287] e820: reserve RAM buffer [mem 0x00091800-0x0009ffff]
[    0.169288] e820: reserve RAM buffer [mem 0xd2983000-0xd3ffffff]
[    0.169289] e820: reserve RAM buffer [mem 0xd2dba000-0xd3ffffff]
[    0.169289] e820: reserve RAM buffer [mem 0xd7eeb000-0xd7ffffff]
[    0.169289] e820: reserve RAM buffer [mem 0xd8760000-0xdbffffff]
[    0.169290] e820: reserve RAM buffer [mem 0xd8fae000-0xdbffffff]
[    0.169291] e820: reserve RAM buffer [mem 0xda719000-0xdbffffff]
[    0.169291] e820: reserve RAM buffer [mem 0xdbe12000-0xdbffffff]
[    0.169292] e820: reserve RAM buffer [mem 0x41ee00000-0x41fffffff]

ช่วงหน่วยความจำที่ขึ้นต้นด้วย "BIOS-e820" ได้อธิบายไว้ในตารางนั้น บรรทัดแรกจะบอกแหล่งที่มาของข้อมูลนี้อย่างชัดเจน รูปแบบที่แน่นอนของข้อมูลนี้ขึ้นอยู่กับเวอร์ชันเคอร์เนลของ Linux ไม่ว่าในกรณีใดคุณจะเห็นช่วงและประเภทในแต่ละรายการ แถวที่ขึ้นต้นด้วย "e820" (ไม่มีส่วน "BIOS-") เป็นการเปลี่ยนแปลงที่ตัวเคอร์เนลทำกับตาราง การใช้งานE820hอาจมีข้อบกพร่องหรืออาจมีการทับซ้อนกันระหว่างช่วงที่ได้รับในรายการต่างๆ เคอร์เนลทำการตรวจสอบที่จำเป็นและเปลี่ยนแปลงตาม ช่วงที่ถูกทำเครื่องหมายว่า "ใช้งานได้" ส่วนใหญ่จะใช้งานได้ฟรีโดยเคอร์เนลโดยมีข้อยกเว้นที่กล่าวถึงในข้อกำหนด ACPI และที่ทราบว่าเคอร์เนลนั้น การใช้งาน BIOS พีซีส่วนใหญ่ส่งคืนตัวบอกช่วงหน่วยความจำสูงสุด 128 ช่วง เคอร์เนล Linux เวอร์ชันเก่าสามารถรองรับช่วงหน่วยความจำได้สูงสุด 128 ช่วงดังนั้นรายการใด ๆ ที่ส่งคืนจากE820hช่วงที่ 128 จะถูกละเว้น เริ่มต้นด้วยเวอร์ชัน? ข้อ จำกัด นี้ผ่อนคลายลง สำหรับข้อมูลเพิ่มเติมโปรดดูชุดโปรแกรมแก้ไขเคอร์เนลที่ชื่อว่า "x86 boot: pass E820 memory map entries มากกว่า 128 รายการผ่านรายการข้อมูลการตั้งค่าที่เชื่อมโยง

ช่วงของประเภทusableและACPI data. ช่วงของประเภทreservedได้รับการสนับสนุนโดย DRAM DIMM หรือสับสำหรับ MMIO โดยซีพียูหรือเฟิร์มแวร์แพลตฟอร์ม ช่วงของประเภทACPI NVSได้รับการสนับสนุนโดยหน่วยความจำเฟิร์มแวร์ ช่วงอื่น ๆ ทั้งหมดไม่ได้ย้อนกลับโดยหน่วยความจำจริงเท่าที่เฟิร์มแวร์บอกได้ โปรดทราบว่าเฟิร์มแวร์อาจเลือกที่จะไม่แมป DRAM DIMM หรือ NVDIMM ที่ติดตั้งไว้ทั้งหมด กรณีนี้อาจเกิดขึ้นหากไม่รองรับการกำหนดค่าหน่วยความจำกายภาพตามที่เป็นอยู่หรือหากเฟิร์มแวร์ไม่สามารถรับข้อมูลจาก DIMM ที่ติดตั้งเนื่องจากปัญหาใน DIMM

คุณสามารถคำนวณจำนวนหน่วยความจำของ DRAM DIMM และ NVDIMM ที่ติดตั้งไว้ที่เฟิร์มแวร์ไปยังเคอร์เนลได้ ในระบบของฉันฉันได้ติดตั้ง DRAM DIMM ขนาด 16 GB ดังนั้นเว้นแต่ว่า DIMM บางตัวไม่ได้รับการติดตั้งอย่างถูกต้องทำงานไม่ถูกต้องข้อบกพร่องในเฟิร์มแวร์หรือไม่รองรับโดยแพลตฟอร์มหรือโปรเซสเซอร์ควรมีเคอร์เนลน้อยกว่า 16 GB เล็กน้อยเล็กน้อย

usableช่วงทั้งหมดรวมกันได้สูงสุด 0x3FA42B800 ไบต์ โปรดทราบว่าที่อยู่สุดท้ายของช่วงนั้นรวมอยู่ด้วยซึ่งหมายความว่าจะชี้ไปยังตำแหน่งไบต์ที่เป็นส่วนหนึ่งของช่วง จำนวน DIMM ที่ติดตั้งทางกายภาพทั้งหมดคือ 16 GB หรือ 0x400000000 ไบต์ ดังนั้นจำนวนหน่วยความจำที่ติดตั้งทั้งหมดที่ไม่พร้อมใช้งานสำหรับเคอร์เนลคือ 0x400000000 - 0x3FA42B800 หรือประมาณ 92 MB จากทั้งหมด 16 GB หน่วยความจำนี้ถ่ายโดยบางreservedช่วงและทุกACPI dataช่วง หากสถานที่บางแห่งใน DRAM DIMM หรือ NVDIMM ถูกกำหนดโดยเฟิร์มแวร์ของแพลตฟอร์มว่าไม่น่าเชื่อถือสถานที่เหล่านั้นจะถูกตัดออกเช่นreservedกัน

โปรดทราบว่าช่วง 0x000a0000-0x000fffff ไม่ได้อธิบายไว้ในE820แผนที่หน่วยความจำตามข้อกำหนด ACPI นี่คือพื้นที่หน่วยความจำด้านบน 640KB-1MB เคอร์เนลพิมพ์ข้อความที่แจ้งว่าได้ลบช่วงนี้ออกจากพื้นที่หน่วยความจำที่ใช้งานได้เพื่อรักษาความเข้ากันได้กับระบบโบราณ

ณ จุดนี้หน่วยความจำที่จะใช้เป็น MMIO สำหรับอุปกรณ์ PCIe ส่วนใหญ่ยังไม่ได้รับการจัดสรร โปรเซสเซอร์ของฉันรองรับพื้นที่แอดเดรสทางกายภาพ 39 บิตซึ่งหมายความว่าแอดเดรสระหว่าง 0 ถึง 2 ^ 39 สามารถใช้งานได้สำหรับการแมป จนถึงขณะนี้มีการจับคู่เฉพาะพื้นที่ด้านล่าง 16.5 GB ส่วนใหญ่กับบางสิ่ง โปรดทราบว่าช่วงนี้ยังมีช่องว่างที่ไม่ได้แมป เคอร์เนลสามารถใช้ช่องว่างเหล่านี้ (ไม่กี่ 100 MB) และพื้นที่ที่อยู่ทางกายภาพที่เหลือ (ประมาณ 495.5 GB) เพื่อจัดสรรช่วงที่อยู่สำหรับอุปกรณ์ IO ในที่สุดเคอร์เนลจะค้นพบอุปกรณ์ PCIe และสำหรับแต่ละอุปกรณ์จะพยายามโหลดไดรเวอร์ที่เข้ากันได้หากมี จากนั้นไดรเวอร์จะกำหนดจำนวนหน่วยความจำที่อุปกรณ์ต้องการและข้อ จำกัด ใด ๆ เกี่ยวกับที่อยู่หน่วยความจำที่กำหนดโดยอุปกรณ์และร้องขอจากเคอร์เนลเพื่อจัดสรรหน่วยความจำสำหรับอุปกรณ์และกำหนดค่าเป็นหน่วยความจำ MMIO ที่อุปกรณ์เป็นเจ้าของ sudo cat /proc/iomemคุณสามารถดูแผนที่ของหน่วยความจำสุดท้ายใช้คำสั่ง

มีสถานการณ์ที่คุณต้องการเปลี่ยนประเภทหน่วยความจำของช่วงหน่วยความจำที่มีอยู่ด้วยตนเอง (เช่นสำหรับการทดสอบ) สร้างช่วงใหม่ (เช่นสำหรับการจำลองหน่วยความจำแบบถาวรบน DRAMหรือหากเฟิร์มแวร์ไม่สามารถค้นพบทั้งหมด หน่วยความจำที่พร้อมใช้งานไม่ว่าด้วยเหตุผลใดก็ตาม) ลดจำนวนหน่วยความจำที่เคอร์เนลใช้งานได้ (เช่นเพื่อป้องกันไม่ให้ไฮเปอร์ไวเซอร์โลหะเปลือยใช้หน่วยความจำเกินขีด จำกัด และทำให้ส่วนที่เหลือพร้อมใช้งานสำหรับแขก) หรือแม้แต่แทนที่ตารางทั้งหมดที่ส่งคืนจากE820h. memและmemmapพารามิเตอร์เคอร์เนลสามารถนำมาใช้เพื่อวัตถุประสงค์ดังกล่าว เมื่อพารามิเตอร์เหล่านี้อย่างน้อยหนึ่งพารามิเตอร์ถูกระบุด้วยค่าที่ถูกต้องเคอร์เนลจะอ่านแผนผังหน่วยความจำที่ BIOS ให้มาก่อนและทำการเปลี่ยนแปลงตามนั้น เคอร์เนลจะพิมพ์แมปหน่วยความจำสุดท้ายเป็น "แผนที่ RAM จริงที่ผู้ใช้กำหนดเอง" ในบัฟเฟอร์วงแหวนข้อความเคอร์เนล คุณสามารถดูข้อความเหล่านี้โดยใช้dmesg | grep user:(แถวช่วงความจำแต่ละแถวเริ่มต้นด้วย "ผู้ใช้:") ข้อความเหล่านี้จะพิมพ์หลังจากข้อความ "BIOS-e820"

บนแพลตฟอร์ม x86 ที่บูตด้วยเฟิร์มแวร์ UEFI ที่รองรับ Compatibility Support Module (โปรดดูข้อกำหนด CSM สำหรับข้อมูลเพิ่มเติมซึ่งแยกจาก UEFI) E820hอินเทอร์เฟซโหมดจริงแบบเดิมได้รับการสนับสนุนและเคอร์เนล Linux โดยค่าเริ่มต้นยังคงใช้อยู่ หากเคอร์เนลทำงานบนแพลตฟอร์ม x86 ด้วย UEFI ที่ไม่รองรับ CSM E820hอินเทอร์เฟซอาจไม่ให้ช่วงหน่วยความจำทั้งหมดหรือใด ๆ อาจจำเป็นต้องใช้add_efi_memmapพารามิเตอร์เคอร์เนลบนแพลตฟอร์มดังกล่าว ตัวอย่างที่สามารถพบได้ที่UEFI หน่วยความจำ V E820 หน่วยความจำ เมื่อมีการจัดเตรียมช่วงหน่วยความจำตั้งแต่หนึ่งช่วงขึ้นGetMemoryMap()ไปเคอร์เนลจะรวมช่วงเหล่านี้กับช่วงจากE820hอินเทอร์เฟซ แผนที่ของหน่วยความจำที่เกิดสามารถดูได้โดยใช้dmesg | grep 'efi:'UEFI efi_fake_memที่เกี่ยวข้องกับพารามิเตอร์เคอร์เนลอีกที่มีผลต่อแผนที่หน่วยความจำ

ข้อกำหนด ACPI (ส่วน 6.3) จัดเตรียมกลไกการแจ้งเตือนเพื่อแจ้งเคอร์เนลเมื่ออุปกรณ์ IO หรือ DIMM ถูกแทรกหรือลบออกจากระบบในสถานะ S ใด ๆ (ฉันไม่รู้ว่ามีเมนบอร์ดที่รองรับการลบ DIMM ในสถานะ S ใด ๆ หรือไม่โดยปกติจะทำได้เฉพาะในสถานะ G3 และอาจเป็น S4 และ / หรือ S5) เมื่อเหตุการณ์ดังกล่าวเกิดขึ้นเคอร์เนลหรือ เฟิร์มแวร์ทำการเปลี่ยนแปลงแผนที่หน่วยความจำตามนั้น sudo cat /proc/iomemการเปลี่ยนแปลงเหล่านี้สะท้อนให้เห็นใน

mevets Jan 05 2021 at 10:13

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

.text
entry:
    call reloc  /* call is pc relative */
reloc:
    pop %r0     /* r0 now contains physical address of reloc */
    sub $reloc, %r0, %r14 /* r14 contains difference between link address of reloc */ /* At this point, r14 is a relocation register. A virtual address + r14 == the corresponding physical address. */ add $proot, %r14, %r0  /* physical address of page table root */
    add $entry, %r14, %r1 /* entry is where we were loaded into ram */ test $0xfff, %r1   /* someone is being funny and not page aligning us */
    jnz bad_alignment
    or   $0x7, %r1 /* put mythical page protection bits in r1 */ mov $1024, %r2     /* number of pages in r2 */
loop:
    store %r1, (%r0)   /* store a page table entry */
    add $0x1000, %r1 /* setup next one 4096 bytes farther */ add $4, %r0        /* point to next page table entry */
    sub $1, r2 /* are we done? */ cmp %0, r2 jne loop /* nope, setup next entry */ add $proot, %r14, %r0
    loadsysreg %r0, page_table_base_register
    mov $1, %r0 mov $v_entry, %r1
    loadsysreg %r0, page_table_enabled
    jmp %r1
v_entry:
        /* now we are virtually addressed */
    call main
1:  jmp 1b   /* main shouldn't return. */


.data
.align 12   /* 4096 byte pages */
proot:
.zero 4096
.text

เครื่องในตำนานนี้เรียบง่ายมากโดยมีตารางหน้าเดียวแบบแบนและเคอร์เนลเชื่อมโยงที่แอดเดรส 0 แต่สามารถเรียกใช้จากที่ใดก็ได้ใน 4M แรก (1024 * 4096) เครื่องจริงเป็นเพียงรุ่นที่มีรายละเอียดมากกว่านี้ โดยทั่วไปคุณไม่สามารถเชื่อถือได้แม้แต่ภาษาของระบบเช่นCจนกว่าคุณจะมีการตั้งค่าพื้นที่ที่อยู่เริ่มต้น เมื่อเป็นเช่นนั้นโค้ดในนั้นสามารถสร้างตารางเพจที่ซับซ้อนมากขึ้นและสืบค้นฐานข้อมูลเช่นโครงสร้างอุปกรณ์หรือแม้กระทั่งความชั่วร้ายเช่น apic / uefi สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเค้าโครง ram เป็นต้น

ในสถาปัตยกรรมตารางเพจที่แมปไปข้างหน้าโดยที่โหนดภายในอยู่ในรูปแบบที่เข้ากันได้กับโหนดลีฟ (ตัวอย่างเช่น x86-classic) คุณสามารถใช้ตารางเพจเดี่ยวแบบวนซ้ำเพื่อให้ที่อยู่ลิงก์ที่ยืดหยุ่นมากขึ้น ตัวอย่างเช่นหากคุณชี้รายการสุดท้ายใน proot (นั่นคือ proot [1023]) กลับไปที่ proot คุณสามารถเชื่อมโยงระบบปฏิบัติการของคุณที่ 0xffffc000 และโค้ดนี้จะใช้งานได้ (เมื่อแปลเป็น x86)