Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comments for "https://os.phil-opp.com/paging-implementation/" #570

Closed
utterances-bot opened this issue Mar 15, 2019 · 81 comments
Closed

Comments for "https://os.phil-opp.com/paging-implementation/" #570

utterances-bot opened this issue Mar 15, 2019 · 81 comments

Comments

@utterances-bot
Copy link

utterances-bot commented Mar 15, 2019

This is a general purpose comment thread for the Paging Implementation post.

Copy link

The bootloader and x86_64 changelog links are swapped.

@phil-opp phil-opp changed the title https://os.phil-opp.com/paging-implementation/ Comments for "https://os.phil-opp.com/paging-implementation/" Mar 15, 2019
@phil-opp
Copy link
Owner

@lilyball Thanks! Fixed in cc0f684.

Copy link

DanGdl commented Mar 15, 2019

This paging approach seems much clear and easy to understand

@phil-opp
Copy link
Owner

@DanGdl Great to hear that!

Copy link

pitust commented Mar 17, 2019

That's better than last time (I hope). Last time the code didn't compile.

@phil-opp
Copy link
Owner

@pitust Please open an issue when you have any problems with compiling the code. You can also find the complete code for the post in the post-10 branch. This branch is automatically tested on every new nightly using different continuous integration services, so it should always compile without problems.

Copy link

songzhi commented Mar 20, 2019

If I use the 'map_physical_memory' feature,and I want to create a new level_4_page_table to be switched to when I create a new process.Do I need to map the whole physical memory in the new page table manually just like what you did in 'bootloader' crate?Actually I found that snippet of code,but I am not really sure should I type it again in my code.

@phil-opp
Copy link
Owner

@songzhi If you want that your new process also has access to the physical memory then you need to add a mapping for it in its address space. As an optimization you can link the level 3 or level 2 page table from your current address space to the new address space instead of copying them, so that both address spaces share the same level 3/level 2 table.

@krsoninikhil
Copy link

For anyone who is updating x86_64 crate from 0.4.0 to required 0.5.2 and getting error like try_from not being stable, you also need to update your rustc version (rustup update). After this, you'll also be required to update your code as per new version of x86_64 which will be suggested by compiler itself or you can refer this commit.

Copy link

How to create global static frame_allocator so that I can move the page mapping inside the page fault handler. I have tried

In lib.rs

pub static mut FRAME_ALLOCATOR: Option<BootInfoFrameAllocator<Iterator<Item = PhysFrame>> = None

I keep getting an error "doesn't have a size known at compile-time"

@phil-opp
Copy link
Owner

How to create global static frame_allocator so that I can move the page mapping inside the page fault handler. I have tried […]

Discussed in #593

Copy link

Just wanted to mention that the src/memory.rs lacks the inclusion of a use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; when you get to talking about the writing the boot frame allocator. I know it's a tiny thing but it threw me for a loop the first time as my first guess would have had it somewhere in the paging structures in the x86_64 crate and not the bootloader.

@phil-opp
Copy link
Owner

phil-opp commented May 13, 2019

@nrlsk Thanks for reporting! Fixed in 6db5ad7.

@phil-opp
Copy link
Owner

@tom7980 Seems like you deleted your comment. It was a valid question, so let me give you a short answer anyway.

Yes, the frame allocator could be implemented this way. We used a similar implementation before. The problem is that it couldn't be stored in a static then because we can't name the generic I parameter, because it depends on unnamable closure types. See #593 for a discussion of the problem.

I'm struggling to test it myself as I'm having trouble building all the dependencies with xbuild.

Could you open an issue for this with more specific information?

@tom7980
Copy link

tom7980 commented May 14, 2019

@phil-opp I can open an issue about it tomorrow as I'm on the wrong computer currently, I was only struggling on my windows environment, once I booted into Linux and tried the build steps I managed to sort it so it might just be my windows env is mis-configured.

Regarding my question, I actually had a go at implementing it but I came up against the issue that the initialisation of the wrapper was expecting a type I where I: Iterator, but due to passing a memory map to the function and doing some transformations onto it I was getting a nested iterator and it wouldn't let me store it anyway!

I'm going to take another look at it tomorrow but I think I understand what you meant about the static still trying to wrap my head around Traits and Generic Types, and to be fair most of Rust!

@phil-opp
Copy link
Owner

@tom7980 Thanks for elaborating!

once I booted into Linux and tried the build steps I managed to sort it so it might just be my windows env is mis-configured.

