ÇEKİRDEK / KOD + SINAMA DEFTERİ

Her iddianın
bir testi var.

Buradaki kod blokları gerçek çalışma alanından derleme sırasında alınır. Sol sütun uygulanan kaynak, sağ sütun assertion veya test hazırlığıdır. Her kayıtta çalıştırma komutu, beklenen çıktı ve kanıtlanmayan alan ayrıca yazılır.

TEK KOMUT / TAM ZİNCİR

Önce ortamı kur ve tamamını sınayalım.

verify.sh VM ve çekirdek birim testlerini çalıştırır, beş imajı üretir, 9 standart ve bir özel DTB konuk açılışını sınar; her biri için geçici JSON raporu üretip 10 raporun içeriğini denetler. Ardından siteyi derleyip bağlantıları kontrol eder. Aşağıdaki tekil QEMU komutları, bu imajlar ve target/debug/rustos-vm üretildikten sonra çalıştırılır.

sh scripts/verify.sh
ÇEKİRDEK / TEST KAYDI

01 / Image başlığı ve giriş sözleşmesi

QEMU, ARM64 Image başlığındaki metin ofsetinden girişe ulaşır. Bu test yalnızca başlık biçimini ve sanal makinede BOOT0 çıkışını kanıtlar; UEFI yükleyiciyi kanıtlamaz.

GERÇEK KAYNAK · kernel/src/boot.S
_image_start:
    b _start
    .word 0
    .quad 0x80000
    .quad _image_end - _image_start
    .quad 0x2
    .quad 0
    .quad 0
    .quad 0
    .word 0x644d5241
    .word 0

.section .text.boot,"ax"
GERÇEK TEST · scripts/test-kernel-boot.mjs
    assert.equal(image.readBigUInt64LE(8), 0x80000n, `${filename}: unexpected text offset`);
    assert.equal(image.readUInt32LE(56), 0x644d5241, `${filename}: ARM64 magic is missing`);
    assert.ok(image.readBigUInt64LE(16) >= BigInt(image.length), `${filename}: declared size is too small`);
YENİDEN ÇALIŞTIRsh scripts/build-kernel.sh && node scripts/test-kernel-boot.mjs
BEKLENEN GÖZLEM

text offset 0x80000; magic 0x644d5241; TURKOS/BOOT0: ARM64 kernel entered

Kapsam sınırı: Başlık denetimi gerçek Apple donanımında veya UEFI üzerinden açılış testi değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

02 / Seri port ve DTB keşfi

İlk BOOT0 mesajı sabit bootstrap PL011 adresinden çıkar. Sonra x0 ile gelen DTB okunur ve UART tabanı bulunan reg değerine geçirilir; test seri portu ve 256/512/1024 MiB RAM boyutunu denetler.

GERÇEK KAYNAK · kernel/src/main.rs
    let hardware = &boot.hardware;
    UART_BASE.store(hardware.uart_base as usize, Ordering::Relaxed);
    write_str("TURKOS/UART: 0x");
    write_hex(hardware.uart_base as usize);
    write_str("\r\nTURKOS/RAM: 0x");
    write_hex(hardware.ram_base as usize);
    write_str("+0x");
    write_hex(hardware.ram_size as usize);
    write_str("\r\n");
    write_str("TURKOS/GICD: 0x");
    write_hex(boot.interrupts.gic_distributor as usize);
GERÇEK TEST · scripts/test-kernel-boot.mjs
    assert.match(serial, /TURKOS\/UART: 0x0000000009000000/);
    assert.match(serial, /TURKOS\/GICR: 0x00000000080a0000/);
    const expectedRamSize = (BigInt(memoryMiB) * 1024n * 1024n).toString(16).padStart(16, '0');
    assert.ok(serial.includes(`TURKOS/RAM: 0x0000000040000000+0x${expectedRamSize}`));
    const freePages = serial.match(/TURKOS\/FREE-PAGES: 0x([0-9a-f]{16})/);
    assert.ok(freePages, `${filename}: free page count missing`);
    const freeCount = BigInt(`0x${freePages[1]}`);
    assert.ok(freeCount > 0n && freeCount < BigInt(memoryMiB) * 256n, `${filename}: impossible free page count`);
YENİDEN ÇALIŞTIRnode scripts/test-kernel-boot.mjs
BEKLENEN GÖZLEM

TURKOS/UART: 0x0000000009000000; TURKOS/RAM: 0x0000000040000000+0x…

Kapsam sınırı: Kök UART/RAM düğümleri için dar QEMU virt profili; genel aygıt ağacı veya bus çevirisi yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

03 / Korunan RAM serbest haritaya girmez

Çekirdek imajı, DTB blobu, FDT rezervasyon bloğu ve statik /reserved-memory aralıkları FreeMap içinden kesilir. Birim testi kısmi sayfaları da tam 4 KiB sayfa olarak korur.

GERÇEK KAYNAK · kernel/src/main.rs
fn plan_free_memory(boot: &BootData, dtb_address: usize) -> Result<memory::FreeMap, memory::Error> {
    let mut free = memory::FreeMap::from_ram(boot.hardware.ram_base, boot.hardware.ram_size)?;
    let kernel_start = core::ptr::addr_of!(_image_start) as u64;
    let kernel_end = core::ptr::addr_of!(_image_end) as u64;
    let kernel_size = kernel_end
        .checked_sub(kernel_start)
        .ok_or(memory::Error::Invalid)?;
    free.reserve(kernel_start, kernel_size)?;
    free.reserve(dtb_address as u64, boot.dtb_size as u64)?;
    for region in boot.reservations.as_slice() {
        free.reserve(region.start, region.size)?;
    }
    for region in boot.static_reserved.as_slice() {
        free.reserve(region.start, region.size)?;
    }
    Ok(free)
}

