The x86-64 architecture uses a hierarchical page table system with four levels (PML4, PDP, Page Directory, Page Table) where the top level is level 4 and bottom level is level 1, unlike ARM64's reverse numbering. The page table entries use a uniform format across all levels, with bits 3, 4, and 7 repurposed for the Page Attribute Table (PAT) to control memory caching behavior (Write Back, Write Through, Write Protected, Write Combining, Uncachable). The CPUID instruction with EAX=0x80000008 returns the largest virtual and physical address sizes, while CR3 register holds the top-level page table address and writing to it automatically flushes the TLB.
深掘り
前提条件
- データがありません。
次のステップ
- データがありません。
深掘り
Live Coding - No AI - Bits Dream Exokernel (BDE)追加:
Hey there, fam. I'm DP and you're watching the Dog Brick Prime channel. It is uh Tuesday, May 12th, 2026, and it's a live coding session where I will be working on my Exo kernel and bootloadader. Uh writing some more code and uh so let's get started. I'm going to play the uh intro. I'll be right back. All right.
So, I am DP and my company name is Bitstream and I'm writing the Bitstream Exo kernel. And uh let's make sure my microphone's working. Yes, it is. Good.
Everything looks good. I'm going to real quickly post a link to the stream on my Facebook page and get the YouTube chat open.
I get this here. Get sharable link.
Paste.
That's not today.
Oh, let's try that again. That was yesterday's stream.
I forgot to hit the refresh button. Get sharable link.
Boom.
There we go.
Facebook is slow. It's still trying to post. There we go.
We'll get that open and we'll get it open on the other computer.
It's almost loaded.
There we go.
All right. The YouTube chat is now open on the other computer so I can see if anybody joins. Feel free to say hi and Let's go ahead and do a screen share here.
And today's wallpaper of the day from Bing.
Oh, I've been updated. Visual code 1.119.
More and more it seems like all the updates are AI related which are features that I won't use chat AI related again AI related Markdown preview mode is now more prominent. I've never done I've never written any markdown in uh in a code editor.
I think I've written a few markdown commands in the forum postings 15 years ago maybe.
web views. Oh. Oh, so I've already seen this update. So, this is the update that popped up on my um Mac uh like a week ago.
All right. Um, I suppose it lags behind on Linux because I'm using the uh the DRO's delivery system rather than installing it myself manually from a direct download. So, it probably takes a week or so for for them to recompile and redistribute it through the uh DRO's uh channels, which is fine. It's not like there's anything on VS Code that's going to be like so groundbreaking that, oh, I can't wait I I can't work until I get this feature enabled. That happens in Rust sometimes and that's when I switched to nightly.
Um, all right. And then going back to uh what I was working on. So I started searching this and then I was uh that's right. I had these up. Okay. So um to uh um to recap for anyone that uh was not here yesterday um I have a uh exo kernel which I am writing uh initially using a UEFI bootloader. I'm going to support x86, ARM, risk 5, and LSON. Um, all all the 64-bit architectures at first and then the 32-bit architectures and then those all support uh UEFI. So, I decided to uh use UEFI as a starting point. So, I wrote the UEFI bootloader uh to load the kernel. The kernel is at this point just a uh well it was just a hello world message but now it actually sets up a stack and then prints the hello world message. So we've got a working stack now. Uh, and I I was initially developing for 64-bit ARM because I was using an M1 MacBook. Uh, and now that I have transitioned over to doing my coding on Fedora Linux, uh, so that I can have a better, uh, bigger screen and a better broadcasting environment for this stream.
um the uh I've added the additional emulation like QEMU virtual machines for testing on all all those different platforms. So I'm now adding in the code for um the uh so this was the module that I had for AR64 um for the setting up the virtual memory page tables which is necessary before you jump to the kernel and I am now transferring uh converting all of that to the equivalent on x8664 um the actual kernel itself I have converted the entry point to be a naked function so that I can set up my own stack and uh I that was this and then I've now factored that out to specify that that is 64-bit ARM specifically and I have added the comparable uh the equivalent um assembly language for x86 as well as I did my best attempt at risk 5 and long and uh I cannot test those yet to see if they're correct because it won't compile uh because the UEFI versions of the targets uh compiler targets are not yet supported by Rust.
So, um, I can, uh, there's a Lunark 64, uh, unknown none, but, there's not yet the unknown UEFI. And same thing for Risk 5. They have the unknown none, but they don't have the unknown UEFI yet.
Um, so at the moment, I cannot compile.
I was thinking, oh, I'll just add these and then I can compile the kernel. Um, but I won't have the ability to compile the UEFI bootloadader yet. But no, the kernel and the bootloadader both use my uh UEFI compatibility module that I wrote um as a shared library dependency. So they so I can't even though the kernel is not targeting UFI, I still can't compile it because it has a dependency that gets compiled in that uses UFI. So, um, so that was what I discovered there. So, that was as far as I got with my risk 5 and long arc support is some untested, uh, set the stack pointer and jump to main. Uh, and that's it. But, uh, what we're working on, so I know that this 64-bit ARM code works, and now I'm working to convert everything to uh, x86, and then I'll be able to test that that works. Um, I've commented those targets out for right now because they do not exist yet in Rust.
The apparently the compiler supposedly the supposedly this has just been added.
The Lung 64 UEFI target has supposedly been approved to be added to Rust, but it hasn't actually shown up yet. So, I've I've been trying to like find it to see how how to activate it. There's an there's a pull request where or a pull request, I'll just say, whatever the correct term would be, um uh where it was accepted as a major change to add this, but then I haven't actually that was 6 weeks ago, and I haven't actually seen anything show up yet. So, I don't know. Uh so, hopefully that's coming. And for risk five, who knows? We'll we'll see. I mean, I would have thought there had been UEFI support for that by now, but um and so this is what I'm doing is I'm building this new uh I'm converting this function from AR uh to x86 and uh that's what I was working on yesterday when I stopped. So I was look I was setting up my additional flags here the try this these are the bit mask flags for the different uh uh the bit masks for the different flags that are used by AR64 uh for virtual memory page tables. They have a whole lot of different uh bit flags set up. And it turns out that x86 uses a lot less.
There's actually uh bits 9 through 11 and 52 through 62. So that's 14 bits out of a 64-bit address that are actually unused and available for private use by the colonel. So that's surprising.
There's certainly not any excess bits left over in the uh on the arm side because they've used all these different bits for all these different things.
In fact, they're so short on on bits that when they extended it to uh 52bit uh extension support um they actually had to uh remove some features. So if you run in extended mode, you actually lose the ability to set some of these permissions on a per page basis and they have to be global settings instead, which is interesting. Um I'm trying to think if I let's see I was going to do a find and replace. Um let's see. It is that key uh that selects all instances I am pretty sure.
Where am I? Here there's one.
Yeah, it even selected the ones that were in comments. So that's good.
So basically, and you know what? I actually I prefixed everything with table entry descriptor. So that's probably what I want to actually um let's Let's do that cuz that's going to get all of them.
Yep, that gets everything.
I didn't refer to him in too many places.
Nothing down here.
So, we're going to do a control shift L.
No. Oh, it's because I It's cuz I clicked on this. This wasn't highlighted anymore. So, let's try this again. Control shift L. There we go. We got everything in the whole file. Oh, that's more of what I was looking for.
Um, I just looking at all the ones that are highlighted to make sure they're what I actually want.
Yep.
All right. And then what we're going to do is add in Um, let's just add a ar64 underscore into every single one of those that way. I've got I've added x8664 over here and now I have ar 64 over here.
put that cursor at the top and then scroll down. All right. So, I like that change. And then uh so now I have x8664.
I have execute never which this actually should be more properly written that way. All right. So I think that's everything. Um, and I bet I had a a bit mask over here, didn't I? for I had a block or page mir index bit mask.
You know what I should have done?
There were a bunch of places where I masked out uh the the flag the the the bits that are used for the flags. I should have just defined that as a as a constant bit map.
Let's see. Let's go find I didn't want to find in that window.
I want to find in this window. There we go. Find 0x.
Why am I that far down?
It shifts. Okay. Yeah. All right. So then kernel stack I have defined here as just a constant. I mean as a as a literal. I guess that's fine.
Oh, yeah. That's because that's a 1 gigabyte page. Okay.
That's a 2 megabyte block.
So, you know what I did is I actually um I factored that out into a convenience function, didn't I? So, probably the convenience function is the only place where I'm going to have to There it is.
Get PA from descriptor.
So, that was actually the uh constant what I was thinking of making into a constant. That's probably the only place that that actually appears then. Oh no, there it is again.
Duplicate child tables descriptor.
So FFF FFF and what was the previous one? This is actually the opposite.
Get physical address.
So that's the bits in between. This is the inverse.
Got it.
So, those are each the only place where I use those con. So, I mean, I could make them a const above in case they would change, but let's see.
they're not going to change in regular 64-bit ARM. If I'm using the if one of the optional extensions like 52 or 56bit extension, um then I'm going to have to enter additional code to factor for that. So maybe I won't make that a constant after all. Maybe I'll just leave it there since that's the one and only place that each of those appears.
That's something I could do in the future though, should there ever be a need for it to to refer to this from more than one place. Um, I could just make that define that as a const.
I could even be fancy and define it as the inverse of each other.
Okay, I'm not worried about that then.
We got the rest of the bits I think defined. Then these are all defined.
Let's see what else. Uh, everything else should basically work the same between x86 and ARM.
It's a lot fewer uh bit mask definitions because it seems to use the same format for all the different table levels unlike ARM who packed so many bit flags in here that they had to use different formats at different levels.
Okay, so there's the beginning of the function.
how to handle page vault. That'll be useful.
Okay.
So it looks like the CR3 register is where the CR2 is automatically set by the CPU on a page fault contains the virtual address that caused the page fault.
So when you set CR3, it automatically flushes the cache. That's good to know.
Me look at this TLB module.
Um, where's the code? Oops.
Flush invalidate TLB completely. That's what I want to see.
How do we get to the code? There it is.
Tada.
Flush all registers. control CR3 CR3 read CR3 write.
Okay, so it literally just writes the same value back to the register, but just the act of doing so flushes.
Okay, that's good to know.
I'm not going to be doing it that way through a crate though. I'll be just dropping into assembly language to do it.
So, that doesn't help me know what the code looks like, but it doesn't let me know what to do.
Ah, Pac-Man. Hello. How are you today?
Okay.
Let's see. Is there a I wish there was a select the entire function command. I don't know how I would do that. Selection.
Oh, I could expand. That would work.
Shift, Alt, right arrow. Oh, look at that.
Nice.
Well, that's a good command to know.
And then we'll come over to this window and we'll go paste.
I'm doing good. Yeah. Uh, woke up nice and early today. Had a good morning.
drinking some nice fresh water.
I broke out. My sister got me a little Is it too hot? No. My sister got me a uh popsicle scented candle. So, I'm burning uh let's see. It's a mixture of fruit punch orange and pineapple popsicles candle. So, it smells I guess very fruity. Um, I can't smell very well, so I can just barely smell it. I just burn candles because I like to watch the flame mainly. Um, and what I'm what am I working on today? So, I'm Oh, hello Adit. Um, I am uh working I'm picking up where I left off yesterday, which is I am translating the 64-bit ARM uh building of the me virtual memory page tables over to the x8664 equivalent um so that we can successfully boot up a x86 version of the kernel zen coding session. Yeah.
Yeah, most days I have a candle burning, but yeah, I can I can hardly unless I get right over it and try really hard.
Um I can get just a little bit of a whiff at for a split second and then it's gone. Um my my sense of smell has never been that good actually since 8th grade. Uh back in um it probably would have been 1989 or 1990.
Uh, I actually up until that point I had a really good sense of smell. Uh, and then one day I just basically lost it and could hardly smell anything at all ever again. Uh, and I guess that's fairly common for people that have a really sensitive sense of smell.
Apparently the nerve is so sensitive that it can get overstimulated and just cut out. And that's what happened to me.
So I can there are only certain things that I can smell at all. And even the one things that I can smell, I typically only can smell for a second and then and then they are gone. I can't smell them on an ongoing basis. So, I can just get a little teeny whiff of some tropical for a split second and then and then I'm just stuck looking at the flame.
Yeah, it's apparently very common with sense of smell and uh um sense other senses that involve just one single nerve. A lot of times if that if that one single nerve is hyper sensitive, it'll just overload and uh like a microphone that's turned up too high and clips and it just permanently clips and it's and it's not registering anymore.
Um but yeah, so I'm trans I'm uh wait my ah I was going down. Okay.
All right. So I just copied this function over. So now that'll that'll satisfy this is no longer red. Okay. And then now I just need to clean this up and make it work on x86.
So let's so I'm receiving handle map and pages allocated from uh as passins and getting the boot services table which is still the same on x86. X. Um, exiting boot services.
Getting the rest of the variables.
Exiting boot services. That's all going to be the same.
What language is that supposed to be?
It's not spelled backwards, so it must be a different language.
Text message H my sister's a librarian and uh one of the libraries that she works at sometimes apparently had a big car fire in the uh parking lot and two or three other people's cars got destroyed. So, she's she's glad that uh she wasn't parked there. Wow. Big big flame flame pictures.
The la and that's the same that's the same library that her last car got destroyed by hail a month after she bought it.
Hindi I see. Um what's the project about? Hello Oarishi.
Um so I'm writing my own kernel and it's going to be small and tightly focused.
Uh, so it's an exo kernel and then there's going to be a bunch of different um layers of abstraction that can be built on top of it to build it up into like a universal platform for all different operating systems and all different emulators to run on simultaneously.
Um, and where am I located? I am in Colorado in the United States. So the state of Colorado. Um, I live in Parker currently, which is a it used to be a small town uh southeast of Denver, but Denver has gotten so large now that it's the city has pretty much grown out around it. So, so now it's like living in a it's like a historic downtown for two blocks and then city all around it.
And uh I'm doing a UEFI bootloader um to go with the kernel. And the kernel is going to this is all that the kernel consists of so far is just an entry function where I'm I'm setting my own stack pointer and then calling a main function and the main function just prints a welcome message and it's just bits dream exokernel. It's the name of my kernel. Um so like as a hello world message. So that's all that the kernel does so far. I mean a few days ago though it only just printed its name.
Now it actually has a working stack um and it can print its name. So that means it can call because I couldn't call functions without a stack. So when I tried to call welcome message as a separate function, it crashed because there was no stack. So now I have my own working stack and I am now I that was on uh 64-bit ARM and now I'm converting um I had in order to get the kernel to load I had to do a full implementation of virtual memory in the bootloadader. So the that's what all this over on the right is is um this was my setting up of virtual memory page tables on 64-bit ARM. So it took me 3 months to figure out all the details of that. And uh so I've finally got to the point where all that is working now and I can successfully boot with a full implementation of virtual memory. um and launched the kernel in virtual memory mode on 64-bit ARM. Uh but then I and now I'm just uh reduplicating that over to x8664 um so that we'll be able to boot both and I'm eventually going to be adding risk 5 and lungark uh as well. Um but the necessary targets in Rust uh for the compiler targets are not there yet. So, this is this is what I think it's going to look like, but I have no way to test this yet because uh Rust can't compile it.
But I am going to have working uh Intel and ARM uh compilations here very soon. So yeah, I'm uh converting this virtual memory page table building system over to the Intel equivalent now.
Yeah, the library is cursed.
That library is way down far to the southeast to uh down toward where I live. Not quite that far, but um it's quite far out of the center of the city.
Smoky Hill is what it's called.
Yeah. My sister's a a librarian that helps people get jobs, uh helps people counsels them, career counseling, and helps them fill out applications and things like that. So, um Okay. So, I was going Ah, that's right. I was right here.
Um, so far I don't see anything that is You know what? I think I'm going to just go ahead and combine these um because I don't really need to go back and forth. I think uh if I come over to this one, it's going to be to copy and paste another function that this relies on.
Let's see page tables max.
So this is going to be looping through the memory map which the memory map is the same u because it's the same UEFI firmware regardless of the platform.
All that is just to find the largest block of unused memory to set up use as new page tables because at this point I no longer have a memory allocator. I have assumed full control of the system.
So there there is no memory allocator. I just have full access to all of memory and I can do anything I want anywhere I want um at my own peril. So I just found the largest unused block of memory and just started using it. Okay, this is going to needed to be adapted because this is going to have to be um implementation specific. So, this is where I am figuring out how many levels of tables and memory addressing bits I'm working with.
And I'm going to have to change the way this works for the levels, too, because they're numbered in reverse on Intel.
Instead of going from three up to negative one uh on Intel, they go from one uh three is the lowest level here. One is the lowest level on Intel.
They go 1 2 3 4 5 instead of 3 2 1 0 negative 1.
Um and then Okay. So back to here. Um so paging.
Let's see. I read um last night in something I read there was a reference to um uh how to read or where it stores how many bits. Um let's see. So, x8664 uh how to read um current memory address bits from CPU registers.
Let's try that.
CPU ID. That's right. That's what it was. Um, so there's a CPU ID.
This is a Rust one. I didn't I don't think I saw this, but I did come across the CPU ID thing.
In the x86 architecture, CPU ID is a processor supplementary instruction allow allowing software to discover details of the processor.
program can use CPU ID to determine processor type and whether features such as MMX or SSSE are implemented blah blah blah. Okay, so EAX equal 8 0000008 virtual and physical address sizes returns largest virtual and physical address sizes in EAX. Bits 0 through 7 are physical address and 8 through 15 are linear address bits and then 30 16 through 31 are reserved EAX equals 0 highest function parameter and manufacturer ID extended So they were using EAX equals 8 to 8. Okay.
So apparently is that is that the value you pass to it? Let's see here. Where is move EAX1?
Okay. So when you and then you call CPU ID.
Got it. So you put a value in EAX register and then call CPU ID. And the value that's in there is the instruction for which uh which version of CPU ID you wanting to do. Okay.
The 12 character ask string stored in EVX, EDX and ECX.
So, EAX1 returns the CPU's stepping model, microarchitecture code name, and family, feature flags in EDX and ECX, and additional feature info in EBX. X. Okay.
All right. Makes sense. And then two is cache and TLB descriptor information returns a list of descriptors indicating cache and TLB capabilities on processors that support this leaf.
Calling CPU ID with EAX2 will cause the bottom bite of EAX to be set to one.
and the remaining 15 bytes of EA, B, C, and D to be filled with 15 descriptors of one bite each. These descriptors provide information about the processor's caches, TLBs, and prefetch. This is typically one cache or TLB per descriptor, but some descriptor values provide other information as well. In particular, zero is used as an empty descriptor. FF indicates that the leaf does not contain valid cache information and that leaf 4 should not uh should be used instead and FE indicates that the leaf does not contain valid TLB and that leaf 18 should be used instead.
There are also some cases such as descriptors 63 and C3 where a single descriptor provides information about multiple TLBs.
For each of the four registers, if bit 31 is set, then the register should not be considered to contain valid descriptors.
Got it. Okay.
Level one data cache 2 three instruction.
Okay. So, I don't think that I need to I'm going to be I mean I'm going to be setting page tables which are used in the TLBs that the TLB is the caches that cache the page table pages but um I don't think I need to know what the structure or features of the TLBs are.
I'm not dealing with those directly.
processor serial number, cache hierarchy and topology.
I'm not interested in caching right now.
Thread core and cache topology.
Thermal and power management.
Extended features base.
That reminds me of um Strong Bad from Homestar Runner. Uh, Bahugua gods text message.
Huh?
Yeah. I just got a fishing attempt from a a fake UPS package delivery wanting me to click on a PDF. Yeah.
Okay. Feature flags.
That's a lot of feature flags.
More feature flags.
X save.
Save and restore CPU extended state.
SGX trucks making loud noises outside.
Processor trace.
Okay.
Bus frequencies, bender attribute enumeration, TLB hierarchy and topology. Let's see.
So, this I could use this to read support for blocks, what Intel calls huge pages. Uh, I'm used to calling blocks because they've been working with ARM.
So I have to remember that's in EAX18.
Um translation cache level. Okay, cache level. I don't care. Um, maximum number of addressable ids for logical processors. Okay.
AMX, Tile Multiplier, TDX, Vector, ISA, AVX, Zeon.
This range is reserved for hypervisors.
Okay, highest extended function. So I think that's what we're looking for.
processor brand string level one cache and TLB identifiers.
There's Okay, this is what I wanted here. The 8 000000 Oh, I lo I lost track. Eight. Eight on each end. Virtual and physical address sizes.
Yeah, see that tells me more returns largest virtual and physical address sizes in EAX.
feature bits in EBX.
Okay, gotcha. So when you you load EAX with 8 Z8 and then you use EBX to set these features that you want clear zero retired invalidate.
Okay.
PlayStation 5 execute only memory.
Does the PlayStation 5 use a x86 processor? Really? H I guess it's an AMD, isn't it? Huh?
I never thought about it.
M commit.
So the features come back in EBX apparently. I was thinking maybe you would have to set these to set those, but it looks like maybe this is what's returned.
So, EAX returns the number of physical address bits.
That's eight bits. So, it could be up to 255.
8 9 10 11 12 13 14 15. Why did they Why did they break this up this way?
Oh, for over here. Okay, got it.
So 8 through 15 are this here and they're split here. Okay.
Maximum page count for invalidate page number of physical threads in processor minus one.
Guest physical address size. A value of zero indicates that the guest physical address size is the same as the number of physical address bits specified in EAX 7 through 0.
Okay, so these are going to be so 16 through 23 are going to be zero if it's the same. Well, but it doesn't have to be. It could be zero.
It could be the same less or zero.
So physical address bits, linear address bits. So by that are they meaning I'm taking that to mean virtual addresses and that's what they have here.
Uh x8664 linear address bits What is a linear address? Just trying to see a I mean I think I understand a linear address also known as a virtual address a linear oh linear address is a memory address obtained in the first phase of memory address translation. ation where a logical address is converted to a linear address through segmentation before further translated to a physical address through paging.
Got it. Okay.
With paging, memory address translations occur in two separate phases. In the first phase, the logical address is converted to a linear address through the translation method for segmentation.
In the second phase, the linear address produced in the first phase is converted to a physical address through the translation method for paging. Notice that paging doesn't replace segmentation, but instead was designed to complement it. In a system that implements paging, the address space is dividable into multiple pages. Uhhuh.
Yeah.
Okay.
All right. So, I think what I need to do then is and this was the Where was the Maybe it wasn't that page.
Yep. This is literally what I was All right.
What's the formatting for?
Let's see here. I had an immediate value just raw. It didn't seem to need to be wrapped in an equal or a dollar sign or anything. All right. So, let's go with that again then.
Let's see.
Let's just delete all that. Um, so first we're going to go move EAX 0X 800 08 And then we're going to go CP UI.
It needs a comma.
What? Oh, I forgot my comma up here.
And I always put a comma on the end just for good measure.
And then uh let's see.
Then I need to move move into a register the contents of EAX and I need an out register to receive it. Uh, and let's call that it's a 64-bit value. So, let's call it uh let's just call it EAX.
And let's do that. So EA let mute EAX equals a U64 and then load the EAX with this code. Call CPU ID and then read read the EAX value into a register of the compiler's choice and save that to EAX.
Okay. And then um so that will get memory address bits.
Physical address bits.
virtual address bits.
So that's interesting. Um, so on on ARM you can set you can set the current number of memory address bits in use lower than with the maximum that the uh system supports. But is that not a thing on Intel? Let's see. x8664.
Uh, can the current memory address bit uh be less than less than maximum.
All right, let's do set memory addresses to use fewer bits.
Then let's just say to use fewer bits.
Modern CPUs provide features that allow the processor to ignore Intel LAM linear address masking. This feature allows the CPU to ignore bits 62- 48 or 62 through 57.
Upper address ignore.
Why in x8664 the virtual address are four bits shorter than physical.
Okay.
Okay. I know. X8664 equivalent of T0 size.
T0 size function defines the region covered by T2BR0.
The x8664 equivalent fixed 48 bit or 57 bit virtual address space. Hm.
While you cannot dynamically set in the same way, you can restrict the valid address space by setting the present bit to zero in the upper level page directory entries. Interesting. Okay.
So, it's actually a lot less to deal with than if it just uses the fixed uh the the full full size full range automatically.
Well, that's cool.
All righty. So, then back to here. Um, so me address bits It's not 64 minus t0 size anymore. It is simply EAX shifted right by 8 and then ored. No, not ored. bitwise anded with um the bottom eight bits. So that's zero FF.
Yeah.
And I know it's not required, but I feel like I want to put that in parentheses just for clarity cuz the EAX bits 8 through 15 are the virtual address bits, which on a regular system is either going to be 48 or 57, but on QEMO virtual machines, it's probably going to be 44, 40, or 36.
So doing a bitwise and it's going to extract just the bottom eight bits. I mean after shifting So then Oh, that's right. It was um CR3 is the uh point to current top level page table which should be in the CR3.
three.
Yep, it is.
So, do you just read?
Yep. CR3 is a register. So, you can just read it directly.
Okay.
Um, so let's call that zero and move one CR3 and then I need to specify a one. That's going to be an out register.
Oops.
Uh, we have our three Oh yeah, I need to uh feel like Some comments are needed.
EAX.
Uh Let's see. Bits uh 7 through 0.
Physical address uh physical addressing bit depth.
Choose a semicolon. It's um uh 15 through 8 virtual add depth and bits uh 23 23 through 16 contain um uh what was it? It was the uh guest physical address that Virtualized guest physical addressing bit depth.
My cursor broke.
What happened? I don't know what happened to my cursor.
Uh, that was weird.
And 0 equals same as I feel like I want using the word bits too much. Okay.
EAX70 contains physical addressing in used for memory addresses.
Let's try that. Read number of bits in use for memory addresses. And then here we are going to read CR3 contains let's say points to points to top page table there. That makes more sense. Uh and then this is going to be CR3.
So old top level table is now CR3 as a mute U64 pointer cuz even if it's a 52bit address it'll still fit within a U64. That's fine. memory address bits is eix shifted eight and then just the first eight. Okay, so top level is now going to be memory address bits uh so if this is Well, 16 through 21 is going to be level one, level two, three, level four.
And then if this is um 49 through inclusive uh what would be let's see + 9 + 9 + 9 so it actually be 57. So 49 through 57 inclusive is going to be five.
Do I want to go ahead and account for um the fifth level at this time?
Think I will comment that out for now cuz I'm going to have to come back and add support for five tables, but not yet. Doing too many things at once.
So we'll just comment that out and leave it for future use.
Okay. Old top level table memory address top level.
Okay, that's still good.
If top level 128 Okay. Copy existing top level table. So, Alec next free page. I need to import that in Oops. What was that?
I did not want to do that.
Uh, what was the shortcut that I thought I learned? Shift alt right arrow.
Expand selection. Shift alt. Okay. So I did control alt. All right. So shift alt right arrow.
There we go. Copy.
Okay.
So, Alec next free page. Oh, and I was going to I need to make sure the code will work on this.
If next page is greater than equal max page, otherwise let page equal um page tables offset next free page.
Okay. So nothing about that function will be different.
New top level table.
Let's see. I didn't have any.
Just had that Okay.
What?
Then copy descriptor to same index.
which also calls it also calls copy descriptor.
So basically I'm going to need all these read, write, copy.
Going to need all those.
And let's check them for modifications.
Read descriptor.
Okay, that should still work. Write descriptor.
Okay, copy descriptor.
Copy descriptor to same index.
Get descriptor type. Okay. So that is not going to be the same.
So I comment that out for get physical address from descriptor.
That might not be the same either. So let's comment that out for the moment so that we get an error.
Everything else looks good.
Okay.
All right. So, copy descriptor to same index old top level table top level table. So, Let top level table equal ALEC next free page. I'm not dealing with the what level it is yet. I'm just calling it the top level table. So that's fine.
Now this is where we need to change if top level table less than three. So that needs to switch to greater than one.
And this would be minus one.
Right? Right. So if top level table is two then we're going to say duplicating L1 tables.
Duplicate top level table. Top level page tables, max pages, next free pages.
Okay.
All right. So then I need duplicate child tables. I need this function.
Why is that? That's not the key I wanted to hit. Try this.
There we go.
Let's see. I can just I can do this.
There we go. That and that that. Okay. So, let's look at this function. Duplicate child tables.
It receives a table current level page tables max pages next free page.
Okay. So for index let descriptor equal read descriptor let descriptor type equal get descriptor. Aha.
So that's going to need to be changed.
So does this Then we're going to be let's see.
Okay. So, this is going to have to be refactored.
All right. So descriptor type uh basically.
So, how is a um page table entry marked as invalid? Is it just zeroed?
A page table entry is marked as invalid when its present bit is set to zero.
Aha.
When the P bit is zero, the remaining 63 bits are ignored.
The page table entry may not exist at all. An operating system may limit the page table size so it does not cover the full address.
I'm getting spam calls again. Uh full virtual address range and the specified address is outside that range.
This can also occur with multi-level page tables where not all levels been filled in. Problem two, the page table entry exists but the page has not been mapped to the virtual address space.
Problem three, the page is not in physical memory. Four, the type of access requested is not permitted.
Okay.
paging implementation.
Back to uh Phil Opbererman again.
Let's see. Is this the same I think this is the article before the one that I looked at earlier.
Mhm.
Okay.
Find valid.
Okay. So it looks like uh I could replace get descriptor type with is valid.
say is is present and that would be descriptor and table entry descriptor x8664 present.
That's all I would have to do there is present and then let's see I wouldn't actually need this could just be if descriptor type is present for the moment. Let's change that.
Um, if descriptor type Equals I was at table or page before index contains table because this is duplicate child table.
So this is specifically assuming that there is a that the lower level is a table.
So, what I could also do is um is block.
I could define a a custom type for the descriptor and then make those um utility functions, accessor functions, whatever you want to call it. Um, I do actually already have a type if table entry descriptor flags.
I can say type table entry descriptor equals U64.
And then I could do um the um uh why am I drawing a blank on the word Uh implement implement um table entry. the scriptor um utilities for table descriptor and then inside that right.
paste.
That should be outded.
And I see that's why that happened. Okay, let's see. And then I need to do a trait, right? Um and that's going to be is present.
Let's see. Is that just U64?
Oh, that should probably return a bool.
I'm should know bool and is block 64. Well, actually this should be a specifically this should be a table entry descriptor and it should return a bool.
Okay. And then Oh, of course.
There we go. Okay, so this should be a copy.
Paste paste.
All right. And descriptor and table entry descriptor present greater than zero.
I should probably put not equal to zero because don't make assumptions about signedness, right?
So then I'm returning if I'm extracting that one bit and just saying not equal to zero is going to specify whether that bit uh you know is set or not. Otherwise, what I'd have to do is I'd have to bit shift this down to be the lowest bit and then cast it to a bool. Uh, but it can't be cast. So, it would still have to be a two bool. And then I would have to write a two bool.
Uh, or I'd have to I'm assuming that two bool would even work.
Missing type.
for function parameter. Okay, so I need to put the name go away.
Okay, so now I have a table entry descriptor type with two accessor functions.
Is present is block and then top level table.
Okay, that's where I'm jumping to duplicate child tables.
So nothing here up above here is affected by my change.
So then we're going to go down to here table.
Okay. So this is let descriptor.
Uh okay.
So instead of U64 Nope. Undo. This should be table entry descriptive.
Okay.
Copy descriptor to same index.
All right. So now read descriptor.
All right. So now I should be able to just do descriptor.is Let's see if I Oh, it's still not catching that. Why is that not catching?
We think about this here.
All right. So, type table entry descriptor trait table entry descriptor utilities implement table entry descriptor utilities for table entry descriptor.
That should be correct.
I wonder if this is just not updating yet because there's errors below it.
Let's delete that.
If Descriptor is present.
And the scriptor is uh is block.
Um Well, does it even matter if it's a block or not? Let's think about that today.
Duplicate child tables.
So I'm going from old top level table to new copy descriptors to same index. So I I copied everything in the top level table.
Now duplicating L1 tables.
I'm in the L Yeah, I'm in the L2, which is my top level table, and I'm going down to the uh L1's to duplicate those. Right.
Duplicating the child tables.
Okay. So in the old one when I go to duplicate the child table I read the descriptor if it's table or page. In other words, if it's not invalid and not a block cuz the entry has already been copied, right?
Copy descriptor to same index.
Let's see what am I doing here.
If it's a table, I'm creating a new table and copying all the entries from the old table to the new table.
So, if it's invalid, I don't need to do that. If it's a block, I don't need to do that.
Okay.
So descriptor is present and not descriptor is block.
So it has to be both valid and not a block descriptor get PA from descriptor.
So this is going to be a table entry descriptor.
It's going to return a U64.
So this is going to be if descriptor is present.
If descriptor is not present.
And then what are the 0 1 2 3 4 5 6 7 8 9 10 11 So 12 through 51 One.
Heat.
This is 12 and then this is up to so let's see 16 so that's 48 so um that's uh 47 so 48 49 50 51 so I I need another F here.
Right.
Strip off the bottom 12.
Yeah.
Let's see. In the old version here, what did I have? get PA from descriptor. So if get descriptor type equals zero, in other words, if it was invalid, this is using present to indicate whether it's valid or not, but it could also be Well, yeah, because if it's not present, the other 63 bits can contain whatever I want it to. I And it's still not going to be a valid virtual or physical address. So, okay.
Let's see. Um trying to think why implement GUID.
trait brilliant utilities implement. and pretty sure that's the same.
So trait with function prototypes function.
Oh, did I have to pass self?
Is that what I missed?
Okay, let's change those to sell.
And then these No, not the right.
What was the um I've forgotten the uh hot key. Uh let's see. D for next. I thought it was E.
VS Code hotkey.
Yep.
Multiple selection.
K.
KD. Well, that worked.
Okay.
So, just to do that again, I want to select this one and this one. I don't want that one. So, I go command D, command K, and then D.
I thought there was a different uh command for that too, but maybe not.
Anyway, this shouldn't sell.
Okay, now that should work.
All right, so we've got that's working.
Okay. So this get PA from descriptor if descriptor is present not then return zero otherwise return that which is modified to support 52bit.
Then down here if descriptor is present and is not block.
So then we're getting the PA allocating the next free page copying it down.
Okay, that should all be adapted now to the new architecture and then we're going to continue.
Okay, we're down to here and my tailbone is hurting. So, I need to stand up for a couple minutes. So, I'm going to take a quick couple minutes break, probably less than 5 minutes, and uh then I will be uh I'll be right back.
Probably three or four minutes.
Okay, I'm back and let's see. Is my microphone working? Yes, it is. Okay.
The uh microphone seems to cut out sometimes on Linux and last night my headphones cut out and wouldn't work.
Had to reboot. So, got myself a string cheese.
Okay, next level.
If top level is this would be actually greater than two, then we're duplicating the two is duplicating one. Three is duplicating two. Okay, so this is minus.
So we're at three here. Top level has to be at least three.
So then we're duplicating the Yeah, cuz we already duplicated the L1 here.
So this would be the L one or L2.
Is this right?
top level is less than three, then we're duplicating the L3.
Top level is less than two, so it has to be one.
Then we're duplicating the L3. Okay.
So again here greater than one minus one greater than 2 minus 2 and then this will be greater than 3 - 3 Yeah, because in the case that if it's the case that top level is four, then we're going to be duplicating the L3 tails because that's the second highest level.
And then we're going to be duplicating the L2 tables and then the L1 tables.
L1 is the lowest level on Intel. It's backwards.
Okay.
Okay. Read descriptor top level table.
All right. So this is going to switch to this is if descriptor type is not table or page continue.
So what that'll switch to is okay. So if this is not table, so that would mean it's either invalid or a block.
Okay. So that's going to be descriptor is present not.
So is not present or descriptor is block.
So if it's invalid or it's a block skip.
Okay.
Then we know that it is a valid table.
So get PA from descriptor duplicate child tables.
The table is current level.
So this will actually be minus instead of top level + one. They're now going the other direction. So this is top level minus one.
And now we're getting to if top level instead of less than one, now we want to say greater than three.
And this should be minus let descriptor equals read descriptor top level table index.
Okay. And then we're not going to do descriptor type anymore.
Instead, what we're going to do is if outer descriptor is present, not outer descriptor is block.
Then we get the PA. That's still the same.
Then we go to the inner.
And same thing here. We're going to we're going to remove inner descriptor type.
And we're going to replace this with inner descriptor.
is present not or inner descriptor is block.
Okay.
And rust automatically shortcircuits comparators. So if inner descriptor is not present, if inner descriptor is present is false, uh rust automatically shortcircuits there and it never evaluates this. So this is never going to cause uh an issue. That's an inherent feature of rust which is really nice. Um then let inner table equals that duplicate. Okay, then this is I cannot double click on a plus sign apparently. There we go. Minus.
Okay.
-3 -2 - 2 - All right. And then allocate a page per kernel stack.
Okay, that'll be the same still.
Next, we're inserting virtual remappings. This probably is all going to be the same.
The the indexing is the same. We've got four uh table levels here.
Now, now this is interesting though because they're numbered in reverse. So now I have page.
So I should actually reverse these.
like that because because I am referring to index by uh my initial intention was to use the the index of index, you know, the uh to the bracketed index number of index to refer to the um table level. And since the table levels are reversed now, so this is actually this would actually be oh and then I need to add a dummy value. Uh, I just need to add a zero because then it would be index 0 1 2 3 4.
So index bracket 1 is now the L1 table which is the lowest level table which is so the tables don't go 01 23 anymore.
Now they go 4 3 2 1.
Then I'm going to need to refactor all the references to index to refer to the correct levels though. So virtual address zero.
Okay. So index I highlighted index so I can see where it Ah, here we go.
Yeah. So this this is now four 3 2 1.
Let's see where else index is referred to.
Mir index. Uh okay. So that that's not relevant. Um, let's see. Does the x8664 equivalent of mir register page attribute table which is accessed via model specific registers.
MSR 277 64-bit register functions similarly to MirIR in conjunction with the PAT MSR bits 7 4 and three in the page table entries act as an index to determine the memory Type comparison ARMS MIR provides up to eight encodings chosen by a threebit index.
PAT provides eight encodings indexed by those three bits. Okay.
page table entries in the PAT table entries. P A 712 and 63. Is that what the other page said?
No. See, this says 74 and three.
each 64bit entry in the page tables maps virtual memory physical memory p and paging levels fourle paging uses PML4 PDP page directory and page table.
PAT can be applied at different levels, often used on 4K PTE or large page entries.
For 4K pages, the PAT is bit seven.
For 2meg and 1 gig pages, the PAT bit moves to 12.
T is supported then PT along with PCD and PWT shall indicate the memory caching type otherwise it is reserved and must be set to zero.
Page attribute table per page table entry memory type selection PAT register is a 64-bit model specific register at address 277. It is divided into eight 8bit fields each holding one of the memory type codes above. Defining the memory type selected by each threebit index. The three bit pad index for a given page table entry is formed from three bits index PAT bit x* 4 plus pcd* 2 plus pwt where pwt the page write through and pcd the page cache disable were the original caching control bits present since 486 and pat bit is bit seven of the page table entry or bit 12 of a 2meg one gig large page directory entry.
Okay. So um So bit three was right through.
Bit three was right through. Okay.
Bit four was cache disable non-cachable.
Bit seven.
Bit seven indicates a block type.
At 12, Intel specifies a default PAT mapping.
PA0 equals WB.
PA. Okay, I get it. PA1= Okay, operating systems may reprogram the MSR.
Linux, for example, sets PA4 equals WC so that right combining mappings can be created.
Okay.
So right through right through PCD and then PAT. So those are the bits. So from least significant it's 347 and then what are the PA0 is write back normal cachable RAM reads and rights are served from the cache. Dirty cache lines are written back to memory only when evicted. This is the default type for system DRAM.
Write through reads are cached.
Writes update cache and main memory simultaneously.
Used where right snooping is important.
Write protected reads are served from cache. Writes bypass the cache and go directly to memory signaling other processors caches to invalidate their copies. It's used for ROM emulation and regions that must be read cached but not be right cached.
Write combining writes are buffered in a small set of right combining buffers and sent to memory in larger bursts. Reads are not cached and bypass the cache.
Okay. And then uncashable. All accesses go directly to memory or the device. No caching or write buffering. Memory ordering is strictly sequential used for memory mapped IO where device register side effects must not be suppressed.
Okay. So uncashable is zero.
So in other words, I need to add um definitions Guess this should be 5 to one, huh? 5 to one.
And So then uh memory cachability flags. uh attributes are encoded across three repurposed bits to create a threebit index equivalent to the AR64 M AI. They are register.
It's three, four, and seven are we for purpose.
I think I want to let's make that a semicolon line.
All right. Okay. So then I need to add a um definition for bit 12 is normally part of the It's normally part of the memory address.
However, in the base of blocks, huge pages.
It is repurposed to be a uh to be the block type index bit.
Yeah.
No con.
Um, let's see.
block.
Uh, let's just go a block huge.
And that is 0 B1 bit shifted 12.
Actually it's not uh in the case of blocks huge paces. However, in the case of um uh what's it called? Um Pat uh page attribute table is active.
a feature named a page attribute table at is activated.
The memory cache ability attributes are encoded across three repurposed bits to create a threebit index equivalent to the AR64 MIR register.
Bits 3, four, and seven are used for this purpose.
least high significance and uh bit 12 is purposed to indicate drugs in this case There.
And then I'm going to um let's feel like I want to move this above and then have this below and then be this here. Um, it's three, four, three, four, and seven are purposed, the pat is the AR64 M I say table entry descriptor. Nope.
X uh PHP mask.
That is 0 B um 0 1 actually no it's seven I'm uh so it's one 7 6 5 4 3 2 1 zero It should say most of the time most systems on most systems Bits 3, four, and seven are repurposed to become the PAT, which works the same as the AR64 Mir index. That is those. And then um let's see, I should make a these are table entry descriptor flags.
Um so let's make a utility function function.
Uh get a t get p index. Let's do that.
And that's going to return uh a U8 and that would not be would be be that.
And then actually let's uh duplicate down and let's make that set.
And let's put some spacing in here.
There we go.
That's looking better.
Okay, now we can just do quick fix. Implement missing members.
There they are.
And oh, and that's not going to return a U8. That's going to return a a bool.
Uh, does it even need to return an error? It's going to drop into assembly, right? So, let's just let's just not even have that return anything.
Um so get pat index from self set PAT index. That's actually going to take a an additional let's see self uh u8 and does that need a name is that what yeah parameter name um so let's call it index X and that's just not used correct. Okay.
All right. So get pat index. Um so we already have a descriptor in memory.
self is a U64. So all we need to do is go self and um table entry descriptor exit six. Uh what's the uh bit three is right through right through.
Okay. And that's going to be it shifted um by three.
Let's see. I could also do this.
uh using boolean logic.
You say if self and table descriptor right through.
I could also We could also define it as a mutable variable variable and just do two additions conditional additions instead of doing bit shifts and if I don't do bit shifts then I could actually the it would even be change proof proof right H okay so let's do self and write through uh times one times one. No.
P A T bit.
page right through.
So wait, page right through plus PCD times 2.
So that formula does not give values 0 through 7.
What this gives is this would be three Um 38 and 28 and then 11 31 39.
So wait. Okay. So, uh, x86 64 p bits bit index values, the combination of PAT, PCD, and PWT access. A threebit index, eight combinations.
This is how I would expect it to work, but how so the way that Wikipedia Th this does not make sense. This has to be the value of the bit. That'd be 1 plus 0 or two plus 0 or 4.
That's Yeah, that's a really stupid way to to print that. Um, so back to what I thought I was actually going to do. Um, so it'd be So let's just bit shift that by three to make it bit zero.
And we're just going to bitwise or with this. Don't make it more complicated than it needs to be.
Bitwise or with not right through but non-catchable and that is bit shifted by four.
Oh actually that would be that would be bit shifted by three.
Because you take this is just bit three. So shift it three to put it in the f in bit zero position. Then this is bit four. Shift it by three to put it in the bit one position.
and then or it with once again this is going to be block and that's bit seven and it needs to be shifted to bit two. So that's five.
1 2 3 4 5. Yeah. So then I have So now I have a a bit zero, a bit one, and a bit two funky.
Um, and that is actually that should be the return value right there.
Found U64. Got it. Okay. Um, so all of that then needs to be surrounded by that so that I can cast it to a U8.
Okay.
And it's not really necessary, but for readability.
Let's do that. Uh, nope. Not that.
That I don't know if that really increased readability because I have the whole thing in parenthesis. Let's undo it.
I do think that keeping these helps with readability though because if I remove those then it's bitwise shift bitwise or and bitwise and bitwise shift bitwise or it's a lot less readable without those. So I think that's our happy medium. And then uh this one um set. So, we're going to let um flags equal Let's see. I don't need to I don't need to explicitly. I'll just cast it. Um, let flags equal.
index and 0 B1.
Let's make it 001 for clarity.
It shifted left by three.
Bitwise or with index bitwise and with 0 B 0 1 0 bit shifted left by three. We're reversing the process bitwise or with index bitwise and 0 B 1 0 0 shifted left by Five and then all of that as U64.
And then let's see. I actually need to be passing in I need to be passing in a mute mutably borrowed table entry descriptor.
which needs to have a name descriptor.
uh and that's immediately borrowed table into descriptor.
Okay, so now I am calling self I I I am a descriptor. Oh wait, duh. I am a descriptor. Never mind.
Never mind.
Swedish chef not impressed.
So this is actually self equal um self bitwise Let's see. Oh, so I need to clear the bits first.
Interesting. Okay, add a line.
So, self bitwise ended with zero B.
Correct. Bits three, four, and seven.
Uh and actually those should be those should be reversed.
Okay. So what is I need the bitwise inverse of that. Um bit and assign is not implemented for U64. Okay, fine.
Self equal self and I don't know if I can just do not probably doesn't work that way cannot oh okay so I need to have mutably borrow itself Can I do that?
So then I need to dreerence it.
and I would need to dreference it there performs the unary unary not operation. So true becomes false. Let's see.
1 U8 becomes 254.
0 becomes 255.
So that is actually possible.
Well, that's very cool.
I just thought I'd try it to see if it worked. I've never done a unary knot in Rust before, but it works.
That's very cool. All right. So, self equals self with those three bits cleared.
Then self equals self and then all that.
And of course the cast is necessary because otherwise that would be a U8, right?
Because all this together is a U8. Yep. And I just did that to double check that my parentheses are all in the right place.
Okay, so that's basically so set pat index.
Um, so we clear those three bits and then set them equal to the new values extracted from index.
That's pretty slick. I like that.
And self has to be mutable in order to do that.
So if we try Okay, this is where we are.
If we just try um let test descriptor equal um let's just let's just pick a random uh U64 value and then we can do uh test descript.
do a let result equal um test descriptor dot get get pad index that works. And then if we say test descript add index to five.
Huh. That's interesting.
See that should not work because test description is not mutable.
Although I wonder I wonder if once I clean everything else up if it'll come back and put an error on that.
Mir index uh that was that would be replaced with um that would become a PAT index.
And how do I let's see PAT Do I trust Wikipedia? Um, x8664 default path.
You know what? Uh, let's see. I have in here.
Let's see. Is it I think it's actually in my main function. Um, no, not that one.
This one.
Okay, it's not there after all. Um, is it uh Oh, I know. I had a I had a print mir values function.
Uh, I think that was in Maine.
And I think I removed it.
Yes.
Which is fine because I don't want to do that again. But what I was uh I was going to put the code in the same place if it was still there, but it's not there anymore. So, let's run file.
Let's put it right here.
Uh values of P A T indices.
So then we're going to need a core arc ASM And let's see, that's not going to work unless it's wrapped in a uh unsave.
Of course, you would wait. Nope.
There we go.
Okay. And then we're going to need uh let's see what is the um Okay. Okay, so the page attribute table is an index.
The PAT register is an MSR at 277.
Okay. Oops. Let's uh open that in a new window. Um using MSRS reading and writing is handled by the read MSR and write MSR instructions respectively. As these are privileged instructions, they can be executed by the OS. Use of the Linux MSR kernel module creates a pseudo file blah blah blah user with permissions r. Okay.
memory type range registers and the address range registers. And those are Okay. So, write MSR at address 277.
The pad is accessed via I32CR PAT which is MSR 277.
Instruction format takes the 64-bit value from EDX EAX and writes it to the MSR specified in ECX.
contents of the EDX register are copied to high order 32 bits and the contents of the EAX register are copied to low order 32 bits. On processors that support Intel 64, the high order bits of each one are ignored.
Okay.
Oh, let's see. I'm actually not wanting to set it yet, am I? I'm just wanting to read it. So, let me move ECX277 read MSR. EAX now contains low 32 bits.
EDX contains high 32 bits.
Right.
My tailbone is starting to hurt again.
Lean forward for a minute before I have to take a break.
edX and EAX. Okay.
All right. So, we're going to have two 32bit values to extract.
So, let's see. We're going to need then we're going to need let mute.
I just thought of something.
Okay, those are mute. Okay, perfect. Uh, so we're going to need let mute. Uh, e a x and e dx.
E a x equ= 0 u64.
Let mute e dx equ= 0 u64.
And we're going to need that's probably good.
So, we're going to move into the ECX the magic number of 277.
Then we're going to read MSR.
And now we need to let's see the contents of EAX and edX. But those are just going to be out.
Uh oh, but I need register. I need to copy them into registers. So let's go uh move into register zero uh of your of the compiler's choice. the E A X and move into the register one of the compiler's choice the EBX.
And then we're going to need an out register EAX.
And we're going to need an out register for EX.
Okay. So, type 277 into the ECX read and then copy the contents of EAX and EDX.
EDX is the high Okay. And then suppose I could just say print values of pat.
And uh next we're going to um let KT equal edX.
It shifted 32 plus EAX and then we're going to do a uh super print string.
The string we're going to print is um contents of a register.
Oops.
false and I'm going to go super print new 64 and that's going to be P and group.
Okay.
And I think I'm going to go ahead and delete these extra lines here just so that it's visibly clearer that that is the debug section that I'm going to delete later.
Okay, now we're down this far.
Still going. All right. Um, we're still going to test that. Okay.
So, pat index. Uh, so theoretically the default What's that back here? There it is.
So, this is this AI is quoting Wikipedia. I would like a corroborating source.
But I'm going to print it out um for myself, I guess.
All right. So PA0 is right back uncashable minus three is uncashable.
All right. So let's use three one bit shifted two. That was for the Mir.
Oh, got it. Okay. So that's where it's positioned.
All right. So let's just use pat index as a you know what let's uh well yeah let's use three and then write protect flags.
Okay. If page one that's the physical address is equal to kernel stack VA then don't do right protection otherwise I'm doing right protection.
Okay. And on this this architecture let's see what's the right protection.
How does that work? All right. So present writable. Um, so I have to enable write access.
So the default is non-ritable.
I guess what I need to do is just look at the flags, right? Um, well, this is specifically for right.
Okay.
So if it right project flag is if it's uh page table page it does not need to be writable.
So zero equals writable.
So this is backwards, right? So if it's kernel stack va, I need it to be writable.
No, not true.
Writable.
Excuse me. And then otherwise I need it to be zero cuz this is now a right enable flag.
So if it's if it's the kernel stack.
Yeah, because I want the stack to be writable. The page tables are not writable. Okay.
And then what else do we have? Let's see. We also need to add go away.
present page zero with the new page descriptor present.
Basically marks it as valid and Writable.
So write enable flags allow R3 access. Um that's going to be No.
Let's see. Let me look at this one here.
Okay. So I had it right protected.
I had it marked as in use.
Intersharable.
non-cashable.
Let's see.
I should probably turn on right through and non-cashable. I mean non-cashable should automatically imply right through but let's uh oh I remember now um three and four those are no longer used are they because we're going to the pat so never mind so three and four don't matter I accessed and dirty so The CPU marks it as dirty all by itself on this system.
We should mark as it accessed.
Oh, what's happening?
present, writable, accessed and then dirty gets modified by the uh CPU. Seven is repurposed.
global. We do well.
Yeah, we do not want the page tables or the kernel stack to be globally accessible to the user. So that's a no.
Execute never.
Uh that should be that should be set. So I had that here.
Right. Protect.
Well, I probably didn't have to uh Oh, that's right. I originally had uh not execute never marked, but then I changed it to match the formatting that the UEFI firmware was using. That's right. Um okay, so we don't need that.
Colonel usable bits.
Okay, so the PAT bit mask block. All right. So, that's how we do blocks now. Um, okay. So, I don't think I need.
So, we're going to mark it as valid in use. And uh if it's the stack, we'll make it writable. Otherwise, let's see.
I I did write protect, but is that um let me see here.
Bit 62 set denies right access from all exception levels.
Okay. So, these are these are kernel pages specifically. This is not the these are not the page tables anymore.
These are the kernel pages that I'm setting up the Got it. Got to remember what where I'm at. Okay. Yeah. So this is um inserting the virtual remappings into the page tables.
So these the page descriptor here is specifically going to be for the kernel unless it's the kernel stack. So if it's the kernel stack, we mark it as writable but the kernel pages are right protected.
Got it. Okay.
H. And that's an interesting that might be wrong because see right now I have Okay. So if there's any if there's any compiled in uh any values that are known at compile time are are compiled in to um to the sections of code with each function that they belong to. So those don't change those don't change during execution because they're known at compile time.
Anything that is a variable gets put on the stack which is writable and then I don't don't have a heap yet. Okay. Yeah. So that that is good. So I want the kernel pages to be non uh to be write protected which is the default. Um the stack we want to be writable. Okay. And then so the other thing I had on here was accessed inner sharable write protected.
Okay. And then set the index. Um let new page descriptor.
Okay. And then so we're going to make that a let a mute.
Or actually it doesn't need to be mute because we're only going to we're only going to do one more time. So, we're going to let new page descriptor again equal new page descriptor dot uh stat pad index.
pad index.
That is what we're doing. Set new page just and then why does that not return a Oh, well that's not going to work unless it's mutable anyway, right?
Ha, that's funny.
So that that's another reason to make it mute then. So make it mute. give it the initial value and then set.
Oh, and that doesn't have to be an assignment anymore. That is simply a that is a mutating function.
Oh, I suppose I could uh well, no, set kind of implies that it's a mutating function, doesn't it? uh selfmutating.
Otherwise, instead of set pad index, it would be like, you know, return a copy um a copy with different pad index and you'd have to re reassign it the result.
But I think this is cleaner.
And in fact, page zero as U64. I could actually just add this right here.
So if I if I put that in parenthesis and just put set pad index pad index and I don't know what what is This move right enable flags.
What?
Oh, because that's not a ah that's not immutable. That's why.
Okay. So, it's cleaner cleaner to do it this way because if I try and call if I try and use this as a U64 and call set pad index on it, it has to mutably borrow itself in the middle of the function. And that's just ugly, hard to understand, and it's hard to read and figure out what's happening. So this is much more readable.
New page descriptor equals page zero as a U64 with present writability and accessed set and then set the pad index.
That's easy to understand.
Okay. Then we've got So this is actually L1 table. No, no, it's not. Oh, well all of these uh there's not going to be an okay. Top level table doesn't need to be mutable.
So this is going to be L4.
This is going to be three, two, one because they're backwards.
So this is going to be the L4 table and this is now going to be if top level is greater than or equal to four.
Let and this is going to have to be L0 descriptor.
Let's see what if we just do L0ore.
I I think I'm going to I'm going to get out of I'm going to miss something if I do large batches across multiple lines.
So, let's just take it line by line. So, this is going to be an L4 descriptor.
L4 table index 4.
And then I don't need descriptor type anymore, right?
What's that getting used for?
Down here.
So, let's remove that.
This is going to be L4.
L4.
Now this is going to be L3.
And this is L4.
L4. Well, descriptor type not equal to table.
So in other words, it's saying that it's either invalid or a block.
So is that's not what I thought I chose is present.
So that would be not if L4 descriptor is not present. So that's if it's invalid or L4 descriptor is block.
So if it's either invalid or a block.
L3 table equal Alec next page allocated a new page.
Uh if and this is again L4 descriptor is block.
Breaking down a block into smaller blocks. Count 0 to 512. New L3 descriptor is L4 descriptor off uh plus a gigabyte uh multiple gigabyte.
Okay. Yep. Yep. That's right.
This one is L3.
L3 inserted L3.
New L3 descriptor at index.
Okay.
else.
Uh this is actually not present.
And then I don't have my zero innit function. That's the last one on this file that I hadn't copied over yet. Just do this. And wait, it's this command.
There we go. Copy.
end paste.
Okay. And zero init page for index page offset index right value zero. Okay. So that'll still work. No difference there.
Okay, this is where I am. So, this is going to be L3 table.
This one's L4.
This one's L3.
Oh, new L3. Let's see. L4 descriptor.
L3 table as U64.
Okay. So then what do I need to be added for for this to be valid on this architecture?
I'm going to first need I'm going to need present and writable.
Why are you up there?
present. I'm also going to need writable.
What else do I need?
I do not need user access.
And then three and four are ignored. um our other used accessed.
Okay, let's mark accessed global And I'm not marking the execute never uh because UFI didn't do it and uh I'm matching the configuration.
Um okay, so accessed.
How did I get into there? I don't understand. I do not remember pressing alt but okay. So we're going to take the L3 table that we've just allocated the memory address of it and we're going to mark it as valid and present uh writable and in use.
And then we also need to give it it needs to be mutable because we also need to give it a set index of uncashable which is three.
But let's let's just make that that index.
Okay, then we are writing the descriptor for L4 table index 4 to new L4 descriptor.
So new L4 descriptor, new L4 descriptor add index and then we need index 4.
Okay. Now next we're going to do the L3 table and this would be if top level is greater than or equal to three.
So this will be So L1's going to now become three and this is be three.
Let's see this whole line will get removed.
This is now three.
This is now three.
And this, let's see, two.
two would actually be the same because we're going in the opposite direction because it was 012 and now it's 4 3 2.
Yeah. So two stays the same but one switches to three.
One switches to three.
Oh, and this is actually paste. But this needs to be three and three.
Two stays the same.
Allocated a new page.
One becomes three.
Oh, and this is the same.
This is actually L3 descriptor is block.
L2 stays the same. One becomes three.
2 megabyte blocks. That's correct.
Write descriptor two stays the same. New two stays the same. Inserted two to two all stays the same.
And this is now not present.
And we're going to zero in it page L2 table.
Okay.
And then we're doing one becomes three.
Two stays the same.
And the flags that I put on it are these plus this and this is a three and this should be mute.
This becomes three.
This becomes three.
This becomes three.
That's three.
That's three.
That's three.
Okay, that should be that level handled.
Let's just look it over again.
Looks good. Oh, but I also need to mark it as a block. Um cuz the let's see breaking down into block a block into smaller blocks. That actually needs to be so the descriptor L4 descriptor.
If it's a block, it would already have bit 12 set.
So, I'm taking the existing one, keeping all the same flags, and just offsetting it in increments of gigabytes.
Okay. So, that's fine. That should still work then. There aren't any other it'll already be marked as present. So, I'm keeping that.
Whether it's writable or not, I'm keeping I'm keeping I'm keeping all the attributes and just taking a 512 gigabyte block and breaking it up into 512 individual 1 gigabyte blocks.
Okay, so that should still be okay.
And same thing here. Then I'm taking a 1 GBTE block.
I'm keeping all the flags and uh intact and just breaking it up into 512 uh 2 megabyte blocks. Okay.
All right. So now we need to handle actually the L2 table is correct because we're going the other way. 432. We went 012. Now we're doing 432.
Okay. So handle L2. So L2 is going to stay the same, but this is going to be greater than greater than or equal to two. L2 descriptor L2 table index 2.
Okay. Then we remove L2 descriptor type L2 L2 that's correct.
This three becomes one.
two stays the same two. Okay. Descriptor type.
So that's going to switch to this. And that needs to be two.
And that needs to be two.
Then this needs to be one.
And then this one just needs to be is block.
This one's going to be one.
Stay at two.
This is going to be potentially. Let's see. This will be a page.
No, no, no. This is still to an L3 table. So, this will be an L1 table. Okay. All right. So, new L1 descriptor. So, we're going to copy these.
Paste.
We're going to make that mute.
Copy that.
paste there. Make that one.
Is that right? L1.
Yeah. new L1 descriptor.
Oh yeah, cuz I was looking Breaking down a block into smaller blocks. Okay, I did this wrong. Go back.
If L2 descriptor is block breaking down a block into smaller blocks.
So this is going to be the new L1 descriptor and then L2 descriptor plus 4096. six.
Okay. So, I'm taking a 2 megabyte block and I'm breaking it down into individual pages.
Maybe I did do it right then. Okay.
because uh on on 64-bit ARM I was uh the block is a different type.
So I couldn't reuse that here.
But what I could do here since on Intel all the all the flags are the same except for bit 12 to indicate that it's a block.
So I can take L2 descriptor Clear the 12th bit, then increment it page by page.
It's going to inherit all the other flags.
So I don't have to mark it as a page anymore. I just have to unmark it as a block.
So this should be a bitwise and with zero B1 bit shifted or well actually I already have a definition for it. It's it's table entry block huge that 4096 that is bit number 12.
There's 0 through three. This is 0 through 11. That's bit 12 because uh 0 through 4095 is the page.
4096 is the first bit of the address.
which if it's 2 megabyte, you know, it's a 2 megabyte block. So in actuality, not only are the first 12 bits zero, but actually the next eight bits are zero as well. So they just repurposed this first bit here of the additional eight that they know are going to be zero.
They repurpose that as the uh the bit to indicate that it's a block.
Interesting.
That's a good way to do it though. I mean you cannot have a block unless uh unless you have at least that many. So you know that those next eight bits are going to be zeros.
Oh, and this is actually not because I want to clear.
So I'm going to take L2 descriptor. I'm gonna break uh add the number of pages off as an offset and then I just need to clear that one bit.
So I'm using the uny.
Yeah.
Next write descriptor. So this is going to be L1 table.
at index count and that's going to be new L1 descriptor inserted L1 descriptor. Oops.
New L1 add index. Okay. And then this this means not present.
And we're going to zero in at the page L1 table.
Okay. So, we're handling the LT table.
If our top level is three or more Let's see. Zero in hitting the page.
That uh that still works for setting invalids, right? because um except that uh that breaks up any additional that's interesting. So um that does not does not preserve any information.
Oh, but if if the present bit is not set, then the other 63 bits are open for me to use for any purpose I want. So, it doesn't matter whether anything's set or not. Okay. So, just zeroing it out is is good. Okay.
All right. So, new L2 descriptor. We're going to keep L3 becomes L1.
Um, and then this is copied from here and that needs to be mute.
Why is this?
Well, it's because they haven't got there yet. Um, and then we need new L2 descriptor. Set pad index 3.
Uh, set pad index. Add index.
Pad index. Pad index.
Okay.
Write descriptor L2 table index 2 new L2 descriptor. Yeah, that's still correct.
All right, so let's re review this here.
If top level is three or more then we need to copy the L2s or two or more. Right? Then we need to Yeah.
L2 L2 one table equal get PA from okay that'll get fixed down here. Um L1 if L2 is block break up the L2 into a bunch of L1 pages write them each in zero and knit them if they're invalid and then add back into the L2 the new pointer to the L1 one table.
Okay.
I don't know why why are you saying value assigned to L1 table is never read because it's read right here.
That's because this is conditional so it may not reach it. Okay, that's all right. We'll fix it down here. Um L2 new L2 descriptors to the L1 table. Yep.
Okay, I think everything's good.
So now we're going to write the new page descriptor into the L1 table.
This is the L1 index one wrote new L1 descriptor.
Uh new page descriptor at index index one.
Okay. And now we're going to say ready to update CR3.
Install new page tables to CR3.
And this is now going to be x86 assembly.
Um, so we are going to need to uh it's just the I know I searched this before and I'm pretty sure it's just a move command.
x8664 set CR3.
I did look this up before and it just used a move command. It sure did.
CR3 must hold the physical address.
They just moved it into a register and then moved it there. Yeah. So, um, so we're going to say move into CR3 passed in an in register.
And then we need an in register of top level table.
And is that already a U64? It is a pointer. So we're going to say as U64.
Okay.
top level table. Let's make sure that's in fact that was what I used here, right? Uh top level table as U64.
Top level table as U64, passing it in to a register of the compiler's choice.
and moving it into CR3. And the act of doing so causes a cache flush. So we don't have to worry about any caching issues like we did on ARM.
That's pretty cool.
Okay. Um, CR3 has been updated.
And then, uh, this is where I was just printing out the stack pointer.
Is the stack pointer on?
It's RSP and RBP.
RSP RBP.
Okay, RSP is coming out uh in a register designated as RSP and getting moved into the SP.
All right, SP equal SP. Okay, that'll work.
All right, I think we are probably done.
Let's see. There's an error showing somewhere. Where am I? Up above.
Ah, yeah. See, now after I finished uh fixing everything else, it finally came back and detected that this had to be mutable. So, I figured that would happen. It just had to clear all the other warnings first before it could detect it. All right.
Now, I can remove me later.
Excellent.
One warning. Not in this file. No, it's over here.
What's the warning in this file?
Where is it? Ah, entry point. Jump to kernel.
Unused variable. Entry point.
Uh, it is not unused. It is just not used by the Rust code. Oh, I see. It's unused in this.
Ah, okay. So, I can fix this.
jump to kernel entry point. Um, so that needs to be.
This needs to be an unsafe and it needs to be a core arc ASM.
And then um what is the I have it in here.
Call. call is the correct function apparently. Uh so call start.
Um does that work for that's for a symbol though.
I don't have a symbol to work with. So is that unconditional branch?
Uh, I bet it's like a JMP or something.
Let's see. What is the x64 assembly unconditional branch jump? Yeah. JMP jump target directly transfers execution to the specified label or address. Call pushes the current instruction pointer onto the stack and jumps to a procedure.
That's why I did call main. Okay, so jump my label or jump address registers or memory.
Jump target is a constant offset from the current instruction.
Indirect has to be stored in a register or memory.
Okay. So, jump immediate.
Relative jumps, near jump, short jump.
Absolute jumps are restricted. Direct far jumps. These used an absolute sag offset encoded as an immediate but were removed in 64-bit mode. 64-bit absolute.
There is no single instruction. It takes a 64-bit absolute immediate address for a jump loaded into a register first.
Okay, that's the same thing that I had to do on ARM uh regarding the uh or actually right here. So that's what I had to do on ARM. I had to I had to load entry point into a register which why did I name it entry? I don't know.
I mean I had other Oh, I had stack I had stack pointer setup code in there before. That's why. So let's uh let's do that just because it makes more sense.
In register doesn't need a name. It's just a register and then we branch unconditionally to the address. And then we're going to do the same thing up here in x86 assembly. So we're going to do a jm no first. Oh yeah. Yeah. JMP to the register of the compiler's choice.
Oh, and does that need to be none uses register jump racks jump to the address currently stored in racks. Jump to the address stored in the memory location. RCX points to okay so I want to take the actual value of the register so no brackets so a register of the compiler's choice which is an in register entry point as you also need option well here let's option no return and option no stack.
Okay.
And we do not have any errors or warnings.
Cool.
So jump to kernel. Oh, so I think we can I think that might be everything now that I can actually uh I could potentially boot this and see whether the virtual memory works. Everything that I just changed.
Oh, let me see what uh Ah, I didn't realize it was after 6:00 already.
Okay. Um, so Oh, my uh my other computer froze.
No wonder I didn't know what time it was. Let me see if has anybody been talking to me and I didn't know it.
Oh, I'm sorry, guys. I the last me the last uh text message on my other computer here is Damn, everything that happened since then I did not know about because my uh computer uh my other computer crashed. It's frozen at 3:12 p.m. That was that was 3 hours and 2 minutes ago.
My other computer.
And how can I Let's see. Is there a uh Oh, what kind of emojis are these?
Let's see. Faces. Oh, there we go.
That's what I was looking for.
Okay. I've never uh I've never used an emoji on on YouTube before. I'm so sorry. So, Internet Spider, uh it's Brody Live X-Ray Expert, if any of you guys are still here, I'm so sorry. I didn't even know you were talking because I I didn't realize the other computer was has crashed and is totally frozen 3 hours ago. Oops. Uh yeah, it is completely crashed.
No wonder I kept looking over but I never saw anything pop up.
Um so internet spider said, "I'm new here. What are you coding today?"
Uh I So yeah, I'm coding a uh I'm working I've been working on the UEFI bootloadader for my exo kernel. So I'm writing an exokernel uh project and uh um and the bootloadader has to uh enable virtual memory. So the page tables uh for virtual memory have to be set up in the bootloader before you um go to uh jump to the kernel. And uh it is rust.
Um isn't it correct to say that all the flags are the same in Intel processors while they believe while they belong to a consistent architecture? Flags have distinct specialized functions for representing states of CPU. Um yeah. So I wasn't saying that the flags are different across Intel processors necessarily. What I was talking about was the flags were different across different levels of the page tables. So um on the ARM the 64-bit ARM processor which I already have working uh the top level table uh all the so the t they differentiate between um a uh block versus a table versus a page.
They have different types and actually different flags in different places. So they're completely different format of the entry. It's a 64bit entry in an array, but the actual structure of the individual bit flags that are embedded inside of that is different at different levels of the table depending on in different types of descriptors. Whereas um on the uh on the Intel side I've realized that they use the same format for the you know bit 3 is the same bit 7 is the same bit 63 is the same on all the different levels of tables and regardless of whether it's a indication of a page or a block. Um, so that was the part that was different.
So yeah, and like I said, X-ray expert, I don't know if you're still here or not. Um, there's one person watching apparently. So sorry that I missed those. I had no idea that the other computer had frozen.
I'm going to force reset it.
Oh, maybe if I can reach the button.
And actually, I think that's the first time it's ever frozen on me. A lot of times it the the graphical desktop crashes, but the apps are still running. But this time the whole computer has frozen, which I don't think I've ever had happen before.
So now it won't even turn off. I'm going to have to pull the plug on it.
Wow.
Um, if only I had looked at the clock and realized that the time has been frozen for 3 hours.
Oops.
Okay. Wow. Um, I feel bad. So, uh, Internet Spider, it's Brody and X-Ray Expert, if you guys ever come back, you know, I'm sorry. I didn't I didn't know you were talking to me. I I thought I had the chat open, uh, and I had no idea that it had crashed.
So, boo on me, but boo on the computer.
Uh, wow. Okay. So, uh, I I do need to end the stream in the next 10 minutes here, though. My mom is on her way. We're going to Taco Tuesday where we can get 83 cent tacos. So, let me pull this up.
Um, let's see. Was there anything else that I was Nope. I think I'm at the point I'm going to have to check over everything that I did, but I think I'm probably at the point where um I will be able to um actually compile and boot the kernel on x8664 for the first time. So, I will do that uh tomorrow. And I need to stop looking at that screen. Um let me make sure that now there's two people on the stream. So yeah, I had no idea that the uh that the other computer had crashed and I wasn't I was missing chat messages. So I feel bad. But um anyway, it is time to quit for today. So I I made some really great progress. I think I got an entire uh virtual memory page tables implementation adapted for the um uh x8664 architecture. So tomorrow I will be um compiling that and seeing if it actually compiles correctly and and executes. And then I'll be setting up some uh kernel uh not kernel um I'll be adapting my QMU launch script to a a new version for the different architecture. Um, and then, uh, so hopefully that all works well and I'll have I'll have the kernel working on or the bootloadader and the kernel working on two platforms tomorrow rather than rather than just one. So that'll be good progress. And uh, on that note, I guess I'm going to say good night. Thank you for everyone for watching and thank you for commenting. I I wish I had known about all of them, but the computer crashed and that's what can you do? Um yeah. So anyway, uh please subscribe, please like, and please come back tomorrow for the uh next live coding stream. Um probably sometime around noon to 1ish mountain time. That's when I usually get started, but it can vary depending on how the day goes. And um uh and if you would like to support me, uh you can buy me a coffee. and I appreciate anything. Uh, but of course, just a like and subscribe is great, too.
So, on that note, I'll take off. Have a great night. See you guys tomorrow.
関連おすすめ
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
So What's Odin Lang Even Good For
TechOverTea
131 views•2026-06-01