I just want to ensure that xbuild/bootimage works on all OSs. If there is some configuration problem with Windows we could at least document it to help others.

I'm going to take another look at it tomorrow but I think I understand what you meant about the static still trying to wrap my head around Traits and Generic Types, and to be fair most of Rust!

Let me/us know if you have any questions, we're always happy to help!

Copy link

" We can't start the region at 28 KiB because it would collide with the already mapped page at 1004 MiB."

Is that "1004 Mib" should be "1000 Kib" ?

@phil-opp
Copy link
Owner

@tiehexue Yes, thanks for reporting! Fixed in a8908ac.

Copy link

lenenel commented Jun 23, 2019

In Temporary Mapping:

Now the kernel can access the level 2 page table by writing to page 0 KiB and the level 4 page table by writing to page 33 KiB.

Should it be 36 KiB for level 4 page? Or I missing something?

@phil-opp
Copy link
Owner

@lenenel You're right, thanks for reporting. Fixed in 2a0e5e4.

Copy link

Barronli commented Aug 27, 2019

@phil-opp Thanks for your great efforts in making the wonderful series! I have a few probably silly questions:

  1. The complete-mapping mechanism uses fixed_offset + physical_addr to get the virtual_addr. Then one should be able to use virtual_addr - fixed_offset to get the physical_addr, as long as the page is mapped, which could be indicated with a PRESENT bit in a pre-mapped page (1bit for 4KB) without the need to walk through the page table hierarchy as given in translate_addr_inner. Am I missing something?

  2. Related to the question above, the VGA text buffer in the post uses identity mapping, is it excluded from the "complete-mapping" mechanism? Or does it mean 0xb8000 can be accessed with virtual addresses of both 0xb8000 and 0xb8000 + fixed_offset?

  3. How do you debug the code for the OS development?

Appreciate your response. Thanks for your time!

@bjorn3
Copy link
Contributor

bjorn3 commented Aug 27, 2019

  1. How do you debug the code for the OS development?

https://os.phil-opp.com/set-up-gdb/

@phil-opp
Copy link
Owner

@Barronli You're welcome, I'm glad you like it!

The complete-mapping mechanism is only used for accessing physical memory. There can be other virtual mappings in the page table that point to the same physical frames. For example, the VGA text buffer is mapped at both 0xb8000 and 0xb8000 + fixed_offset. This means two things: First, we can't translate an arbitrary virtual address by just subtracting the fixed_offset since this only works if the virtual address is part of the complete-mapping. Second, it is important that we do not access arbitrary memory in the complete-mapping because it might lead to undefined behavior if the same memory is mapped at a different virtual address to (it effectively creates two &mut references that point to the same memory).

Regarding debugging, there are a few useful techniques. For example, you can use QEMU's -d int flag to retrieve the register content on every exception/interrupt. Through the content of the instruction pointer register, you can then find the instruction that caused the exception in the disassembly of your kernel binary. You can also use the QEMU monitor or GDB for full debugger support, as @bjorn3 mentioned. I'm planning a "Debugging" post that covers this topic, but it will probably take some time until I get to it.

I hope this helps!

Copy link

@phil-opp Before I get to my comment - great set of posts - incredibly simple to understand and follow and a good intro to rust. Can see a lot of efforts that you have put into this - can't appreciate enough.

Now onto some failures that I see with updated dependencies:

Building bootloader
Downloaded x86_64 v0.7.2
Compiling semver-parser v0.7.0
Compiling cc v1.0.37
Compiling cast v0.2.2
Compiling bootloader v0.6.4 (/home/anup/.cargo/registry/src/github.com-1ecc6299db9ec823/bootloader-0.6.4)
error[E0461]: couldn't find crate core with expected target triple x86_64-bootloader-1691626545719462551
|
= note: the following crate versions were found:
crate core, target triple x86_64-bootloader-8482436261164568872: /home/anup/OSDev/rust_os/target/bootimage/bootloader/bootloader-sysroot/lib/rustlib/x86_64-bootloader/lib/libcore-c2b176c4b831be55.rlib
crate core, target triple x86_64-bootloader-8482436261164568872: /home/anup/OSDev/rust_os/target/bootimage/bootloader/bootloader-sysroot/lib/rustlib/x86_64-bootloader/lib/libcore-c2b176c4b831be55.rmeta

error: aborting due to previous error

error: Could not compile cast.
warning: build failed, waiting for other jobs to finish...
error: build failed
Error: Bootloader build failed:

