 All right, let's get started. So today, we're going to talk about assembly theory. So I will first give you an overview of what assembly looks like and what we are supposed to do in assembly theory. Then we'll try to study how to design the design doc, to write up the design document by learning how DamVM works and how by fully understanding DamVM. And then I will give you some key points when you write your design document, what to address, and what to explain in the document. So this is a summary. So it has coloring questions as you. And it has design document of which these two are due this Friday afternoon. So basically, you can split assembly theory into three stages. So first stage, you do physical memory management. You allocate free physical pages inside the kernel. And that's the first stage. And second stage, you design the user address space and also the page table. And you do with all kinds of TRB management and all that. And finally, you will do swapping. So in the very first stage, where you don't need to worry about user address space or page table at all, just allocate and free physical pages. In the second stage, you can assume you have enough memory. So when you want to allocate a physical page, use the function you implemented in the first stage. You can assume that you can always get a physical page. You have enough memory. And finally, you can shrink the memory size. Now you cannot assume that every time you want a page, you will get a page. There are circumstances where when you want a physical page, there is no free physical pages available. So you need to swap the physical page out to the disk. And also maybe later on, when some user program access that page, you need to swap that page in from disk to memory. So here, this is other three stages of assembly theory. You can do this incrementally. And all the functions you implemented in the previous stages that you will use in the last stage. For example, in the first stage, you will design many two functions. Allocate a physical page and a free physical page. So it's basically the two functions you write in the first stage. And then in the second stage, you basically need to design the user address space. Then you have your page table design. In the second stage, one main function you want to write is the VM fault. How to handle the virtual memory page fault or TRB fault. And finally, when you do swapping, there are two main functions that you want to write, which is swap out a page or swapping a page. So these are the three stages of assembly theory. And you want to do this strictly in this order. You don't want to do swapping first, and then physical memory management. Because you cannot do swapping or address space without a physical memory management block ready. So you want to do this in this order. First deal with the physical memory, then use address space, then swapping. So as you know that in assembly theory, you are supposed to implement the virtual memory system. So first of all, what is a virtual memory system? What's the interface of it? What's the functionality of it? So this is what you would expect from a virtual memory system, from either from a user program's point of view or from other part of the kernel's point of view. So our virtual memory system is supposed to manage all the physical pages. You can imagine, in this scenario, the resource is that the kernel managing is all the physical pages, physical memories. And in particular, this virtual memory system should be able to allocate a physical page and reclaim a physical page. It should provide such functionality. And also, the virtual memory system are supposed to do the translation between the virtual address space and physical address space. As you already learned from the lectures, every address you use in user space is virtual address. The user always uses a virtual address. And when you actually want to get the content from that virtual address, you need to translate that virtual address into a physical address. That translation is done or is managed by the virtual memory system. And in particular, you will need to manage the TRB for the translation. So this is the interface or the functionality of the virtual memory system. These are the functionalities that you should implement in this assignment. And let's look at the interface of each functionality one by one. So first of all, we have physical page management. For this, we have many two functions. That's important. Other or others are just some auxiliary functions that you write to help you do the job. So first of all, we have a function called vmplusdrap. This function is an initialization module for this virtual memory system. This function will be caught by the kernel in the bootup process to tell the VM, hey, I'm booting up. Do you have any death structures you want to initialize? For example, you may have a big static array. This is where you want to initialize that whatever death structure you use for the virtual memory system. And then we have these two very important functions called allocated key pages and free key pages. As the name suggests, allocated key pages are supposed to give you N number of pages where N could be one, two, or three, four. MSO, right? So this is the two most important functions you need to write for this, in the first stage, the physical memory management. So we also have a function called free key pages where the caller of the free key pages tell the virtual memory system that I'm done with the physical page. Now it's okay for you to reclaim it. You can wipe the content. You can mark the status of the page whatever you want to do. So these are the two interfaces that allow the virtual memory system to provide the physical page management to other part of the system, right? Then we have two TLB related functions called tlb should done or tlb should done all. At the first stage, when you do the physical page management, you don't have to worry these functions too much or for now you can just leave it empty. You can just do nothing in these functions. So but later on when you do user address space and when you have context switches, you may want to modify these two functions to do the job that the name suggests where tlb should done will close one tlb entry or flush out one tlb entry. tlb should done all will flush all the tlb entries but for now just leave it alone. And finally we have one function called vm thought. Again, in the first stage where you do the physical page management, you can just ignore this function. This function is important in the second stage where you want to manage the user address space. So far, when you're working on the physical page management, you don't have any user program running. So nobody will trigger a vm thought. So you can just leave this function alone but you will worry about this function in the second stage. Let's take a look at what the, any questions so far about this page, about physical page management? So as I said in the slides, the functions are defined in vm.edge. So this function basically defines the interface of the virtual memory system. So we have vmbluestrap, which is to initialize the destructors of your virtual memory system. Sorry about that. You have vmthought, for now just leave it alone when you're working on the physical pages. Then we have allocatedK pages and freeK pages. You can see that the allocatedK page is, where is that? Okay. Oh, sorry. I just keep this configured. So allocatedK page is take one argument which is how many number, how many pages that the caller want to allocate. And freeK page is take one argument which is where the initial address of the physical page. From this to what can you tell from the, what can you say about these two functions? So when I say I want to allocate four pages, can you just return me any number of arbitrary four pages, scatter around all the physical address space? That has to be continuous, right? So because allocatedK pages just return the start address of the first page. So implicit assumption of the allocated pages is that these pages has to be physically continuous with each other, right? Say I want to allocate four pages, then you have to return four continuous pages instead of just random four package, random four pages all around the physical address space. So similarly, when user want to free these pages, all you have or all you can get from the function is the start address of the chunk of pages, right? So this give you a hint, right? When you receive a freeK pages call, the only thing you get is a start address of the physical pages. A natural question to ask is, how do you know how many pages to free, right? That's something you need to consider when you do this physical page management. That's just one of the questions you need to consider. And finally, we have one more offline here. And finally, we have this TRB related functions, which I suggest you to leave it alone in the first stage. So this is the first part which related to physical page management. And the second part of the interface is related to user address space management, which basically the AS on scope function family, right? Some of them you become very familiar with in assignment two, like AS create, AS destroy, AS copy, which you use in fork, AS activate, which you use in child of four country, AS define stack, which you use in C's exact V, right? There are some other functions that you need to understand what they are supposed to do and why you may want to change the implementation of it. So this, so create, destroy, copy, activate, define stack is quite obvious. You know what it does. AS define region and AS prepare load and complete load is called during the loadf process where this function tells you that, so in loadf, you give a binary, user binary. The loadf are supposed to load that binary into memory, right? You do that in exact V when you want to execute a new user program. So AS define region basically tells the virtual memory system, right, that this user program has such a region with a starter address and with the length of the region. So the AS define region is the interface of the virtual memory system to receive how many regions that the user program has. And also prepare load is to tell the virtual memory system that the loadf function now are ready to load all this content into memory and complete load tells the virtual memory system that loadf is done with the loading part. So let's take a look at the loadf function and see how these functions are called. I don't know why I have it here. So let's open the loadf function which is in assembly you will find yourself that you need to understand the implementation of loadf to be able to implement the three functions correctly, which is define region, prepare load and complete load. So here loadf basically do load the user binary in a loop where it read all those regions. So loadf will first tell the virtual memory system that user program has this, this, this and regions. There could be a variable number of regions that user program typically a program has too, which is code segment and data segment, but some user program may have more regions or segments. So loadf first call AS define region to tell the virtual memory system where the region is. You have a virtual rest start, you have a memory size which are size of the region. You have the permission of the region. Different regions have different permissions. For example, the code regions are read only, right? You cannot write to a code region. They will overwrite the user's code. And the data region is read or readable and writeable, right? So later on when, so for example, when you get a page fault, say a user program want to write a virtual address. The first thing you want to figure out is that which region that virtual address belongs to, right? So that you can check the permission. For example, if you have a right page fault to a page, and then you figure out that this page is read only, then you should reject such page fault by, I don't know, maybe panic or kill the user program, right? So this is the info where you want to record or you want to record what's the permission of the region or what's every information you need for each region. So later on, when you have a page fault, you can check against that. Or another case would be you have a page fault on a virtual address, but you find out that that virtual address doesn't belong to any regions. Then what do you can infer? That's the invalid user address, right? You should reject that instead of allocated pages for that. So that's AS defined region, which basically tell you which regions are there. Then you have this prepare load, which just to tell you the virtual memory system is, just to tell you the loadf program is ready to load this content into memory. And why doesn't it matter? So there are basically two approaches to allocate pages. Say a user code segment is five pages. There are two options when you want to implement the virtual memory system. Whether you allocate these five pages on-demand, which means that you don't allocate any physical pages at all when users start running. Then whenever you have a page fault, you figure out whether or not it's a valid address, then if it's valid, you allocate pages at this time. That's a one approach which is called on-demand page allocation, right? Or another design option could be you just allocate, pre-allocate all the pages for the user, whether you don't care if the user actually access it or not, right? This is where, so if you decide to implement the second strategy where you want to pre-allocate all the pages for the user without triggering any VM fault later on, you can do it here. So this is where basically the loadf tells you that I'm going to load all the content into memory. I'm going to write all those pages. Whether or not I want to allocate the pages at this point or later on when there is a page fault, it's up to you. But at least the loadf system tells you that he's about to write to their pages. And then we have a lot, we read the content from the disk and load the content to memory, load a segment. And finally when the loadf is done, loading the contents or the segments because it's complete load, right? So this is how the other part of the system in this case loadf, we use the interface of the virtual memory. You order to implement those interfaces correctly, you need to understand how those interfaces are going to be used. This is a great example of how this interface will be used in practice. So we have the user address page related to virtual memory interfaces. Any questions about this part? This is, so the today's content is also the stuff you want to discuss in the design documents, right? So you know what to do, implement the virtual memory system. You know what's required, that's what we're doing. We want to identify what interfaces we want to provide, what the functions you want to implement. Then you need to discuss how do you want to do it, how to design all those functions. And today we'll learn that by seeing how DOM VM does that and how you can improve upon that. This is our way to approach the design documents. First identify what's there to be done and then see what's already there. So what's the DOM VM has already done. Then you can discuss what you can improve upon DOM VM. So this is the second part, which is address space related interfaces. So finally we have one more thing to implement for a simultaneously, which is called S-Break. S-Break is basically the syscall that user can use to request memories. This is related to the heap allocation, which you will do in the second stage. So you can read the menu pages of S-Break and figure out what S-Break is supposed to do and implement that. I assume at this point you are very familiar with all the syscall stuff. So you can play with it. So now we have finished going over all the interfaces or all the functions you are supposed to implement in a summary. Now let's take a look at how DOM VM accomplish these functionalities. DOM VM is also a virtual memory system. You need to do all we have discussed. So let's see how DOM VM does it. So in terms of physical memory allocation, DOM VM implemented these two functions. One is allocator capages and free capages. So the way DOM VM does it is really done in the sense that, suppose this is the whole physical memory. Suppose you have 16 megabytes of memory. This will be physical address zero. This will be physical address 16 megabytes. And when you boot the sys161, it will load the kernel in somewhere in the physical memory. The kernel will occupy some physical memories. Let's suppose this first p address is where, right after the kernel's pages. So this is basically the first available physical addresses. And then we have the last physical address, which is basically the size of the memory. So from the first physical address and to the last physical address is where the available physical pages or physical memories are. This is if you want to manage all the physical pages, this is the pages you want to manage. And how DOM VM manages these physical pages? Any idea? When I call allocator capages, say I want to allocate five pages, what DOM VM does? So what DOM VM do to give me those five pages? Yeah, so basically you have this available number of pages. Whenever I want to allocate five pages, it just forward this first physical address by that many. Say I want to allocate four physical pages, it just forward this to four physical pages and return this chunk to me. Next time, I want to allocate eight physical pages. Take forward this first physical address again here. Keep forwarding. When I call free capages, what does DOM VM do? Nothing. So I keep forwarding this pointer until this first physical address is equals to or larger than last physical address. At that point, DOM VM will just panic. So as you can see that, DOM VM really don't have the capability of managing it in a sense of allocate and reclaim. DOM VM can only allocate it for you. But DOM VM doesn't have a way to reclaim all those physical pages. When you call free physical pages, free capages in the case, DOM VM just does nothing. So this is clearly not desirable. We want to manage all those physical pages instead of just panic when we run out. We should be able to claim them. And actually, you can see that from the code of DOM VM. So take a look at allocate capages. What does it do? Call this magical function called getp pages, which stands for get physical pages. And you jump to getp pages. Well, DOM VM is certainly clever enough to know to use a lock to protect the access to the memory. Because this is one thing you also need to consider when you implement your own virtual memory system. Because this physical memory is system-wide, so you may have multiple threads that are called allocate capages at the same time. You want some synchronization primitives to protect access. In this case, DOM VM decides to use a spin lock. So it first acquires a spin lock. Then it causes function, which is called RAM-steal-memory. It just keeps stealing memory from the RAM system until it runs out. If you go to the RAM-steal-memory, you will see that's exactly what we have described in the slide. So it just forward this first physical address by that number of bytes and keep doing that until it runs out of memory. So if, here, in this case, the first physical address plus the size is larger than last physical address, it just returns 0, which means there is no more memory. In that case, allocate capages will return 0, and it will return 0 all the way up. And eventually, somewhere it will panic. So this is how DOM VM manages the physical memory. Clearly, the limitation is there is no reclaim or recycling happening here. And it just keeps stealing the RAM until it runs out. How do you want to improve this? How would you manage the physical pages? Clearly, you need to maintain some information about the status of each physical page. So you know that when I want to allocate, I just scan my data structure, figure out which pages are available, and return that. When somebody calls free capages, I want to mark those pages as available again. So next time, I can return them to some other program. So there is one data structure called call map, which is very important in physical memory management. Basically, call map is the data structure that has the information of the status of all the physical pages. You can just imagine it as kind of similar to the file tables or process tables. But it's different from that in the sense that the size of call map is fixed. Say you have 200 physical pages in total. How many entries are there in your call map? 200, fixed. There is no allocation and stuff. You just figure out how many physical pages are there and initialize the call map of their size. So you need to think about what information you want to keep for each physical page. For example, most basic information is whether this page is available or not. That's one flag to indicate the availability of the physical page. What about others? What other information you want to keep for each physical page? That we will discuss in detail next time. So by this time, I just want to give you an overview of 7.3 so you know what to write in the design document. That's certainly something you want to discuss in the design document, what the call map looks like. And secondly, how does the MVM handle user address space? So this is what the user address, virtual address space, looks like. We have virtual address 0. We have virtual address 0x and a median, which is the user space top. So inside that, user have a code region, which is a couple of pages, not data region, maybe one or two pages, and also a heap, which is variable and can group downwards all the way up. And at the top of the user address space, we have a stack that starts from the user space top and then group downwards. This is what user address space looks like. And let's take a look at how the MVM handle this stuff. So first of all, you can see what assumptions that MVM makes of the user address space. What is that? You can see it's V-Base 1, P-Base 1, m-pages, which corresponding to the first segment. V-Base 2, P-Base 2, m-pages 2, which corresponding to the second segment. All in two segments. So MVM assume that there will be only two segments in the user address space. If the user address has more than two segments, that's all right, MVM cannot handle it. It will panic or doing some sort of silly, right? So this is one assumption that MVM assumes that user program will always have two, at most two regions. And finally, we have a physical address called AS-Stack-P-Base, which stands for Stack-Physical-Base-Address. What does that mean? So this is a physical address, one value, which point to the physical base of the stack, right? So another assumption that MVM makes is that the size of the stack is fixed. And you will see that in OS16, when MVM I think is 12 or something like that, maybe not 12, but it's a fixed number. So MVM assumes that the user stack is fixed-sized, so that they can use one physical address to store the base of the stack. Say for example, this P-Base is OX at a medium minus four times the page size. In that case, MVM assumes that the user stack is at most four pages. If the user stack has some like infinite recursive cause, like which expanded the stack beyond that four pages, MVM will just panic. Because MVM assumed that the stack size is, the maximum stack size is fixed. And you can also see that it is caused some waste, right? Because you have one fixed value. Some user program is very lightweight. It will only use at most one pages, but you allocate the four pages for it. Some user program may have a heavy recursive kind of cause where it will require a larger stack, but the MVM cannot handle it. So MVM use one physical page size, one stack size for all user programs, right? That's not efficient. That will waste the physical pages. So clearly we want to improve it. So here you can see the limitations of MVM. As we have mentioned, fixed number of regions, too. Oh, another thing I want to mention is, do you see anything related to heap in the structure? No. Because typically a user program have at least a code segment, a data segment. Some user program may have multiple data segments, but typically it has at least one code segment and one data segment. So you can assume that the user program have at least two. And at most how many? We don't know, but at least two. So these two corresponding to the code and data segment. So MVM doesn't provide heap region to the user address space at all. So if some user called malloc, that will fail because there's no heap to allocate memory from, right? So this is something you want to discuss in your design document. How do you support variable number of regions? How do you support the heap? Here you can treat the heap as just another segment where the initial size is zero. You don't have any heap. Whenever you receive a spread call, you just expand the heap. This is another variable size region you want to support. And about the stack. How many, how do you manage the stack? Remember that a stack is also a variable size region inside user address space, right? And you want to improve upon this. So we want to provide variable number of regions. You do not want to put a limit on how many regions that user program have. And you also don't want to use one fixed stack size for all user programs, right? So this is the thing you need to consider when you design the virtual memory system. And finally, let's see how DanVM handles the address translation. And because DanVM handle design address space this way, it's very easy for DanVM to translate the virtual address, right? For each region, you have virtual base. You have physical base. You have the region size, right? For each region, you have three information. The base, virtual base, physical base, and size. So whenever you have a virtual address that you want DanVM to translate, first you find out which region that virtual address is. You can do it by comparing the vbase and impages, right? So first figure out which virtual regions that the virtual address falls into. Then you just figure out what's the offset inside the region. You first minus the virtual address by the base. So you got the offset inside the region. Then you plus that offset by the physical base. Then you get the physical address. So address translation for DanVM is quite simple, right? And this approach only works for DanVM because DanVM use this kind of scheme to manage all those physical and virtual addresses. Another limitation you will notice from here is that say I want to allocate four pages here, four virtual pages here. And conceptually, do I require these four physical pages that correspond to the virtual pages to be continuous? Because I have this virtual to physical translation, one flexibility we have is that for continuous virtual pages, we do not require the physical pages to be continuous. The physical pages can actually be scattered around all the physical memory. We have that translation that gives the user the illusion that these four pages are continuous, whereas in fact they are not. But here for DanVM, you can see that DanVM has to allocate these four pages continuously. Because it only has one kind of base plus size information for this whole region. Instead of each page, right? So when user want have a four page sized co-segment, that four virtual pages has to correspond to four continuous physical pages. That's the limitation you have for DanVM, right? How do you want to improve upon this? Well, do not use such kind of brittle translations. Use a clever way like page table, where for each physical page or for each virtual page, you have a translation, right? So this is where you want to discuss the design of your page table. What kind of page table you want to use? You can review the types of page tables we discussed in lecture, decide what kind of page table you want to use. It can be a one level page table fixed sized, or it can be a two level page table, or you can even use a link list. For each page, you have a list entry. Every time you just query that list. So there are a variety kind of design options you can use in terms of page table. So finally, swiping. Of course, DanVM doesn't suppose swiping at all. When there's no memory, it just panic, right? And you want to add this functionality. For those of you who work alone, I don't know how many of you are there, you don't need to consider swiping. But for others who work in teams, you need to discuss swiping in the design development. So the strategy for Simon 3 of Aurora is that you want to start with the physical page management by first design the com app destructor and implement these two functions, right? Then you move on to the address space design where you design the address space structure and also your page table. And finally, you want to handle the swiping. So there, how many weeks are there for Simon 3? Like six, seven? How many? Yeah, actually Jeff was thinking about bring the deadline of the Simon 3 early. All that. I'm not sure, it's not final yet. I want to bring it before the final, but anyway. So you can assume you have seven weeks, right? So you kind of do some time allocation for the physical pages, I would say at most one week, two weeks tops. Then for the address space, you want to spend another two weeks on it. And finally, you want to do swiping. Swiping will take you roughly three weeks to finish. That's kind of the schedule you want to stick on in the remaining of the semester. And for the design document, as I said, you want to discuss this stuff like the data structure design, what's the comment looks like, what's the address space looks like, and what's the page table looks like, what kind of options to use. And for each of the important functions, how do you want to handle it? For example, allocated key pages, free key pages, right? And that's for the physical page management. And for the user address space, you want to discuss whenever you have a VM file, whether it's a TIP file or a page file, how would you handle it? What's the overall flow of handling such a page file or TIP file, right? I finally want to discuss the AS family functions, function families, like especially AS copy and AS defined region. So those two I think is a little bit harder than others. I mean, AS create, AS destroy is kind of a trivial. AS copy and defined region may be tricky to implement. And finally, you want to discuss, do you need synchronization? If you need synchronization, what kind of primitives do you want to use? Whether or not you want to use a lock to protect some shared resource or you want to use some other kind of synchronization primitives, but you need to consider in the whole process, for example, in the physical page management, clearly you need one synchronization where you want to synchronize the access to the com app, right? That's one example of synchronization you need to consider. And in VM thought, maybe you have other synchronization issues you want to consider. So that's basically what I got from today. Any questions on this? Yeah. What's that? No, I don't think so. The main pages are only for this course. Yeah, so I would suggest you read the code and read the comment in the header files. Basically header files is interface specification of all the functions. I think in the header file for each function they have one or two sentences to describe the functionality of their function. They don't have like what's the error code to return, that's that kind of stuff. They don't have that, but they do have some description. Any other questions? No, no, no, don't do that. So read the code of done VM. That's a good question. Don't do that because last previous year we have students do that and they start a lot in the later stage. At first it may seems easy to do. You have a lot of code to play with. You may want to tempt it to adapt it. Don't do that. So I would suggest that you guys start fresh from the clean slate. You can read the code of done VM and see how it does. As we have reviewed, there's not really much done VM does, right? So I would suggest you design the virtual memory system from scratch. First design a com app and then do allocated capabilities, free capabilities and so on. Don't try to adapt done VM. That will be done. Any other questions? Okay then. So I'll see you next time.