fn frame_smoke_test(free: memory::FreeMap) -> bool {
GERÇEK TEST · kernel/src/memory.rs
    fn excludes_kernel_and_dtb_pages() {
        let mut map = FreeMap::from_ram(0x4000_0000, 0x10000).unwrap();
        map.reserve(0x4000_1003, 0x1ffd).unwrap();
        map.reserve(0x4000_8000, 0x1000).unwrap();
        assert_eq!(map.total_pages(), 13);
        assert_eq!(
            map.regions(),
            &[
                Region {
                    start: 0x4000_0000,
                    end: 0x4000_1000
                },
                Region {
                    start: 0x4000_3000,
                    end: 0x4000_8000
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked
BEKLENEN GÖZLEM

excludes_kernel_and_dtb_pages … ok; korunmuş aralıklar hiçbir tahsiste dönmez

Kapsam sınırı: Dinamik DTB rezervasyonu ve çevrilen ranges bilinçli olarak reddedilir; firmware haritası henüz yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

04 / Özel DTB ile uçtan uca rezervasyon

Test, QEMU cihaz ağacına ilk dört RAM sayfasını kapsayan statik no-map havuzu ekler. Gerçek çekirdek açılışında ilk verilen çerçevenin 0x40004000 olduğu seri günlükte aranır.

GERÇEK KAYNAK · scripts/test-reserved-boot.mjs
  put('-t', 'x', dtb, '/reserved-memory/pool@40000000', 'reg', '0', '40000000', '0', '4000');
  put(dtb, '/reserved-memory/pool@40000000', 'no-map');
GERÇEK TEST · scripts/test-reserved-boot.mjs
  assert.match(serial, /TURKOS\/STATIC-RESERVATIONS: 0x0000000000000001/);
  assert.match(serial, /TURKOS\/FRAME-ADDRESS: 0x0000000040004000/);
  assert.match(serial, /TURKOS\/FRAME-COUNT: 0x0000000000000010/);
  assert.match(serial, /TURKOS\/FRAME-TEST: PASS/, `QEMU stderr: ${errorOutput}`);
  assert.doesNotMatch(serial, /TURKOS\/(DTB|MEM|FRAME)-ERROR/);
YENİDEN ÇALIŞTIRnode scripts/test-reserved-boot.mjs
BEKLENEN GÖZLEM

STATIC-RESERVATIONS: 0x…0001; FRAME-ADDRESS: 0x0000000040004000; FRAME-TEST: PASS

Kapsam sınırı: no-map şu anda yalnızca ayırıcıdan dışlanır; MMU eşleme kuralı henüz kurulmadı. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

05 / Tahsis, tükenme ve çift iade

FrameAllocator yalnızca izinli serbest haritadan 4 KiB çerçeve dağıtır. Birim testi korunmuş bölge iadesini, hizasız adresi, çift iadeyi ve tekrar tahsis sırasını denetler.

GERÇEK KAYNAK · kernel/src/memory.rs
    pub fn release(&mut self, address: u64) -> Result<(), Error> {
        if address & (PAGE_SIZE - 1) != 0 {
            return Err(Error::Invalid);
        }
        let end = address.checked_add(PAGE_SIZE).ok_or(Error::Invalid)?;
        if !self
            .allowed
            .regions()
            .iter()
            .any(|region| region.start <= address && end <= region.end)
        {
            return Err(Error::Invalid);
        }
        let index = self
            .free
            .regions()
            .iter()
            .position(|region| region.start >= address)
GERÇEK TEST · kernel/src/memory.rs
    fn release_coalesces_regions_and_rejects_protected_or_double_free() {
        let mut map = FreeMap::from_ram(0x4000_0000, 0x5000).unwrap();
        map.reserve(0x4000_4000, 0x1000).unwrap();
        let mut allocator = FrameAllocator::new(map);
        for _ in 0..4 {
            assert!(allocator.allocate().is_some());
        }
        assert_eq!(allocator.release(0x4000_4000), Err(Error::Invalid));
        assert_eq!(allocator.release(0x4000_0001), Err(Error::Invalid));
        allocator.release(0x4000_0000).unwrap();
        allocator.release(0x4000_2000).unwrap();
        allocator.release(0x4000_1000).unwrap();
        allocator.release(0x4000_3000).unwrap();
        assert_eq!(allocator.release(0x4000_2000), Err(Error::Invalid));
        assert_eq!(allocator.remaining_pages(), 4);
        for offset in 0..4 {
            assert_eq!(allocator.allocate(), Some(0x4000_0000 + offset * PAGE_SIZE));
        }
    }

    #[test]
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked memory::tests
BEKLENEN GÖZLEM

release_coalesces_regions_and_rejects_protected_or_double_free … ok

Kapsam sınırı: Ayırıcı tek CPU ve sabit bölge metaverisi ile sınırlı; eşzamanlı erişim ve MMU yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

06 / 16 sayfalık gerçek konuk yaz/oku/iade

Normal Image 16 çerçeveyi aynı anda ayırır; her ilk sözcüğü kaydedip işaret yazar, okur ve eski değeri geri koyar. Ardından sayfaları iade ederek başlangıç serbest sayfa sayısına dönüldüğünü kontrol eder.

GERÇEK KAYNAK · kernel/src/main.rs
fn frame_smoke_test(free: memory::FreeMap) -> bool {
    #[cfg(feature = "frame-stress-test")]
    const TEST_PAGES: usize = 128;
    #[cfg(not(feature = "frame-stress-test"))]
    const TEST_PAGES: usize = 16;
    let initial_pages = free.total_pages();
    let mut allocator = memory::FrameAllocator::new(free);
    let mut frames = [0; TEST_PAGES];
    for index in 0..TEST_PAGES {
        let Some(address) = allocator.allocate() else {
            return false;
        };
        if frames[..index].contains(&address) {
            return false;
        }
        frames[index] = address;
        // SAFETY: finalized free RAM excludes image, DTB and both reservation
        // sources. Direct QEMU Image boot uses an identity-mapped RAM view.
        unsafe {
            let first = address as *mut u64;
            let last =
                (address + memory::PAGE_SIZE - core::mem::size_of::<u64>() as u64) as *mut u64;
            let before_first = read_volatile(first);
            let before_last = read_volatile(last);
            let first_canary = 0x5455_524b_4f53_4652 ^ address;
            let last_canary = !first_canary;
            write_volatile(first, first_canary);
            write_volatile(last, last_canary);
            let passed = read_volatile(first) == first_canary && read_volatile(last) == last_canary;
            write_volatile(first, before_first);
            write_volatile(last, before_last);
            if !passed {
                write_str("TURKOS/FRAME-ERROR-ADDRESS: 0x");
                write_hex(address as usize);
                write_str("\r\n");
                return false;
            }
        }
    }
    let address = frames[0];
    write_str("TURKOS/FRAME-ADDRESS: 0x");
    write_hex(address as usize);
    write_str("\r\nTURKOS/FRAME-COUNT: 0x");
    write_hex(TEST_PAGES);
    write_str("\r\nTURKOS/FRAME-EDGE: PASS\r\n");
    for frame in frames.into_iter().rev() {
        if allocator.release(frame).is_err() {
GERÇEK TEST · scripts/test-kernel-boot.mjs
    assert.match(serial, /TURKOS\/FRAME-TEST: PASS/);
    assert.doesNotMatch(serial, /TURKOS\/FRAME-ERROR/);
    if (filename === 'ExceptionImage') assertFatalFrame(serial, filename, '3c', '544b42524b5f');
    if (filename === 'FaultImage') assertFatalFrame(serial, filename, '00', '544b5544465f');
YENİDEN ÇALIŞTIRnode scripts/test-kernel-boot.mjs
BEKLENEN GÖZLEM

FRAME-COUNT: 0x0000000000000010; FRAME-TEST: PASS; FRAME-ERROR yok

Kapsam sınırı: Bu 16 sayfalık erken boot yüküdür; büyük bellek baskısı veya çok CPU kanıtı değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

07 / BRK ve UDF ayrı hata sınıfları

İki ayrı test imajı x3 içine farklı işaret koyarak BRK ve UDF üretir. Vektör girişinde özgün x3 yığına alınır; test SLOT=0x04, EC ve x3 değerlerini tam eşleşmeyle arar.

GERÇEK KAYNAK · kernel/src/vectors.S
            sub sp, sp, #EXCEPTION_FRAME_SIZE
            str x3, [sp, #EXCEPTION_X3_OFFSET]
            mov x3, #vector_slot
            b __exception_entry
GERÇEK TEST · scripts/test-kernel-boot.mjs
  if (filename === 'ExceptionImage') expectedMarkers.push('X0=0x544b42524b5f5830', 'X3=0x544b42524b5f5833', 'X30=0x544b42524b5f4c52');
  if (filename === 'FaultImage') expectedMarkers.push('X0=0x544b5544465f5830', 'X3=0x544b5544465f5833', 'X30=0x544b5544465f4c52');
  const cli = (args) => execFileSync(binary, ['--vm-dir', vmDir, ...args], {
    cwd: root,
    encoding: 'utf8',
    timeout: 20_000,
YENİDEN ÇALIŞTIRsh scripts/build-kernel.sh --exception-test && sh scripts/build-kernel.sh --fault-test && node scripts/test-kernel-boot.mjs
BEKLENEN GÖZLEM

BRK: EC=0x3c, X3=0x544b42524b5f5833; UDF: EC=0x00, X3=0x544b5544465f5833

Kapsam sınırı: Fatal senkron yol sınandı; IRQ, EL2 konuk yolu, SIMD bağlam geri yükleme ve istisnadan dönüş yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

08 / Çift DTB reg alanını güvenli reddet

Aynı reserved-memory çocuk düğümündeki ikinci reg özelliği ilk korunan aralığı sessizce örtebiliyordu. Geçerli FDT biçimli karşı-örnek eklendi; ayrıştırıcı artık ikinci reg alanını Invalid ile reddediyor.

GERÇEK KAYNAK · kernel/src/dtb.rs
                            // A second property must not replace an earlier protected range.
                            if child_reg.is_some() {
                                return Err(Error::Invalid);
                            }
                            child_reg = Some(value);
                        }
GERÇEK TEST · kernel/src/dtb.rs
    fn rejects_duplicate_reserved_reg_property() {
        let first = [0x4001_0000u64.to_be_bytes(), 0x1000u64.to_be_bytes()].concat();
        let second = [0x4002_0000u64.to_be_bytes(), 0x1000u64.to_be_bytes()].concat();
        let mut blob = reserved_fixture(Some(&first), false, Some(&[]), false);
        let mut extra = Vec::new();
        prop(&mut extra, 27, &second);
        let insertion = blob
            .windows(first.len())
            .position(|window| window == first)
            .unwrap()
            + first.len();
        let added = extra.len() as u32;
        blob.splice(insertion..insertion, extra);
        let total = blob.len() as u32;
        blob[4..8].copy_from_slice(&total.to_be_bytes());
        let strings = be32(&blob, 12).unwrap() + added;
        blob[12..16].copy_from_slice(&strings.to_be_bytes());
        let structure_len = be32(&blob, 36).unwrap() + added;
        blob[36..40].copy_from_slice(&structure_len.to_be_bytes());
        assert_eq!(be32(&blob, insertion), Ok(3));
        assert_eq!(be32(&blob, insertion + 4), Ok(16));
        assert!(
            discover(&blob).is_ok(),
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked dtb::tests
BEKLENEN GÖZLEM

rejects_duplicate_reserved_reg_property … ok; sıfır uzunluk ve kesik ikinci aralık testleri de geçer

Kapsam sınırı: Genel FDT uyumluluğu değil; desteklenmeyen dinamik/çevrilen rezervasyonlar hâlâ kapalı reddedilir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

09 / 4.096 adımlık çerçeve sahipliği

Deterministik ayır–iade dizisi, korunan sayfalardan tahsis yapılmamasını ve serbest+eldeki sayfa toplamının her adımda sabit kalmasını doğrular. Bölge kapasitesi aşılırsa FreeMap değişmeden kalır.

GERÇEK KAYNAK · kernel/src/memory.rs
    pub fn release(&mut self, address: u64) -> Result<(), Error> {
        if address & (PAGE_SIZE - 1) != 0 {
            return Err(Error::Invalid);
        }
        let end = address.checked_add(PAGE_SIZE).ok_or(Error::Invalid)?;
        if !self
            .allowed
            .regions()
            .iter()
            .any(|region| region.start <= address && end <= region.end)
        {
            return Err(Error::Invalid);
        }
        let index = self
            .free
            .regions()
            .iter()
            .position(|region| region.start >= address)
GERÇEK TEST · kernel/src/memory.rs
    fn deterministic_fragmentation_preserves_page_ownership() {
        const PAGES: usize = 96;
        const BASE: u64 = 0x4000_0000;
        let mut map = FreeMap::from_ram(BASE, PAGES as u64 * PAGE_SIZE).unwrap();
        map.reserve(BASE + 5 * PAGE_SIZE, 2 * PAGE_SIZE).unwrap();
        map.reserve(BASE + 20 * PAGE_SIZE, 4 * PAGE_SIZE).unwrap();
        map.reserve(BASE + 50 * PAGE_SIZE, PAGE_SIZE).unwrap();
        let allowed = map.total_pages();
        assert_eq!(allowed, 89);
        let mut allocator = FrameAllocator::new(map);
        let mut held = [false; PAGES];
        let mut held_count = 0u64;
        let mut seed = 0x5455_524b_4f53u64;

        for _ in 0..4096 {
            seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
            if (seed & 3 == 0 && held_count > 0) || allocator.remaining_pages() == 0 {
                let mut index = seed as usize % PAGES;
                while !held[index] {
                    index = (index + 1) % PAGES;
                }
                allocator.release(BASE + index as u64 * PAGE_SIZE).unwrap();
                held[index] = false;
                held_count -= 1;
            } else {
                let address = allocator.allocate().unwrap();
                let index = ((address - BASE) / PAGE_SIZE) as usize;
                assert!(index < PAGES && !held[index]);
                assert!(!((5..7).contains(&index) || (20..24).contains(&index) || index == 50));
                held[index] = true;
                held_count += 1;
            }
            assert_eq!(allocator.remaining_pages() + held_count, allowed);
        }
        for (index, occupied) in held.iter().enumerate() {
            if *occupied {
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked memory::tests
BEKLENEN GÖZLEM

deterministic_fragmentation_preserves_page_ownership … ok; reserve_capacity_error_leaves_the_free_map_unchanged … ok

Kapsam sınırı: Tek CPU ve 96 sayfalık sentetik model; gerçek çok-CPU yarış koşulu veya MMU sınaması değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

10 / Tarihsel 64 çerçeveden 128 çerçeveye

H04 aşamasında StressImage 64 ilk-sözcük sınamasını geçti. E04 ile aynı ayrı imaj 128 çerçevenin ilk ve son sözcüğünü sınar; normal Image 16 sayfada kalır.

GERÇEK KAYNAK · kernel/src/main.rs
    #[cfg(feature = "frame-stress-test")]
    const TEST_PAGES: usize = 128;
    #[cfg(not(feature = "frame-stress-test"))]
    const TEST_PAGES: usize = 16;
    let initial_pages = free.total_pages();
    let mut allocator = memory::FrameAllocator::new(free);
    let mut frames = [0; TEST_PAGES];
    for index in 0..TEST_PAGES {
        let Some(address) = allocator.allocate() else {
            return false;
        };
        if frames[..index].contains(&address) {
GERÇEK TEST · scripts/test-kernel-boot.mjs
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 256, 128);
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 512, 128);
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 1024, 128);
YENİDEN ÇALIŞTIRsh scripts/verify.sh
BEKLENEN GÖZLEM

Güncel StressImage 256/512/1024 MiB; FRAME-COUNT: 0x0000000000000080; FRAME-EDGE: PASS

Kapsam sınırı: 64 sayfalık eski ölçüm tarihsel; güncel iki uç kanaryası bile tüm sayfa/RAM veya MMU testi değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

11 / Rust–assembly fatal çerçeve sözleşmesi

Rust ExceptionFrame boyutu ve kesilen SP ofseti derleme zamanında doğrulanır; assembly boyut, x3 ve SP ofsetlerini Rust yerleşiminden alır. BRK/UDF imajları kesilen x0, x3 ve x30 için ayrı işaretler üretir.

GERÇEK KAYNAK · kernel/src/main.rs
const _: () = {
    assert!(size_of::<usize>() == 8);
    assert!(size_of::<ExceptionFrame>() == 256);
    assert!(offset_of!(ExceptionFrame, registers) == 0);
    assert!(offset_of!(ExceptionFrame, interrupted_sp) == 248);
};
GERÇEK TEST · scripts/test-kernel-boot.mjs
function assertFatalFrame(serial, filename, expectedEc, markerPrefix) {
  const frame = serial.match(/TURKOS\/EXCEPTION: EL([12]) SLOT=0x04 EC=0x([0-9a-f]{2}) ESR=0x([0-9a-f]{16}) ELR=0x([0-9a-f]{16}) KIND=SYNC SP=0x([0-9a-f]{16}) SPSR=0x([0-9a-f]{16}) X0=0x([0-9a-f]{16}) X3=0x([0-9a-f]{16}) X30=0x([0-9a-f]{16})/);
  assert.ok(frame, `${filename}: fatal frame missing or incomplete`);
  assert.equal(frame[2], expectedEc, `${filename}: wrong exception class`);
  assert.notEqual(BigInt(`0x${frame[4]}`), 0n, `${filename}: empty interrupted PC`);
  assert.notEqual(BigInt(`0x${frame[5]}`), 0n, `${filename}: empty interrupted SP`);
  assert.equal(BigInt(`0x${frame[5]}`) % 16n, 0n, `${filename}: unaligned interrupted SP`);
  assert.equal(frame[7], `${markerPrefix}5830`, `${filename}: interrupted x0 was not saved`);
  assert.equal(frame[8], `${markerPrefix}5833`, `${filename}: interrupted x3 was not saved`);
  assert.equal(frame[9], `${markerPrefix}4c52`, `${filename}: interrupted x30 was not saved`);
}

async function testImage(filename, requiredMarkers, memoryMiB = 512, frameCount = 16) {
YENİDEN ÇALIŞTIRsh scripts/verify.sh
BEKLENEN GÖZLEM

BRK/UDF: x0, x3, x30 işaretleri tam eşleşir; kesilen SP 16 bayt hizalıdır; EC=0x3c/0x00

Kapsam sınırı: Yalnız fatal EL1 senkron yolları ölçüldü; IRQ, EL2, SIMD bağlamı ve istisnadan dönüş yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

12 / Her açılışın makinece okunur kanıtı

Dokuz standart ve bir özel DTB açılışı, imaj SHA-256, virt-11.1 profili, RAM, beklenen/görülen seri işaretleri ve PASS/FAIL durumuyla ayrı JSON raporuna yazılır. Hata durumunda seri günlüğü test VM’i silinmeden önce kopyalanır.

GERÇEK KAYNAK · scripts/boot-report.mjs
export function writeBootReport({ scenario, imagePath, dtbPath, memoryMiB, machine, expectedMarkers, serialPath, status, error, directory = bootReportDirectory() }) {
  if (!/^[A-Za-z0-9_-]+$/.test(scenario)) throw new Error(`Invalid boot-report scenario: ${scenario}`);
  mkdirSync(directory, { recursive: true });
  const serial = serialPath && existsSync(serialPath) ? readFileSync(serialPath, 'utf8') : '';
  const report = {
    schemaVersion: 1,
    scenario,
    image: imagePath,
    imageSha256: digest(imagePath),
    dtbSha256: dtbPath ? digest(dtbPath) : null,
    machine,
    memoryMiB,
    expectedMarkers,
    observedMarkers: expectedMarkers.filter((marker) => serial.includes(marker)),
    serialSha256: serialPath ? digest(serialPath) : null,
    status,
    error: error ? String(error.message ?? error) : null,
    failureSerial: null,
  };
GERÇEK TEST · scripts/check-boot-reports.mjs
assert.equal(files.length, expectedCount, 'one JSON report is required per QEMU boot');
const scenarios = new Set();
for (const name of files) {
  const report = JSON.parse(readFileSync(join(directory, name), 'utf8'));
  assert.equal(report.schemaVersion, 1);
  assert.equal(name, `${report.scenario}.json`);
  assert.ok(!scenarios.has(report.scenario), `duplicate scenario ${report.scenario}`);
  scenarios.add(report.scenario);
  assert.equal(report.status, 'pass', `${name}: boot failed: ${report.error}`);
  assert.equal(report.machine, 'virt-11.1');
  assert.ok(Number.isInteger(report.memoryMiB) && report.memoryMiB >= 256);
  assert.match(report.imageSha256, /^[0-9a-f]{64}$/);
  const currentHash = createHash('sha256').update(readFileSync(report.image)).digest('hex');
  assert.equal(report.imageSha256, currentHash, `${name}: image changed after the test`);
  assert.match(report.serialSha256, /^[0-9a-f]{64}$/);
  assert.ok(report.expectedMarkers.includes('TURKOS/FRAME-TEST: PASS'));
YENİDEN ÇALIŞTIRsh scripts/verify.sh
BEKLENEN GÖZLEM

10 JSON raporu; her beklenen işaret görüldü; başarısızlık fikstüründe *.serial.log korundu

Kapsam sınırı: Raporlar yerel/geçici test artefaktıdır; imzalı sürüm kanıtı veya genel kullanım imajı değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

13 / İkinci RAM bankasını sessizce kaybetme

Hardware tek RAM aralığı taşıdığı için ikinci kök reg bankası güvenli biçimde Unsupported olur; kesik ikinci bankaysa Invalid. Bu, birden çok bankayı destekleme iddiası değil, veri kaybını önleme kapısıdır.

GERÇEK KAYNAK · kernel/src/dtb.rs
                            if ram.is_some() {
                                return Err(Error::Unsupported);
                            }
                            let pair_len = (address_cells + size_cells) * 4;
                            if bytes.len() > pair_len && bytes.len() % pair_len == 0 {
                                return Err(Error::Unsupported);
                            }
                            let (base, size) = reg(bytes, address_cells, size_cells)?;
                            if size == 0 || base.checked_add(size).is_none() {
                                return Err(Error::Invalid);
                            }
                            ram = Some((base, size));
GERÇEK TEST · kernel/src/dtb.rs
    fn rejects_second_ram_range_in_one_reg() {
        let mut blob = fixture();
        let name = b"memory@40000000\0";
        let reg_token = blob.windows(name.len()).position(|w| w == name).unwrap() + name.len();
        let reg_offset = reg_token + 12;
        let second = [0x5000_0000u64.to_be_bytes(), 0x1000_0000u64.to_be_bytes()].concat();
        blob.splice(reg_offset + 16..reg_offset + 16, second);
        // A second bank is not supported by the single-range Hardware contract.
        let total = blob.len() as u32;
        blob[4..8].copy_from_slice(&total.to_be_bytes());
        let strings = be32(&blob, 12).unwrap() + 16;
        blob[12..16].copy_from_slice(&strings.to_be_bytes());
        let structure = be32(&blob, 36).unwrap() + 16;
        blob[36..40].copy_from_slice(&structure.to_be_bytes());
        let len_offset = reg_token + 4;
        blob[len_offset..len_offset + 4].copy_from_slice(&32u32.to_be_bytes());
        assert_eq!(discover(&blob), Err(Error::Unsupported));
        // A malformed trailing bank must be rejected as invalid, not ignored.
        let mut truncated = fixture();
        let reg_token = truncated
            .windows(name.len())
            .position(|w| w == name)
            .unwrap()
            + name.len();
        let reg_offset = reg_token + 12;
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked dtb::tests
BEKLENEN GÖZLEM

rejects_second_ram_range_in_one_reg … ok; ikinci bankanın ilk bankaya indirgenmesi yok

Kapsam sınırı: Çoklu RAM tahsisi hâlâ yok; tanınmayan topoloji açılışı kapalı reddedilir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

14 / 128 çerçeve ve iki uç kanaryası

Ayrı StressImage 128 benzersiz çerçeveyi eşzamanlı ayırır; her sayfanın ilk ve son 64-bit sözcüğünü yaz/oku/geri koy yapar, tüm çerçeveleri iade eder ve serbest sayfa toplamına döner.

GERÇEK KAYNAK · kernel/src/main.rs
    const TEST_PAGES: usize = 128;
    #[cfg(not(feature = "frame-stress-test"))]
    const TEST_PAGES: usize = 16;
    let initial_pages = free.total_pages();
    let mut allocator = memory::FrameAllocator::new(free);
    let mut frames = [0; TEST_PAGES];
    for index in 0..TEST_PAGES {
        let Some(address) = allocator.allocate() else {
            return false;
        };
        if frames[..index].contains(&address) {
            return false;
        }
        frames[index] = address;
        // SAFETY: finalized free RAM excludes image, DTB and both reservation
        // sources. Direct QEMU Image boot uses an identity-mapped RAM view.
        unsafe {
            let first = address as *mut u64;
            let last =
                (address + memory::PAGE_SIZE - core::mem::size_of::<u64>() as u64) as *mut u64;
            let before_first = read_volatile(first);
            let before_last = read_volatile(last);
            let first_canary = 0x5455_524b_4f53_4652 ^ address;
            let last_canary = !first_canary;
            write_volatile(first, first_canary);
            write_volatile(last, last_canary);
            let passed = read_volatile(first) == first_canary && read_volatile(last) == last_canary;
            write_volatile(first, before_first);
            write_volatile(last, before_last);
GERÇEK TEST · scripts/test-kernel-boot.mjs
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 256, 128);
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 512, 128);
await testImage('StressImage', ['TURKOS/BOOT0: ARM64 kernel entered'], 1024, 128);
YENİDEN ÇALIŞTIRsh scripts/verify.sh
BEKLENEN GÖZLEM

256/512/1024 MiB: FRAME-COUNT=0x80; FRAME-EDGE: PASS; FRAME-TEST: PASS

Kapsam sınırı: Sayfanın tamamı değil iki sınır sözcüğü sınanır; tek CPU ve kimlik eşlemeli erken açılıştır. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

15 / Açık onaylı Image reseti

Reset komutu önce veri kaybı onayı ister. QMP system_reset aynı çalışan Image konuğuna iki kez gönderilir; üç BOOT0 görülür ve ikinci QEMU sahibi kilitte reddedilir. GUI de uyarı ve ayrı onay gösterir.

GERÇEK KAYNAK · src/lib.rs
fn reset_kernel(root: &Path, name: &str) -> Result<()> {
    let dir = vm_path(root, name)?;
    if !matches!(read_config(&dir)?.boot, Boot::Kernel { .. }) {
        bail!(
            "Yeniden başlatma yalnız Image VM'lerinde destekleniyor; UEFI konuğunu sıfırlamıyoruz"
        );
    }
    qmp_command(&dir.join("qmp.sock"), "system_reset")
        .with_context(|| format!("VM çalışmıyor veya QMP kopuk: {name}"))?;
    Ok(())
}
GERÇEK TEST · scripts/test-kernel-boot.mjs
      for (const bootCount of [2, 3]) {
        cli(['reset', vmName, '--acknowledge-data-loss']);
        const resetDeadline = Date.now() + 12_000;
        while (Date.now() < resetDeadline) {
          serial = readFileSync(logPath, 'utf8');
          if ((serial.match(/TURKOS\/BOOT0: ARM64 kernel entered/g) ?? []).length >= bootCount
            && (serial.match(/TURKOS\/FRAME-TEST: PASS/g) ?? []).length >= bootCount) break;
          await delay(100);
        }
        assert.equal((serial.match(/TURKOS\/BOOT0: ARM64 kernel entered/g) ?? []).length, bootCount, `reset ${bootCount - 1}: missing fresh BOOT0`);
      }
      const secondStart = spawnSync(binary, ['--vm-dir', vmDir, 'start', vmName], {
        cwd: root, encoding: 'utf8', timeout: 20_000,
      });
      assert.notEqual(secondStart.status, 0, 'second start unexpectedly succeeded');
YENİDEN ÇALIŞTIRcargo test --locked && node scripts/test-kernel-boot.mjs && node scripts/test-gui-macos.mjs
BEKLENEN GÖZLEM

İki yeni BOOT0; tek QEMU; GUI uyarı/onay ve CLI running eşleşmesi

Kapsam sınırı: UEFI/Alpine reseti kapalı; reset temiz kapanma veya kalıcı disk bütünlüğü garantisi değildir. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

16 / MMU açılmadan eşleme sahipliği

Saf planlayıcı RAM ile MMIO için ayrı nitelik ve izinler tutar. Hizasız, taşan, çakışan, korunan RAM veya tablo/veri çerçevesi çakışması ve çalıştırılabilir MMIO reddedilir; tablo çerçeveleri plan bırakılınca iade edilir.

GERÇEK KAYNAK · kernel/src/paging.rs
    pub fn map(&mut self, mapping: Mapping) -> Result<(), Error> {
        if mapping.pages == 0
            || mapping.virtual_start % PAGE_SIZE != 0
            || mapping.physical_start % PAGE_SIZE != 0
        {
            return Err(Error::Invalid);
        }
        if mapping.kind == Kind::Mmio && mapping.access == Access::ReadExecute {
            return Err(Error::UnsafeAttribute);
        }
        let size = mapping.pages.checked_mul(PAGE_SIZE).ok_or(Error::Invalid)?;
        let virtual_end = mapping
            .virtual_start
            .checked_add(size)
            .ok_or(Error::Invalid)?;
        let physical_end = mapping
            .physical_start
            .checked_add(size)
            .ok_or(Error::Invalid)?;
        if mapping.kind == Kind::Ram
            && !self
                .allowed_ram
                .regions()
                .iter()
                .any(|region| region.start <= mapping.physical_start && physical_end <= region.end)
        {
GERÇEK TEST · kernel/src/paging.rs
    fn out_of_frames_does_not_add_partial_mapping() {
        let (mut frames, allowed) = allocator(2);
        let mut plan = Plan::new(&mut frames, &allowed).unwrap();
        assert_eq!(
            plan.map(Mapping {
                kind: Kind::Mmio,
                physical_start: 0x0900_0000,
                ..ram(0x4000_0000, 513)
            }),
            Err(Error::OutOfFrames)
        );
        assert_eq!(plan.table_count(), 0);
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked paging::tests
BEKLENEN GÖZLEM

7 paging testi; hata sonrası eşleme yok ve sayfa sayısı değişmez

Kapsam sınırı: İzinli FreeMap dışı RAM reddedilir; çekirdek imajını eşleyecek ayrı yetki kuralı, donanım descriptor, TTBR/SCTLR ve MMU etkinleştirme yok. İşlem gerekçesi ↗

ÇEKİRDEK / TEST KAYDI

17 / GICv3 ve timer keşfi, IRQ kapalı

QEMU virt DTB kökünden GIC distributor/redistributor ve non-secure fiziksel timer PPI okunur. Bilinmeyen GIC ve aralık dışı PPI reddedilir; seri günlüğü IRQ-MASK: ON gösterir.

GERÇEK KAYNAK · kernel/src/dtb.rs
pub fn discover_interrupts(blob: &[u8]) -> Result<InterruptHardware, Error> {
    let size = total_size(blob)?;
    if blob.len() < size || be32(blob, 20)? < 17 || be32(blob, 24)? > 17 {
        return Err(Error::Invalid);
    }
    let blob = &blob[..size];
    let structure = section(blob, be32(blob, 8)? as usize, be32(blob, 36)? as usize)?;
    let strings = section(blob, be32(blob, 12)? as usize, be32(blob, 32)? as usize)?;
    let mut cursor = 0;
    let mut depth = 0;
    let mut address_cells = 2;
    let mut size_cells = 1;
    let mut gic_node = false;
    let mut timer_node = false;
    let mut compatible: Option<&[u8]> = None;
    let mut node_reg: Option<&[u8]> = None;
    let mut interrupts: Option<&[u8]> = None;
    let mut interrupt_cells = None;
    let mut controller = false;
    let mut disabled = false;
    let mut gic = None;
    let mut timer = None;
GERÇEK TEST · kernel/src/dtb.rs
    fn rejects_unknown_gic_and_out_of_range_timer_ppi() {
        assert_eq!(
            discover_interrupts(&irq_fixture(14, b"arm,gic-v2\0")),
            Err(Error::Unsupported)
        );
        assert_eq!(
            discover_interrupts(&irq_fixture(16, b"arm,gic-v3\0")),
            Err(Error::Unsupported)
YENİDEN ÇALIŞTIRcargo test --manifest-path kernel/Cargo.toml --lib --locked && node scripts/test-kernel-boot.mjs
BEKLENEN GÖZLEM

GICD=0x08000000; GICR=0x080a0000; timer PPI=30; IRQ-MASK: ON

Kapsam sınırı: irq.rs yalnız ABI taslağıdır; IRQ vektöründen geri dönüş, ayrı yığın kullanımı ve timer kesmesi henüz yok. İşlem gerekçesi ↗

40 / 40 ÇEKİRDEK BİRİM TESTİ

Test adlarının tamamı

Bu liste kaynak dosyalarındaki #[test] işaretlerinden derleme sırasında okunur. Birim testleri QEMU açılışının yerine geçmez; DTB, bellek, eşleme planı ve IRQ ABI davranışını izole eder.

DTB · 21 test

  1. discovers_gicv3_and_nonsecure_physical_timer_without_enabling_irq
  2. rejects_unknown_gic_and_out_of_range_timer_ppi
  3. finds_qemu_profile
  4. rejects_second_ram_range_in_one_reg
  5. rejects_second_root_memory_node
  6. rejects_short_and_bad_magic
  7. rejects_out_of_bounds_structure
  8. rejects_truncated_and_oversized_blob
  9. reads_empty_reservation_map
  10. reads_reservation_and_rejects_overlap
  11. rejects_unterminated_reservation_map
  12. rejects_unaligned_and_overflowing_reservations
  13. reads_static_reserved_memory_node
  14. excludes_each_static_range_from_free_pages
  15. rejects_missing_malformed_and_overflowing_static_ranges
  16. rejects_zero_length_and_truncated_second_static_range
  17. accepts_adjacent_static_ranges_without_merging_them
  18. rejects_duplicate_reserved_reg_property
  19. rejects_overlapping_static_ranges
  20. rejects_dynamic_or_translated_reserved_memory
  21. ignores_disabled_reserved_memory

Bellek · 10 test

  1. excludes_kernel_and_dtb_pages
  2. overlapping_reservations_do_not_reintroduce_pages
  3. reserve_map_and_static_reservation_can_overlap
  4. aligns_ram_inward_and_rejects_overflow
  5. allocates_only_unreserved_pages_and_reports_exhaustion
  6. release_coalesces_regions_and_rejects_protected_or_double_free
  7. fragmentation_limit_does_not_lose_a_frame
  8. repeated_multi_frame_cycles_preserve_all_pages
  9. deterministic_fragmentation_preserves_page_ownership
  10. reserve_capacity_error_leaves_the_free_map_unchanged

MMU önkoşulu · 7 test

  1. owns_and_releases_all_table_frames
  2. ram_and_mmio_have_distinct_attributes
  3. invalid_or_overlapping_map_does_not_change_ownership
  4. out_of_frames_does_not_add_partial_mapping
  5. protected_ram_target_is_rejected_without_frame_loss
  6. rejects_table_frame_as_data_target_without_leak
  7. new_table_frame_cannot_alias_prior_data_mapping

IRQ ABI önkoşulu · 2 test

  1. irq_context_reserves_all_general_and_simd_register_slots
  2. independent_irq_stack_contract_is_aligned_and_bounded

cargo test --manifest-path kernel/Cargo.toml --lib --locked

10 / 10 SANAL AÇILIŞ

Hangi imaj, hangi gözlem?

Image · 256 / 512 / 1024 MiB

BOOT0, DTB UART/RAM/GIC/timer, 16 çerçeve ve FRAME-TEST: PASS.

StressImage · 256 / 512 / 1024 MiB

128 benzersiz çerçeve; FRAME-COUNT=0x80, FRAME-EDGE: PASS ve FRAME-TEST: PASS.

PanicImage · 512 MiB

Normal bellek testinden sonra TURKOS/PANIC.

ExceptionImage · 512 MiB

BRK, SLOT=0x04, EC=0x3c ve korunan X0/X3/X30.

FaultImage · 512 MiB

UDF, SLOT=0x04, EC=0x00 ve farklı X0/X3/X30.

Image + özel DTB · 512 MiB

İlk dört sayfa atlanır; FRAME-ADDRESS=0x40004000.

Image 512 MiB senaryosu iki açık onaylı QMP resetiyle üç BOOT0 üretir. 9 standart senaryo scripts/test-kernel-boot.mjs, özel DTB senaryosu scripts/test-reserved-boot.mjs içindedir. Her açılışın imaj kimliği ve seri gözlemi ayrı JSON raporunda tutulur; VM'ler test sonunda kapatılır. GUI kabulü ayrı macOS testiyle ölçülür; gerçek Mac üzerinde doğrudan çekirdek açılışı bu matrise dahil değildir.

SONRAKİ KANIT KAPISI

Geçmeyen katmanı geçmiş gibi göstermiyoruz.

UEFI çekirdek yükleyicisi, MMU, IRQ, GIC/zamanlayıcı, EL0 görevleri, virtio blok sürücüsü ve gerçek Apple donanımı açılışı bu kayıtta PASS değildir. Yeni bir özellik eklendiğinde aynı kalıp kullanılacak: kaynak satırı → test kodu → komut → beklenen çıktı → açık sınır.

Çekirdek planı ↗Aşamaların gerekçesi ↗