Did I miss something / do something wrong?

@phil-opp
Copy link
Owner

phil-opp commented Sep 1, 2019

@siebenHeaven Thanks for the praise, it's great to hear that you like it!

I already encountered this error a few times. I think the problem is that cargo-xbuild fails to remove old versions of the sysroot after the target JSON changed, but I haven't had the time to debug it yet. As a workaround, try to do a cargo clean. This should clean all compilation artifacts and so that a subsequent build recompiles core for the correct target.

Copy link

@phil-opp Thanks for the instant help, that worked!

Copy link

htbf commented Sep 26, 2019

I got around the type awkwardness / iterator inefficiency for the allocator as follows:

pub struct BootInfoFrameAllocator<T: Iterator<Item = PhysFrame>>(T);

impl BootInfoFrameAllocator<core::iter::Empty<PhysFrame>> {
    pub unsafe fn new(memory_map: &'static MemoryMap)
        -> impl FrameAllocator<Size4KiB>
    {
        BootInfoFrameAllocator(
            memory_map.iter()
            .filter(|r| r.region_type == MemoryRegionType::Usable)
            .map(|r| r.range.start_addr()..r.range.end_addr())
            .flat_map(|r| r.step_by(4096))
            .map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
        )
    }
}

unsafe impl<T> FrameAllocator<Size4KiB> for BootInfoFrameAllocator<T>
    where T: Iterator<Item = PhysFrame>
{
    fn allocate_frame(&mut self) -> Option<PhysFrame> {
        self.0.next()
    }
}

It's not perfect, and implementing new for Empty is a slightly weird artifact, but since only one implementation for new exists, you don't need to type annotate it and you can just call

let mut frame_allocator = unsafe {
    memory::BootInfoFrameAllocator::new(&boot_info.memory_map)
};

@phil-opp
Copy link
Owner

@htbf We used this approach in the first version of the post. The problem is that the generic type can't be named since it contains closures. For this reason, we changed it to the non-generic version to allow storing the BootInfoFrameAllocator in static variables more easily.

I plan to switch back to this version as soon as the Permit impl Trait in type aliases RFC is fully implemented. Then we could use an approach like this to store a generic BootInfoFrameAllocator in a static variable.

@andyhhp
Copy link

andyhhp commented Jul 27, 2020

Also when you're running 32bit userspace under a 64bit kernel, full legacy behaviour still applies, so all segments can have a nonzero base if you have a particularly evil application developer, or its something like a DOS emulator and playing around with 16bit code.

@phil-opp
Copy link
Owner

True. I meant when you're fully running in 64-bit mode, but I agree that it's important to be exact here. I updated my comment to ensure that no one gets confused by it.

@ethindp
Copy link

ethindp commented Jul 27, 2020 via email

@andyhhp
Copy link

andyhhp commented Jul 28, 2020

The processor gave you the faulting linear address in %cr2. That, along with the pagefault error code, need to be used to judge whether the access was legitimate (and the pagetables need modifying) or whether it was illegitimate and you should crash.

Examples of legitimate pagefaults would be an access to a frame which is currently paged out to disk (at which point you've got to pause the task, page it back in from disk, edit the PTE to point at the new frame, then resume the task), or a write to a Copy-on-Write page (at which point you've got to allocate a new page, copy the old contents, and adjust the PTE from the faulting process to be read/write and point at the copy).

Examples of illegitimate accesses might be a write to a read-only location (which isn't Copy on Write), or a kernel access to userspace mappings when SMEP/SMAP is active, etc.

Unless you've started implementing any demand-faulting logic for code (which you probably haven't done, if you don't have userspace yet), then pagefaults should typically be fatal.

Copy link

pitust commented Aug 26, 2020

I am getting an error:

Panic: panicked at 'huge pages not supported', src/memory.rs:40:43

It seems that the last page is huge.
I fixed it like this:

#[macro_export]
macro_rules! phmem_offset {
    () => {
        x86_64::VirtAddr::new(PHBASE.load(Ordering::SeqCst) as u64)
    };
}
fn translate_inner(addr: VirtAddr) -> Option<PhysAddr> {
    let (level_4_table_frame, _) = Cr3::read();

    let table_indexes = [
        addr.p4_index(),
        addr.p3_index(),
        addr.p2_index(),
        addr.p1_index(),
    ];
    let mut frame = level_4_table_frame;

    for &index in &table_indexes {
        let virt = phmem_offset!() + frame.start_address().as_u64();
        let table_ptr: *const PageTable = virt.as_ptr();
        let table = unsafe { &*table_ptr };
        let entry = &table[index];
        frame = match entry.frame() {
            Ok(frame) => frame,
            Err(FrameError::FrameNotPresent) => return None,
            Err(FrameError::HugeFrame) => return Some(frame.start_address() + addr.as_u64() % (frame.size())),
        };
    }

    Some(frame.start_address() + u64::from(addr.page_offset()))
}

the phmem_offset!() macro is there because i use an AtomicUsize to store my physical base (i modified the bootloader crate to map me a nice 1920x1080 24bpp graphics mode and needed physical memory base to be global for my io module which needed to know the physical memory base to know where to write to access 0xfd000000 which is the framebuffer)

Copy link

pitust commented Aug 26, 2020

Also, writing to zero is a very bad idea. Here is why:

pwndbg> disassemble core::ptr::write_volatile 
Dump of assembler code for function core::ptr::write_volatile:
   0x000000000020ecc0 <+0>:     sub    rsp,0x28
   0x000000000020ecc4 <+4>:     mov    QWORD PTR [rsp+0x18],rdi
   0x000000000020ecc9 <+9>:     mov    QWORD PTR [rsp+0x20],rsi
   0x000000000020ecce <+14>:    mov    QWORD PTR [rsp+0x10],rdi
   0x000000000020ecd3 <+19>:    mov    QWORD PTR [rsp+0x8],rsi
   0x000000000020ecd8 <+24>:    call   0x20e260 <core::intrinsics::is_aligned_and_not_null>
   0x000000000020ecdd <+29>:    mov    BYTE PTR [rsp+0x7],al
   0x000000000020ece1 <+33>:    mov    al,BYTE PTR [rsp+0x7]
   0x000000000020ece5 <+37>:    xor    al,0x1
   0x000000000020ece7 <+39>:    mov    cl,BYTE PTR [rsp+0x7]
   0x000000000020eceb <+43>:    test   cl,0x1
   0x000000000020ecee <+46>:    je     0x20ed01 <core::ptr::write_volatile+65>
   0x000000000020ecf0 <+48>:    jmp    0x20ecf2 <core::ptr::write_volatile+50>
   0x000000000020ecf2 <+50>:    mov    rax,QWORD PTR [rsp+0x10]
   0x000000000020ecf7 <+55>:    mov    rcx,QWORD PTR [rsp+0x8]
   0x000000000020ecfc <+60>:    mov    QWORD PTR [rax],rcx
   0x000000000020ecff <+63>:    jmp    0x20ed05 <core::ptr::write_volatile+69>
   0x000000000020ed01 <+65>:    ud2    
   0x000000000020ed03 <+67>:    ud2    
   0x000000000020ed05 <+69>:    add    rsp,0x28
   0x000000000020ed09 <+73>:    ret  

and rust:

#[inline]
#[stable(feature = "volatile", since = "1.9.0")]
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
    if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) {
        // Not panicking to keep codegen impact smaller.
        abort();
    }
    // SAFETY: the caller must uphold the safety contract for `volatile_store`.
    unsafe {
        intrinsics::volatile_store(dst, src);
    }
}

