 OK, that was interesting. So don't use HDMI 2 in this room. It just randomly dies. Cool. All right, so we're back. So our SATP pointer, that point us to the highest level page table. In this case, it was where L2 is. And then that's all the information we need to actually translate a virtual address. So we just keep following them, following them, following them until we eventually get to L0. And then that's just exactly too loud. And that's just exactly how we treat a single large page table that has the translation in it. And we just substitute in the physical page number for the virtual page number, keep our offsets the same. And we're all good. So stream down, so hopefully it works. So any questions about what we did yesterday? Yeah, so its main advantage is its size efficiency. So before when we had a single big page table, it was like 1 gigabyte. Here, to translate a single address, we need just only three of these smaller level page tables. So instead of 1 gigabyte, well, each of these fit exactly on a page. So we have three pages, which are 4,096 bytes each. So this would be what? 12 kilobytes to translate as address, as opposed to just 1 gigabyte. Apparently that is not back up. So I'll have to off to upload the lecture later. OK, sorry. All right, room's cursed. Sweet. So last time the internet dropped, I've only ever had problems in this room. So it's just cursed. All right. So quick aside, so who here has heard the term alignment before? Have we heard? So one person, so one person I know that took the advanced version of this course. So just so you know, if they haven't taught you this before, alignment is something we care really, really a lot about, especially when you're implementing hardware and using low level code. So alignment just means that everything of the same size eventually lines up with byte 0. Oh, they're not uploaded? OK, I'll fix the slides after the lecture. I had to upload them like 10,000 times on the train because my internet kept cutting out. So I guess it just decided to do something weird. So I'll fix it after the lecture. So yeah, sorry about that. So today's so far been a disaster, so great. So let's hope it doesn't continue. So in this case, what alignment means is that if I have pages and I say that they are 4,096 byte aligned in memory, it means they always start with all the lower 12 bits being 0. And we like that in computing because we could start the pages wherever we wanted, but it would actually make our lives a lot more complicated. For instance, I could start a page that just address like 7C00. And then if you go to the last byte on the page, which would be 4,095 bytes later, the last byte would be at address 8BFF, which is a bit weird. Because ideally, we would like if we know we're on page 7, it'd just be nicer if it just started with all the 12 bits being 0, so it would start at 7000. And that way it would end at 7FFF, instead of being like a weird thing where it's 7C00 and ends at 8BFF. Just makes our lives a lot easier. You can glance at it. You can know it's on the same page. And that's what we mean by alignment. Everything, like the offset 0 of that page corresponds with all the 12 bits being 0, 0. So the question is that, well, if I wanted to do something like, hey, I want everything to be 8 byte aligned, which for some systems, you actually want your memory to be 8 byte aligned or instructions or whatever. So you should be able to say, hey, is address something like EC 8 byte aligned? Yes or no? So who thinks that is 8 byte aligned? Who thinks it is not? Who thinks what the hell are you talking about? All right, what the hell are you talking about? So 8 byte aligned, you can think of that if you start at index 0, at byte 0, that no matter how far you go up, you are always at boundaries of 8 bytes. So if everything is aligned, it would either start at byte 0, byte 8, 16, 24, 32, so on and so forth. So it basically just means it's divisible. Yep. Yeah, sorry, that should be 7 fall by 3F. So I'll fix that when I upload the slides. Yeah, I got a bit too friendly with my F key. All right, so in that case, if things are 8 byte aligned, well, that means, again, it should start at either 0, 8, 16, so on and so forth. So if this is 8 byte aligned, then it would be nicely divisible by 8. The remainder would be 0. In this case, it's not if you can actually read like hex, or just throw that into a calculator on the internet. So one quick way you can see if it's 8 bit aligned is well. So your 8 byte align is go back, so we'll do that later. So if we're 8 byte aligned, another way of expressing that on a computer, well, just means that all of the bits that all the lower bits we need to represent, this are all 0 all the time. So 8 is to the power of 3. So we would need our 3 lower bits to all be 0. So 0, 0, 0. And if they are all 0, that would be on one of those boundaries. So you can double check this by, you know, it's the same thing as it being exactly divisible by. So in this case, valid addresses would be something like, if we fill up the rest with 0s, well, a valid address that would be 8 by the line would be 0. Next one would just be incrementing anything except the lower 3 bits. So the next one would be something like this, which would be 0, 8, which makes sense. That's 8. And then the next one would be something like this. And that would be address 1, 0, or in plain decimal, that's 16. So those would all be valid addresses that are 8 by a line. So 0, 8, da, da, da, da, da. And if you write this out, you can even notice a pattern here. So either the last hex digit will either be a 0 or an 8 if it is 8 by the line. So does that make sense to everybody? So if that's the case, it doesn't even matter what the rest of them are. So we can look at this and see, if we have the address EC, we can look and immediately know, hey, it's not a 0 or a 8. So it is not 8 by the line. So does that make sense to everyone? So same thing with pages. We want pages to be aligned at that page boundary so we don't have to monkey with anything. Everything is nice aligned at 0. Everything lines up nicely. So with that in mind, let's simulate an MMU and actually create our own page tables and monkey around with it and break it. Everyone likes breaking stuff. So in this case, that whole translation of going through all the page tables isn't really that difficult. All there is is you just have a for loop and you monkey with bits. You don't have to really care about this that much. But basically, all it does is shift the bits around to remember our formatting for the page table entry, like the lower 10 bits were all had to do with the flags. So that's all this is doing in pulling out the physical page number, which is those 44 bits, and then monkeying with it. So we don't have to read that. And the few other functions it does is we can allocate pages and they will be page aligned. So all their lower 12 bits will all be 0. So you can request a page from the kernel and it guarantees that they're aligned. So with that, to set up a page table, all we have to do to go ahead and translate an address is we create a L2 page table. So we're going to assume three levels of page tables. Yep. Does no one see anything I did yesterday? Yeah, so yeah, sorry. So this code is in the lecture 14 one because it was related to yesterday and I ran out of time yesterday. So I was going to do it, but we're doing it now. Yeah, so sorry, this is the lecture 14 directory and this is 15. OK, so to do this, we allocate a page and we're going to use that page we got for an L2 page table, which is our big one. And the only thing our MMU needs to resolve an address is we have to set a root page table. And here, I have a global variable that tells me what page the root page table is on. So that's all that does. So in order to resolve an address, I need at least an L1 page table, an L2, and an L0. So I would allocate a L1 page table, which would return me a new page. And then I use it for whatever I want. In this case, I need to have an entry in my L2 page table and I want to resolve an address. So I'll just put it at index 0 for now because the address I want to translate is ABCEDF. So if this is my address I need to translate, well, we can figure out what indexes this would use. It wouldn't have to translate the last three hex characters because that's the offset onto the page because we're assuming our normal page size of 4,096, so we don't need to touch that. So the upper part of address for the whole virtual page number is ABC. And if we go ahead and let's go ahead and write that out in binary because everyone loves doing that. And if I do that, ABC in binary at the top C is 1, 1, 0, 0. Then B is 1, 0, 1, 1. And A is the one you should know off the top of your head. It's 1, 0, 1, 0. Like 10, 10, lots of fun. So if you do that, you can write out those 12 bits. And then you can start grouping the indices. And they are in groups of 9. So we start at the back. And our first group of 9 is here. That would be what we use for L0 index. And then the next group of 9 would be here. And I just won't write the leading 0s. But if you write something like that, you assume leading 0s for the rest of it. So that's what we would use for L1 index. And then our L2 index would, of course, just be a bunch of 0s. So if we were trying to resolve the address ABC, like as the virtual page number, well, we would first look at index 0 in our L2 page table. Then whatever that entry points us to, we use it as our L1 page table. In the L1 page table, what index would we look at? Or in other words, what is 0, 1, 0 in decimal? You can show me on one hand. I see 1, 5, right? Do we not remember all our binary? So easy way to do it is, remember back in grade 3 when they taught you the tens columns, the hundreds columns, and all that? It's the exact same thing. Yeah, 1, 0 is 1. Whatever I've written up there. Oh, I just said the wrong thing. Yeah, I wrote the right thing. Sorry. OK, I thought we were going back to grade 3. So 1, 0, 1, sorry, is just 5. So our L1 index is 5. All right, let's see. I'll say this correctly. What is 0, 1, 0, 1, 1, 1, 0, 0 in decimal? Who's that fast? 188. Yeah, so a student among you would have realized that I already wrote it out here. So I want this to resolve, so I already wrote it out here. So all that is, hopefully, 188. So first it would look at the L2 page table at index 0, then use that as the L1 page table, look at index 5 there, and then that would find the L0. And then from the L0, it would look at index 188. And then that's where it would pull the physical page number from and just replace it, and then our translation's done. So if we go back to here, at this point right here, we have an L2 page table, but it's just full of 0s, which doesn't help us. That essentially means everything is invalid. So there's no entries in it. And then we create an L1 page table, and we need to create it before we can point to it. So we create an L1 page table, and then we create an entry in our L2 page table at index 0, because when we resolve this address, that's the page table where we look at. And all this PTE from page table does is set the valid bit for the page table entry and take this page table, which would be nicely aligned at the page boundaries and just chop off the offset, because we don't need it, because we know we use that as basically a big array. And it would stuff the physical page number of that page table into there so we can use it. So then we have to go one level deeper. So we create an L0 page table. Again, it would be allocated, be nothing. And then in our L1, we create an entry for index 5, which points to that L0 page table we just made. And then in the L0 page table, we create an entry for 188. And we just get the page table entry from the physical page number, and we want to translate physical page number CAFE. So if we do that, hopefully when we translate this virtual address, A, B, C, D, E, F, what should we get? Yep. What is the actual address? OK, anyone tell me the actual address we would get at the end of all this CAFE? It would start with CAFE, yep. Yeah, CAFE, then DEF, right? So to translate it, we don't have to translate DEF, because that's our part of the offset. So we don't translate that. And as soon as we follow through all the steps, basically we replace A, B, C with whatever physical page number this is. So we should get CAFE and then DEF. So any questions about how I got that? OK, so let's simulate, make sure it works. So I'll also make up another address, which is 1, A, B, C, E, D, E, F. And by default, all the page tables should have are just full of zeros. So they would all have invalid entries. So if I try and resolve this address, it should page fault, which means I can't translate the address because it's not valid. So if I go ahead and do that, build M and U sim. I can see that translated address A, B, C, D, E, F to CAFE, DEF. And then when I translated 1, A, B, C, D, E, F, I got a page fault, which means it didn't translate properly because I didn't have any valid entries. So does that kind of make sense to anyone? Yep. So as part of the page table entry, one of the flag bits is a valid bit. So by default, when you get a page, it's all zeros. So all the entries would appear as invalid. So if I try and resolve it, it would be like, OK, that's not valid, so I wouldn't follow that memory. It doesn't lead me anywhere. Yep. So the question is, why didn't I get a page fault for the first address? And I didn't get a page fault for the first address at A, B, C, D, E, F because when I would translate that, I have to follow it like this, right? Like, given an L2 page table, I look at index 0. I find the L1 from that, then I look at index 5, and then I would look at index 188. And then if that is valid, then I can actually translate the address, right? Oh, OK, yeah. So the question is for this, it looks like I didn't set valid from address, but this PTE from page table, it sets that it basically makes a page table entry that's valid. Yeah, so as part of this, this entry would contain a valid page table entry. And yeah, either you could trust me or we can look at the code. So here, the PTE from ppn, like the little helper function, all it does is takes the physical page number, shifts it over 10 bits, and then sets valid, and PTE valid is just 1. So this sets the lowest bit as 1, which means valid. Yeah, so what happens with, yeah, so let's go over why there's a page fault at 1, A, B, C, D. So can anyone off the top of their head tell me why there would be a page fault there? Like, where did I screw up? Or where does it fail? Yep, yep. So here, let's go over that quick. Oops, hi, doc. So if we change the virtual address to instead be 1, A, B, C, D, E, F, well, the rest of it, thankfully, stays the same. But the lowest bit is now, whoops. But now we have a 1. So 1, and it would go right here. And it still fits within that L1 index. So now we change our L1 index. So yeah, so whoops, I wrote my line bad. There. So if I have a 1 in front of it, well, that just means that 1 is set if I just add it. So now my L1 index is no longer 5. What is it? 13. Everyone got 13? Yeah, so now my L1 index for this address is 13. So if we tried to translate it, well, if we try and translate it now, it's L2 index is still 0. So it would still follow it to this L1 page table. But in the L1 page table, the entry for 13 is just invalid because we never set it to be anything. So it would just stop there. So it's invalid. It would stop, then generate a page fault, which basically just means that, hey, I can't translate this anymore. So yeah. Oh, OK. So yeah, the question is when you shut off your machine, do you have to save everything? So RAM is volatile, so everything gets wiped. So as part of the initialization process, when you boot up your computer, well, the kernel is going to have to set up page tables like this, and it's going to have to zero everything. So it would have to zero it as part of everything being invalid. So if you're going to use a page for a page table, well, by default, you just want a page of all zeros, which is all invalid, and then you can fill it in as you go. So what would I do if I want, let's make one ABCDEF translate to something more fun. So how would I make it translate to, I don't know, let's say like feed DEF. So how would I go about doing that? So if I go back here for this address, one ABCDEF, it's L0 index is actually still the same, right? It's 188. So I must have to do something else. So where is the first index that's different? Yeah, so it's the L1 index is where it changes first, because it would start at L2, translate to L1, and then here in the L1, our index is different, so we're going to have to start by making that valid. Yep, yep. So in this case, we would need to create a new L0 table because we need to point to something. So we would have to go ahead and create a new L0 table. I will be super creative and call it L0 page table 2, because I'm just that creative. And then in here, we essentially would just make entry 13, point to that new page table we just have. So now it points to a different L0 page table. And this is where it would follow if it's translating one ABCDEF. Any questions about that? So now as the final step to the puzzle, all I would have to do is in the new L2 page table, it would be located at the same index. But I just need to change it to whatever I want. What I say I want it to be feed or face. Let's go with face. Face is fun. So if I change it to face, now should I get a valid address out of one ABCDEF? Hopefully. What will it be? Face EDF. Everyone got that? So let's make sure we didn't screw up. So if we do that, then look. Hey, yay. So we translated the address. So we could also do weird things if we really wanted to. So instead of here, they point to different L0 page tables. But there's no reason why I couldn't, if you wanted to, just point them to the same L1 page table. So if I do that, what's going to be the result if I translate both the addresses? Yeah, for both of them, they're going to be CAFE EDF, because they're essentially pointing at the same thing. So then two virtual addresses actually translate to the same physical address, in which case you might have two different variables. And if you change one, it changes the other that you thought was independent. And you could also do really weird things like you could map this differently in different processes. So one process points to the same piece of memory that another process points to. And they both point to the same physical piece of memory from two different virtual addresses. So that's something you could do. Generally, you probably don't want that to happen by default and just point to random memory. But if you want to share memory, well, that's the mechanism that the kernel would do to share memory, is they would just make you point to the same physical page as someone else. So I guess bonus question. So let's change this back. And let's change this address to be, I don't know, let's just double 07. So will this resolve to an address 1abcd007? I see some shaking. So what address would that translate to now? Yeah. Yeah, face 007. So let's verify that that works. So in this case, it would work because it's on the same, like it's the same virtual page. So no matter what you change the offset to, it's still on the same page. So all those addresses are going to be valid. So as soon as you have one entry, it's valid for every address on the page. We're all good. So any questions about that? Hopefully that is clear. Sweet. All right, now to go on to fun stuff. So here's some more questions you could get, like typical questions on exams, like how many page tables do we need? And this is also good for programs like this. So if we want to resolve one address, even if it's on multiple pages, well, we need at least three page tables if we have three levels of page tables. So that's the minimum I need to resolve any single address. Well, you could say that assume our program uses 512 pages, while questions you could ask is what's the minimum number of page tables we need and what's the maximum number of page tables we need. Any guesses? Yep. So like just one L0, right? So for one address, we need at least three. So I guess minimum for 512 entries would be three then, right? OK, what about maximum? OK, I see a confused phase. So let's go over why you would at minimum need three page tables to translate 512 pages. So let's go through here. So one way we could have it is, well, to translate, let's start with one address. So to translate one address or one page, we would have our L2 page table. It would have exactly one entry that points to another. Then inside here, it would have exactly one entry that points to another. And then that would actually have the entry that we use, right? So this is what we had, and we could translate everything on that page. So if we got really lucky, how many entries can this, I'll label them. How many entries does this L0 page table have? Yeah. Yeah, so this has 500, whoops, that is not 512. So this is 512 entries. So what we could do with only three page tables is we could just fill this bad boy up with 512 entries. Yep. So you have to start at an L2, right? If you have three levels of page tables, you have to start at an L2. You have to start somewhere. No, because the hardware, if you're using three page tables, like three levels of page tables, it needs to step through three levels. So what you could do, which would be super screwed up, is, well, if you have a single L2 page table, I guess you could point to yourself. But you probably don't want to do that. And if you point to yourself, you're using an entry for yourself, and really bad things can happen. Yeah. So if we need, so it's just a system design question, so how many levels we need? Just depends on how big of a virtual address we want to support. And that's it. So in this case, it's like 39-bit virtual addresses, so it has to be three levels of page tables. And that's it. So yeah, yeah. So we're stuck with three levels. We can't get rid of three levels. We, at a minimum, have to have three levels. So if we get really lucky, well, in our L2 page table, it could point to a single L1, and then that L1 points to an L0. And that L0 is just full of entries. It has 512 entries, so it can actually map 512 different pages. So what would happen if I can't fill up that table? So if my best case is I can fill up my L1 table with 512 entries, what's my worst case? Yeah, my, yep, 3 times 512. Everyone agree with 3 times 512? Yep. Not counting empty ones. Yeah, so our worst case would probably be each L0 page table has exactly one entry. That would probably be the most wasteful thing we could do aside from just allocating empty ones and just being like, yeah, screw you guys. So if our worst case is we have an L0 with exactly one entry on it, whoops, that's not how you spell that. Well, how many of those would I actually need to support 512 different mappings? So yeah. Yeah, so I would need 512 L1 page tables. Each one has exactly one entry. So if I wanted to make this bad, well, in my L0 table, I could, because there's 512, if I was really good or really lucky, I could fill this up with 512 entries and support pointing to all those L0 page tables. Or since we're talking about the worst case, well, this could also only have one entry per. So if I go back and I just do one entry per in my L1, well, I have to point to at least 512 L0 page tables. So I also need 512 of these. I need 512 L1 page tables in the worst case that all have one entry each. So everyone, you have to make this joke. Everyone on the same page with that one? OK, terrible. How many L2 page tables do I need? 512, I see a one. All right, one. One's in the chat. Two people? All right, 512. Like three people. Great. All right, so for the L2 page table, remember that's our starting point. So we have to be given an L2 page table. And since it's your starting point, you only have one of them. So it needs to start somewhere. So our L2 page table would just start off just somewhere. And because it has to point to 512 entries, well, it would just have to be full in this case. In this case, this one would have 512 entries in it because we've spread them out as much as we can. So any questions about that? Yeah, so the question is, could I design the first case, or is it just a matter of luck? So if you just boot up your computer and you can do whatever you want with memory, you could easily do that. You could make sure that your program just uses sequential addresses because sequential addresses will be just one index off. So typically, you'll do that. Everyone's heap is like that. Everything looks sequential. So that's the reason why you want contiguous addresses. So your page tables actually don't get really messed up. So you would do that. But the page tables might map to physical addresses that aren't contiguous. Ideally, you would like that to be contiguous as well. But you typically just try to make it as best you can. And that's your job as a kernel. But as things run and things allocate, deallocate, you have new processes, things typically get messy. So it's a best case basis. OK, any other questions? So our best case here was three page tables. And our worst case is 512 plus 512 plus 1. So 1,025 page tables is the worst if we have to map 512 individual pages. So best case is we fit them all on a single L0. Worst case is we have one entry per. In L0, one entry per in L1. OK, so yeah. So the question is, well, if I only have one L2 page table, what about if I have more than 512 entries? Well, you can't, right? Because our L2 indexes here are nine bits. So we can't use more, so we only have one. So if you want to support more virtual addresses, you just have to add another level. So I could add a fourth level that has 512 entries. And then I have to do this with another step. OK, any questions? Any more questions about that? So sweet. So here's another type of question. How many levels do you need, which we kind of did quickly yesterday? So all these questions will say what you'll get all the information, except for one piece of information that you will have to figure out. So in this case, we have a 32-bit virtual address, and it tells you your page size. And it's the one we all know and love. And we have a bit different. We have a page table entry size of four bytes. So if we do our design insight of having each, dividing it into smaller page tables and having and fitting each one exactly on a page, well, if we want to find the number of page table entries we could have in a page. That's 2 to the 12 divided by 2 to the 2, which is that four bytes. So unlike before, we can have another factor of two more entries. So we can have 2 to the 10 entries in these page tables. Now, if our page table entry size gets halved, we can fit double the number of entries. Makes sense. So it's always log 2 number of page table entries for page is the number of bits you need to index. So in this case, what's log 2 of 2 to the power of 10? 10. They cancel each other out exponent math. So for each level, we have 10 index bits. So you can figure out the number of levels you need. It's just a simple all that is as a floor. So it just means we round up because it's fine if we can't fill one level, but we have to have as many levels as we need. We can't just end early. So in this question, our number of virtual bits is 32. And our offset bits, which you derive from the page table size. So remember, everything is nicely page aligned. So you never have to monkey with however many bits represent that page. So in this case, the offset bits are 12 because 496 is 2 to the 12. And you'll see this number over and over and over again. So virtual bits minus offset bits, that's 32 minus 12. And our index bits, well, we have 10. So 32 minus 12 is 20 divided by 10. We don't need to do any rounding or anything like that. The number of levels we get is just 2. What would happen if I say we have a 33-bit virtual address now? I just need a little bit more memory. So how many levels would I need in that case? Three, right? So I don't fit exactly within two. So it would be like 33 minus 12, so 21 divided by 10. So I would need one additional level. And in that level, it would only have two entries that I use. So then all my L2 page table, I'd only have two entries I actually use to tell me which L1 or L1 page table I need to point at. OK, so this is actually really, really, really, really slow. So for that, like that MMU simulator, that's what your hardware would actually have to do. So to decode a single address, well, it needs to do a lot more than that original memory access. So instead of what everyone thought before, where, hey, I'm accessing memory, so it's just have a lot it takes to access memory, well, now with page tables, we have to access memory multiple times. So each time, we're going to have to access the L2, which would be a memory read, then L1, which is a memory read, then L2, which is a memory read, and then our original memory read. So instead of just one memory access, we're turning that into four, which intuitively seems to make you think that your machine would be four times slower, but we have some tricks we can do. So likely, we're going to access the same page multiple times. So remember, we even use the same translation. So when I change the offset to just be within the same virtual page, my translation didn't really change. It would have followed the same steps, and all the addresses are valid. And whenever you use your process, while at any given time, you might only need a few mappings available at a time. Like you only use a few pages in your program at any given time. So our solution to that would be just the computer science classic. We just implement a cache. Like basically, that translation at the end of the day was virtual page number to physical page number. So we had to go through multiple levels to decode it, but after we decode it, well, we could cache that result. All at the end of the day, what we care about is, for this virtual page number, what's the physical page number? So that's exactly what something called a TLB is, or a translation look-aside buffer. Why the name? They need to be fancy, but all it is is a cache for virtual page numbers to physical page numbers. So how it works is, well, in this case, you'll see the virtual address and logical address. They mean the same thing. So on your CPU, it would use the virtual address, and then it would take the virtual page number, look it up in a lookup table, basically, and check if it's in that table. If it's in the table, it would be something called a TLB hit. So it's in the cache. So I can just use that for the translation directly, and I don't have to go through all those three levels again and again and again. And then if there is a miss, well, then I have to go through those three layers of steps, and sorry, this image kind of sucks. But does that kind of make sense to everyone? Basically, just a cache, so I don't have to go translate it using those three levels again and again. So this cache would be a limited size. But each entry in the cache essentially covers a whole page. So if this had even like 128 entries, well, each page is 4 kilobytes, so that actually covers a bunch of translations at a given time. So it actually covers a lot of space. Well, it covers a decent amount of space now. Maybe a lot of space if you had 4 kilobytes of RAM, but it's like a decent amount now. Yep. Yeah, so the question is, why don't I just go back to my one big page table, because that would just be faster? So if I went back to my one big page table, well, I still have to translate. I still need two memory accesses, right? I need to find where it is in that page table. And then from that, I have to then get the original address it was referring to. So it's still two memory accesses for that big table. And also that table, we threw out the window before, because it was just way too big. But even if you had a single big page table, this would still be helpful, because it would just cache that translation in the big page table, and you don't have to access it. But the main reason we don't use the single page table is just because it would waste way too much space. OK, so that's four. And I guess we'll finish up this lecture next week. And yeah, after this, you should start doing Lab 2, because if you haven't started it yet, you are screwed. So just remember, pulling for you, we're all in this together.