วงแหวน CPU เป็นความแตกต่างที่ชัดเจนที่สุด
ในโหมดที่ได้รับการป้องกัน x86 CPU จะอยู่ในหนึ่งใน 4 วงเสมอ เคอร์เนล Linux ใช้ 0 และ 3 เท่านั้น:
- 0 สำหรับเคอร์เนล
- 3 สำหรับผู้ใช้
นี่เป็นนิยามที่ยากและรวดเร็วที่สุดของ kernel vs userland
ทำไม Linux ไม่ใช้ริง 1 และ 2: https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used
วงแหวนปัจจุบันถูกกำหนดอย่างไร?
แหวนปัจจุบันถูกเลือกโดยการรวมกันของ:
ตารางตัวอธิบายทั่วโลก: ตารางในหน่วยความจำของรายการ GDT และแต่ละรายการมีเขตข้อมูลPrivl
ที่เข้ารหัสแหวน
คำสั่ง LGDT ตั้งค่าที่อยู่เป็นตารางตัวอธิบายปัจจุบัน
ดูเพิ่มเติมที่: http://wiki.osdev.org/Global_Descriptor_Table
ส่วนที่ลงทะเบียน CS, DS ฯลฯ ซึ่งชี้ไปที่ดัชนีของรายการใน GDT
ตัวอย่างเช่นCS = 0
หมายความว่ารายการแรกของ GDT ใช้งานได้ในปัจจุบันสำหรับรหัสดำเนินการ
แหวนแต่ละวงทำอะไรได้บ้าง?
ชิปซีพียูถูกสร้างขึ้นทางกายภาพเพื่อที่:
โปรแกรมและระบบปฏิบัติการมีการเปลี่ยนแปลงระหว่างวงแหวนอย่างไร
เมื่อซีพียูเปิดทำงานมันจะเริ่มรันโปรแกรมเริ่มต้นในริง 0 (แบบดี แต่มันเป็นการประมาณที่ดี) คุณสามารถคิดว่าโปรแกรมเริ่มต้นนี้เป็นเคอร์เนล (แต่โดยปกติจะเป็น bootloader ที่เรียกเคอร์เนลยังคงอยู่ในแหวน 0)
เมื่อกระบวนการของผู้ใช้ต้องการให้เคอร์เนลทำอะไรบางอย่างเพื่อให้มันเหมือนกับเขียนไฟล์มันใช้คำสั่งที่สร้างการขัดจังหวะเช่นint 0x80
หรือsyscall
ส่งสัญญาณเคอร์เนล x86-64 Linux syscall ตัวอย่าง Hello world:
.data
hello_world:
.ascii "hello world\n"
hello_world_len = . - hello_world
.text
.global _start
_start:
/* write */
mov $1, %rax
mov $1, %rdi
mov $hello_world, %rsi
mov $hello_world_len, %rdx
syscall
/* exit */
mov $60, %rax
mov $0, %rdi
syscall
รวบรวมและเรียกใช้:
as -o hello_world.o hello_world.S
ld -o hello_world.out hello_world.o
./hello_world.out
GitHub ต้นน้ำ
เมื่อสิ่งนี้เกิดขึ้น CPU จะเรียกตัวจัดการการติดต่อกลับขัดจังหวะซึ่งเคอร์เนลที่ลงทะเบียนในเวลาบูต นี่คือตัวอย่าง BareMetal คอนกรีตที่ลงทะเบียนจัดการและใช้มัน
ตัวจัดการนี้ทำงานในวงแหวน 0 ซึ่งตัดสินใจว่าเคอร์เนลจะอนุญาตการดำเนินการนี้ดำเนินการและรีสตาร์ทโปรแกรม userland ในริง 3 x86_64
เมื่อมีการexec
ใช้การเรียกระบบ (หรือเมื่อเคอร์เนลจะเริ่มทำงาน/init
) เคอร์เนลจะจัดเตรียมการลงทะเบียนและหน่วยความจำของกระบวนการ userland ใหม่จากนั้นจะข้ามไปที่จุดเริ่มต้นและสลับ CPU ไปยังวงแหวน 3
หากโปรแกรมพยายามทำสิ่งที่ซุกซนเช่นเขียนลงทะเบียนต้องห้ามหรือที่อยู่หน่วยความจำ (เพราะเพจจิ้ง) ซีพียูยังเรียกเคอร์เนลจัดการโทรกลับบางส่วนในแหวน 0
แต่เนื่องจาก userland ซนเคอร์เนลอาจฆ่ากระบวนการในเวลานี้หรือแจ้งเตือนด้วยสัญญาณ
เมื่อเคอร์เนลบูทจะทำการติดตั้งนาฬิกาฮาร์ดแวร์ด้วยความถี่คงที่ซึ่งสร้างอินเทอร์รัปต์เป็นระยะ
นาฬิกาฮาร์ดแวร์นี้สร้างการขัดจังหวะที่เรียกใช้วงแหวน 0 และอนุญาตให้กำหนดเวลาว่ากระบวนการ userland ใดที่จะปลุก
ด้วยวิธีนี้การกำหนดเวลาสามารถเกิดขึ้นได้แม้ว่ากระบวนการจะไม่ทำการเรียกระบบใด ๆ
อะไรคือจุดที่มีหลาย ๆ วง?
มีข้อดีสองประการที่สำคัญของการแยกเคอร์เนลและ userland:
- มันง่ายกว่าที่จะสร้างโปรแกรมเมื่อคุณมั่นใจว่าจะไม่เข้าไปยุ่งกับโปรแกรมอื่น เช่นกระบวนการ userland หนึ่งกระบวนการไม่ต้องกังวลเกี่ยวกับการเขียนทับหน่วยความจำของโปรแกรมอื่นเนื่องจากการเพจหรือการวางฮาร์ดแวร์ในสถานะที่ไม่ถูกต้องสำหรับกระบวนการอื่น
- ปลอดภัยยิ่งขึ้น เช่นการอนุญาตไฟล์และการแยกหน่วยความจำสามารถป้องกันไม่ให้แอปแฮ็คอ่านข้อมูลธนาคารของคุณ แน่นอนว่าสิ่งนี้ถือว่าคุณเชื่อถือเคอร์เนล
จะเล่นกับมันอย่างไร?
ฉันได้สร้างการตั้งค่าโลหะเปลือยที่ควรจะเป็นวิธีที่ดีในการจัดการแหวนโดยตรง: https://github.com/cirosantilli/x86-bare-metal-examples
ฉันไม่ได้มีความอดทนในการสร้างตัวอย่าง userland อย่างน่าเสียดาย แต่ฉันไปไกลถึงการตั้งค่าการเพจดังนั้น userland ควรเป็นไปได้ ฉันชอบที่จะเห็นคำขอดึง
อีกวิธีหนึ่งคือโมดูลเคอร์เนล Linux ทำงานในวงแหวน 0 ดังนั้นคุณสามารถใช้พวกเขาเพื่อลองการดำเนินการพิเศษเช่นอ่านการลงทะเบียนควบคุม: https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers -cr0-CR2-CR3 จาก-a-โปรแกรมรับ-กลุ่ม A / 7419306 # 7419306
นี่คือการตั้งค่า QEMU + Buildroot ที่สะดวกเพื่อลองใช้งานโดยไม่ต้องฆ่าโฮสต์ของคุณ
ข้อเสียของโมดูลเคอร์เนลคือ kthreads อื่นกำลังทำงานและอาจรบกวนการทดลองของคุณ แต่ในทางทฤษฎีคุณสามารถจัดการตัวจัดการขัดจังหวะทั้งหมดด้วยโมดูลเคอร์เนลของคุณและเป็นเจ้าของระบบซึ่งจะเป็นโครงการที่น่าสนใจจริง ๆ
แหวนเชิงลบ
ในขณะที่วงแหวนด้านลบนั้นไม่ได้อ้างอิงในคู่มือ Intel จริง ๆ แล้วมีโหมด CPU ที่มีความสามารถเพิ่มเติมนอกเหนือจากวงแหวน 0 นั้นจึงเหมาะสำหรับชื่อ "วงแหวนลบ"
ตัวอย่างหนึ่งคือโหมดไฮเปอร์ไวเซอร์ที่ใช้ในการจำลองเสมือน
สำหรับรายละเอียดเพิ่มเติมดูที่: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
แขน
ใน ARM วงแหวนจะถูกเรียกว่า Exception Levels แทน แต่ความคิดหลักยังคงเหมือนเดิม
มีข้อยกเว้น 4 ระดับใน ARMv8 ที่ใช้โดยทั่วไปดังนี้:
EL0: userland
EL1: เคอร์เนล ("ผู้ควบคุม" ในคำศัพท์ ARM)
ป้อนด้วยsvc
คำสั่ง (SuperVisor Call) ซึ่งก่อนหน้านี้รู้จักกันก่อนหน้านี้ว่าเป็นswi
แอสเซมบลีรวมซึ่งเป็นคำสั่งที่ใช้ในการโทรระบบ Linux สวัสดีชาวโลกตัวอย่าง ARMv8:
.text
.global _start
_start:
/* write */
mov x0, 1
ldr x1, =msg
ldr x2, =len
mov x8, 64
svc 0
/* exit */
mov x0, 0
mov x8, 93
svc 0
msg:
.ascii "hello syscall v8\n"
len = . - msg
GitHub ต้นน้ำ
ทดสอบกับ QEMU บน Ubuntu 16.04:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-as -o hello.o hello.S
arm-linux-gnueabihf-ld -o hello hello.o
qemu-arm hello
นี่เป็นตัวอย่าง BareMetal เป็นรูปธรรมที่ลงทะเบียนการจัดการ SVC และไม่รับสายเรียก
EL2: hypervisorsเช่นXen
เข้าไปพร้อมกับhvc
คำแนะนำ (HyperVisor Call)
ไฮเปอร์ไวเซอร์คือระบบปฏิบัติการระบบปฏิบัติการคืออะไรสำหรับผู้ใช้
ตัวอย่างเช่น Xen อนุญาตให้คุณเรียกใช้หลาย ๆ OS เช่น Linux หรือ Windows บนระบบเดียวกันในเวลาเดียวกันและมันแยก OS ต่างๆออกจากกันเพื่อความปลอดภัยและความสะดวกในการดีบักเช่นเดียวกับ Linux สำหรับโปรแกรมผู้ใช้
Hypervisors เป็นส่วนสำคัญของโครงสร้างพื้นฐานคลาวด์ในปัจจุบัน: อนุญาตให้เซิร์ฟเวอร์หลาย ๆ เครื่องทำงานบนฮาร์ดแวร์เดียวทำให้การใช้งานฮาร์ดแวร์ใกล้เคียงกับ 100% และประหยัดเงินเป็นจำนวนมาก
AWS เช่นใช้ Xen จนกระทั่ง 2017 เมื่อย้ายไป KVM ทำข่าว
EL3: อีกระดับหนึ่ง ตัวอย่างสิ่งที่ต้องทำ
เข้าสู่smc
คำสั่ง (การเรียกใช้โหมดปลอดภัย)
ARMv8 สถาปัตยกรรมแบบอ้างอิง DDI 0487C.a - บท D1 - ระดับ AArch64 ระบบโปรแกรมเมอร์รุ่น - รูปที่แสดงให้เห็นถึง D1-1 นี้สวยงาม:
สังเกตว่า ARM อาจจะเป็นเพราะประโยชน์ของการเข้าใจถึงปัญหาหลังเหตุการณ์มีการตั้งชื่อที่ดีกว่าสำหรับระดับสิทธิ์มากกว่า x86 โดยไม่จำเป็นต้องมีระดับลบ: 0 เป็นต่ำและ 3 สูงที่สุด ระดับที่สูงขึ้นมีแนวโน้มที่จะถูกสร้างขึ้นบ่อยกว่าระดับที่ต่ำกว่า
สามารถสอบถาม EL ปัจจุบันด้วยMRS
คำสั่ง: https://stackoverflow.com/questions/31787617/what-is-the-current-execution-mode-exception-level-etc
ARM ไม่จำเป็นต้องมีระดับการยกเว้นทั้งหมดเพื่อให้มีการใช้งานที่ไม่ต้องการคุณสมบัติในการบันทึกพื้นที่ชิป ARMv8 "ระดับการยกเว้น" พูดว่า:
การใช้งานอาจไม่รวมถึงระดับข้อยกเว้นทั้งหมด การใช้งานทั้งหมดจะต้องมี EL0 และ EL1 EL2 และ EL3 เป็นตัวเลือก
QEMU เช่นค่าเริ่มต้นเป็น EL1 แต่สามารถเปิดใช้งาน EL2 และ EL3 ด้วยตัวเลือกบรรทัดคำสั่ง: https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulating-a53-power-up
ตัวอย่างโค้ดทดสอบบน Ubuntu 18.10