Of course, "to keep the codegen impact smaller", we won't panic, but instead abort (so basically asm!("ud2"); ).
This is a little annoying, since we don't have illegal opcode handler so we land in double fault handler with knowledege that something broke. What? Figure it out yourself.

Copy link

pitust commented Aug 26, 2020

To be clear, that is from the core crate

Copy link

pitust commented Aug 26, 2020

Writing to zero basically will cause a lot of suffering and confusion.

Copy link

pitust commented Aug 26, 2020

Nevermind, in the example it's writing to 400.

@phil-opp
Copy link
Owner

@pitust

It seems that the last page is huge.
I fixed it like this: […]

Thanks for sharing your code, it is much simpler to handle huge pages than I thought! We should update this in the post. I opened #852 to track this.

Also, writing to zero is a very bad idea

You're completely right. I opened #851 to track this and I try to update the post to use a different page soon.

i modified the bootloader crate to map me a nice 1920x1080 24bpp graphics mode

That's great! Would you mind sharing your code? I'm currently working on a new bootloader version with UEFI support that will use a pixel-based framebuffer by default (see https://github.com/rust-osdev/bootloader/tree/uefi). For consistency, this means that we also want to default to a pixel-based for the BIOS implementation. Right now, we only support a small 320x200 framebuffer in the BIOS implementation, so your implementation might be useful for us.

@pitust
Copy link

pitust commented Aug 27, 2020

I completly disabled bootloader's printer interface and changed config_video_mode to:

config_video_mode:
    push bx
    push ax
    mov ax, 0x4F02
    mov bx, 0x4192
    int 0x10
    pop bx
    pop ax
    ret

I then asked it to map me all the RAM, and used that to write to the framebuffer which is at 0xfd000000, at least in QEMU. I have no clue how compatible this is.

@andyhhp
Copy link

andyhhp commented Aug 27, 2020

Looking at PageTableEntry.frame() logic, there are some subtitles.

First of all, architecturally, a page address also isn't valid if it has any reserved bits set in addition to the present bit. For long mode paging, reserved bits are any address bits beyond MAXPHYSADDR, the NX bit if EFER.NXE is clear, any low order address bits on a large page except for PAT (bit 12), and very irritatingly, GLOBAL on the 4th level (but only on AMD hardware. Intel permits this bit to be set).

Very occasionally, logic deliberately sets a reserved bit (usually 51) and puts non-address metadata in bits 50:1. This trick is no longer safe now that some large servers implement all 52 physical address bits.

Secondly, and more importantly, PSE doesn't exist in a 4k page, and the bit is the PAT bit instead. Specifically, the PAT bit moves position depending on whether the page is large or not, and a 4k page isn't large by virtual of being the 4th step on the pagewalk, not by its bit layout. The currently logic will confuse the a non-WB cache type on the mapping as a large page.

~Andrew

P.S. if x86 pagetables look like they're an organic mess grown over the past 30 years, that's perhaps because they are 😅

Copy link

So, I was doing very well following your tutorial, (thanks for the work, much appreciated!), until just after the section on "Translating Addresses". I now get the following:

error[E0658]: mutable references are not allowed in constant functions
   --> /Users/josiah/.cargo/registry/src/github.com-1ecc6299db9ec823/x86_64-0.11.0/src/structures/idt.rs:403:30
    |
403 |                 interrupts: [Entry::missing(); 256 - 32],
    |                              ^^^^^^^^^^^^^^^^
    |
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable

error[E0277]: the trait bound `PageTableEntry: core::marker::Copy` is not satisfied
   --> /Users/josiah/.cargo/registry/src/github.com-1ecc6299db9ec823/x86_64-0.11.0/src/structures/paging/page_table.rs:192:22
    |
192 |             entries: [PageTableEntry::new(); ENTRY_COUNT],
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `core::marker::Copy` is not implemented for `PageTableEntry`
    |
    = note: the `Copy` trait is required because the repeated element will be copied

error: aborting due to 55 previous errors

Some errors have detailed explanations: E0277, E0658.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `x86_64`

I actually have 55 errors total, but they are similar to or copies of E0658. Errors seem to be all related to compiling of x86_64. I have been able to compile and run every part of the tutorial up till this error.

It feels like I underwent a system environment change that created this error for me. The only distinct thing I did around the time this error popped up though was to update Ruby and install Substrate, the blockchain building tool that uses Rust. Not sure if maybe something in that installation upended my environment. I have checked all toolchain as best I could, making sure I'm still using nightly Rust, rustc, cargo, etc. I tried adding the crate attributes as the compiler error suggested but got nowhere.

Any advice before I try a full reset/restart?

Copy link

Further note: I tried various versions of x86_64, including the most recent 0.12.2 and several earlier versions. Nothing changed.

@bjorn3
Copy link
Contributor

bjorn3 commented Oct 5, 2020

Try updating your rustc version. The latest rustc should work with x86_64 version 0.12.2 I think.

Copy link

@bjorn3 Tried it, nothing changed - still have the same errors.

@phil-opp
Copy link
Owner

phil-opp commented Oct 7, 2020

@quaesaevum You still have version 0.11.0 of x86_64 somewhere in your dependency tree, which is the reason for this error (see the path in the error message you posted). Try cargo tree to see where it is coming from. Also note that the bootloader crate also depends on x86_64, even though this is not shown in cargo tree, so try updating it too.

Copy link

@phil-opp Thanks! That was it exactly.

Copy link

lattice0 commented Dec 3, 2020

Now that x86/amd64 is dead can you make these tutorials but for ARM64? Paging is very different on arm.

@phil-opp
Copy link
Owner

phil-opp commented Dec 6, 2020

I don't have plans to port these tutorials to ARM64 anytime soon. I'd rather invest my time in continuing this series (e.g. threads, userspace, etc). After all, x86 is still the dominant architecture on consumer systems.

@GuillaumeDIDIER
Copy link

And well, x86 has an ugly but at least somewhat specified and generic interface to the hardware. I don't think there's any equivalent to UEFI in ARM64 land, yet.

Also, the OS course I took still uses 32 bit (with Simics, or an old crash box computer dedicated to it - boots using CDs), because it teaches the right concepts with less registers to guet wrong, and a somewhat simpler handling of setting up paging (which is a bit more complicated under x86_64, as page tables have to be set up before jumping to long mode).

Once you have this knowledge of concepts, you should be able to deal with other platforms. (Or not because they have horrendous documentation when it comes to devices etc)

@bjorn3
Copy link
Contributor

bjorn3 commented Dec 6, 2020

UEFI exists for ARM64 too. In the past everyone was using their own firmware and device interfaces, but as far as I know ARM nowadays is pushing for some standarization to make it more suitable for use on servers.

Copy link

I've been trying to beef up my page fault handler to actually allocate new pages in some cases (e.g. if the kernel stack needs to grow and it's safe to do so). But I'm having trouble (a lot of trouble) trying to figure that out, because I need to have some frame allocator to allocate frames that back those pages.

In the examples here (and in the heap section), you have a frame allocator which is created by kernel_main, and gets handed off to other functions as needed. But I don't see a way to use an approach like that for my page fault handler, as I won't be able to pick up the allocator from the kernel main function. Nor can I just instantiate an allocator in the page fault handler, since then the allocator that gets instantiated with zero knowledge of what frames are already allocated.

I have tried to make a static allocator with a mutex in front of it, but that then runs into lifetime issues - I'm really bad at understanding lifetimes, but I think it's because the compiler wants the allocator to have a static lifetime but that struct requires data that doesn't have a static lifetime (in this case, coming from the BootInformation struct I'm loading with the multiboot2 crate).

So I'm pretty much stuck - either I have memory unsafety because I'm not using a single global allocator, or I can't compile due to lifetime incompatibilities. Any advice on getting out of this conundrum?

@alexxroche
Copy link
Contributor

When I reached the end of paging-implementation,

At this point, we should delete the create_example_mapping function again to avoid accidentally invoking undefined behavior, as explained above.

If I do that it no longer compiles.

error[E0425]: cannot find function create_example_mapping in module memory
--> src/main.rs:32:13
|
32 | memory::create_example_mapping(page, &mut mapper, &mut frame_allocator);
| ^^^^^^^^^^^^^^^^^^^^^^ not found in memory

(Every other post ends with code that passes cargo check.) Maybe this post should instruct the student to:

At this point, we should delete:

// src/memory.rs

/// A FrameAllocator that always returns `None`.
pub struct EmptyFrameAllocator;

unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
    fn allocate_frame(&mut self) -> Option<PhysFrame> {
        None
    }
}

to avoid accidentally invoking undefined behavior, as explained above.


If we remove fn create_example_mapping from src/memory.rs at this point, then src/main.rs
throws errors. If we comment out the lines in src/main.rs until the errors abate the "New!" no longer appears.
(So we have no example of actually using the new frame_allocator.)

@ethindp
Copy link

ethindp commented Mar 24, 2021

@drzewiec typically you static-ize your frame handler in some way like this (this is from my kernel):

lazy_static! {
static ref MAPPER: Mutex<Option<OffsetPageTable<'static>>> = Mutex::new(None);
static ref FRAME_ALLOCATOR: Mutex<Option<GlobalFrameAllocator>> = Mutex::new(None);
// ...
}

// Instantiate the mapper and frame allocators:
    let mut mapper = MAPPER.lock();
    let mut allocator = FRAME_ALLOCATOR.lock();
    *mapper = Some(unsafe { init_mapper(physical_memory_offset) });
    *allocator = Some(GlobalFrameAllocator::init(memory_map));
    let end_addr = start_addr + size;
    match (mapper.as_mut(), allocator.as_mut()) {
        (Some(m), Some(a)) => allocate_paged_heap(start_addr, end_addr - start_addr, m, a),
        _ => panic!("Cannot acquire mapper or frame allocator lock!"),
    }

You'll need a function to allocate a paged heap, of course, but this strategy should work.

@drzewiec
Copy link

drzewiec commented Mar 24, 2021 via email

Repository owner locked and limited conversation to collaborators Jun 12, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests