feature article
Subscribe Now

How To Implement Virtual Memory, Part 4

Cut and Paste Code to Set Up Your x86 MMU

Fiddly little bit-twiddling. Nobody likes it, but if you’re writing boot ROMs, device drivers, or low-level system functions, you’ve got to roll your sleeves up and do it. That’s especially true when you’re tweaking your processor’s MMU to create and enable virtual memory, paging, or protections. To make life easier, we herewith present Little Chunks o’ Code to help with that. 

Most of what we’ve discussed so far applies to most any processor, but today’s code snippets are obviously x86-specific. The same virtual memory concepts may apply to ARM, MIPS, RISC-V, and other CPU families, but the code presented here sure won’t. 

The first bit of code creates your page directory and two page tables. Both tables are minimal, with just a few entries apiece. It’s written as a function or subroutine that you can call from somewhere else, although you don’t have to. It also makes some assumptions about where your RAM is located, but that’s a hard-coded number that’s easy to tweak.

The first program relies on the second program, which is a little function to create new segment descriptors in the GDT. And it, in turn, relies on the third program to find an unused GDT entry it can use. All three pieces can be used standalone, or together. 

; This program creates page directory and page tables and enables paging.

; No address translation is performed (i.e., identity-mapped).  

; Assumptions:

; 1) Physical memory from 0x00001000 through 0x00003FFF is not already in use.




; Define some useful descriptor type values… 

DRO EQU 0 ;data, read-only

DRW EQU 1 ;data, read/write

SRO EQU 2 ;stack, read-only

SRW EQU 3 ;stack, read/write

CEO EQU 4 ;code, execute-only

CER EQU 5 ;code, execute/read

CEOC EQU 6 ;code, conforming, execute-only

CERC EQU 7 ;code, conforming, execute/read




page_enable PROC FAR




; Create a page directory

MOV EAX, 1000h ;base address = 0x00001000

MOV EBX, 1000h ;size = 4096 bytes

NOV CL, DRW ;type = read/write data segment

MOV CH, 0 ;privilege = level 0

CALL make_descriptor

OR EAX, EAX

JL error ;failed, couldn't make descriptor




MOV ES, AX ;ES gets new segment selector

MOV EDI, 0

MOV EAX, 0

MOV ECX, 1024

REP STOSD ;start by zeroing out the table




MOV DWORD PTR ES:[0], 00002003h ;1st PDE -> 1st page table

MOV DWORD PTR ES:[0FFCh], 00003003h  ;1024th PDE -> 2nd page table





; Create first page table...

MOV EAX, 2000h ;base address = 0x00002000 

MOV EBX, 1000h ;size = 4096 bytes

MOV CL, DRW ;type = read/write data segment

MOV CH, 0 ;privilege = level 0

CALL make_descriptor

OR EAX, EAX

JL error ;couldn’t make descriptor




MOV ES, AX ;ES gets new segment selector

MOV EDI, 0

MOV EAX, 00000003h ;first page frame address = 0

MOV ECX, 1024

table1:

STOSD ;write one PTE

ADD EAX, 1000h ;increment page frame address

LOOP table1 ;repeat 1024 times




; Create second page table

MOV EAX, 3000h ;base address = 0x00003000

MOV EBX, 1000h ;size = 1024 bytes

MOV CL, DRW ;type = read/write data segment

MOV CH, 0 ;privilege = level 0

CALL make_descriptor

OR EAX, EAX

JL error ;couldn’t make descriptor




MOV ES,AX ;ES gets segment selector

MOV EDI, 0

MOV  EAX, 0

MOV ECX, 1008

REP STOSD ;zero first 1008 PTEs

MOV EAX, 0FFFF0003h ;1009th page frame address = 0xFFFF0000

MOV ECX, 16

table2:

STOSD ;write one PTE

ADD EAX, 1000h ;increment page frame address

LOOP table2 ;repeat 16 times




;Enable paging

MOV EAX, 1000h ;physical address of page directory = 0x1000

MOV CR3, EAX ;load into Control Register 3




PUSHFD ;save EFLAGS register

CLI ;disable interrupts temporarily




MOV EAX, CR0 ;copy CR0 into EAX

BTS EAX, 31 ;set bit 31

MOV CR0, EAX ** enable paging **

JMP flush ;flush prefetch queue

flush:

NOP

POPFD ;restore EFLAGS

RET

Listing 1: Sample program to create an x86 page directory and two page tables.

This second program creates a new segment descriptor based on the parameters you pass to it. It moves the input data around to conform to the somewhat convoluted format that x86 MMUs require. It also does one small security check, ensuring that the privilege level of the new segment it creates is not any more privileged than the program that’s calling it.

; Create a segment descriptor in the GDT. 

; All information is passed in registers. 

; Returns 16-bit selector in AX, or -1 if error. 

; Assumptions: 

; 1) Segment register DS holds selector to GDT alias as writable data segment

; 2) Base address is passed in EAX 932 bits)

; 3) Segment length (not limit) is passed in EBX (32 bits)

; 4) Segment type is passed in CL (3 bits)

; 5) Segment privilege level is passed in CH (2 bits)




make_descriptor PROC FAR

PUSH EDX ;save work registers

PUSH EAX

CALL find_first_GDT ;find the index of the first available slot

MOV EDX, EAX ;EDX gets free slot, or -1 if none

POP EAX

OR EDX, EDX

JL error ;sorry, no free GDT slots available




base_address:

MOV DWORD PTR DS:[EDX*8]+2, EAX ;write base address bits 23-00

ROL EAX, 8

MOV BYTE PTR DS”[EDX*8]+4, AL ;write base address bits 31-24

ROR EAX, 8

DEC EBX ;segment length becomes segment limit

CMP EBX, 0FFFFFh ;is limit under 1 MB? 

JA page_granular ;no, go to page-granular setup

byte_granular:

MOV WORD PTR DS:[EDX*8]+0, BX ;write limits bits 15-00

ROL EBX, 16

MOV BYTE PTR DS:[EDX*8]+6, BL ;write limit bits 19-16

ROR EBX, 16

AND BYTE PTR DS:[EDX*8]+6, 0Fh ;drop G, D, X, U bits

OR BYTE PTR DS:[EDX*8]+6, 40h ;set D bit

JMP segment_type

page_granular:

SHR EBX, 12 ;shift limit right 12 bits

MOV DWORD PTR DS:[EDX*8] +0, BX ;write limit bits 15-00

ROL EBX, 16

MOV BYTE PTR DS:[EDX*8]+6, BL ;write limit bits 19-16

ROR EBX, 16

AND BYTE PTR DS:[EDX*8]+6, 0Fh ;drop G, D, X, U bits

OR BYTE PTR DS:[EDX*8]+6, 0CDh ;set G and D bits

segment_type:

MOV BYTE PTR DS:[EDX*8]+5, 0 ;clear Access Rights byte

CMP AL, CERC

JA error ; sorry, invalid segment type

SHL CL, 1 ;shift ot make room for A bit

OR BYTE PTR DS:[EDX*8]+5, CL ;write Type bits and A bit

priv_level:

MOVZX AX, CH ;AX gets requested privilege level

MOV BX, WORD PTR SS:[ESP]+4 ;BX gets caller’s CPL

ARPL AX, BX ;adjust AX if necessary

SHL AL, 5 ;move DPL field into position

OR BYTE PTR DS:[EDX*8]+5, AL ;set DPL bits

OR BYTE PTR DS:[EDX*8]+5, 90h ;set Present and System bits

MOV AX, DX ;AX gets GDT index

SHL AX, 3 ;index * 8 = selector

ARPL AX, BX ;adjust RPL field if necessary

POP EDX

RET

error:

POP EDX

MOV EAX, 0

DEC EAX

RET ;return -1

Listing 2: Sample program to create an x86 segment descriptor in the GDT based in input parameters.  

This little guy simply searches through the Global Descriptor Table (GDT) looking for an unused slot it can use. If there are none, it returns –1, otherwise you get the index of the free slot in EAX. It needs no input parameters. As above, this assumes that you can both read and write the GDT via data segment DS. 

; Return in to the first free GDT descriptor in EAX, or -1 if none.

; Assumptions: 

; 1) Segment selector in DS points to an alias of the GDT as writable data. 




find_free_GDT: 

PUSH EBX ;save work registers

PUSH ECX

MOV BX, DS ;BX gets alias selector

MOVZX EBX, BX ;extend BX to all 32 bits

LSL ECX, EBX ;ECX gets alias segment limit

SHR ECX, 3 ;limit / 8 = number of slots

JECXZ not_found ;null length GDT; exit

MOV EAX, 0 ;start at GDT(1)

next:

INC EAX

CMP BYTE PTR DS:[EAX*8]+5, 0 ;Access Rights byte = 0? 

LOOPNZ next ;repeat as necessary

JNZ not_found

found:

POP ECX

POP EBX

RET ;return value in EAX

not_found:

POP ECX

POP EBX

MOV EAX, 0

DEC EAX

RET ;return -1 in EAX

Listing 3: Sample program to locate the first unused descriptor slot in the GDT.

Leave a Reply

featured blogs
May 6, 2021
For the third time*, I find myself writing the obligatory, "Hello, World!" first blog post. So "Hello" to all you readers of Cadence's Computational Fluid Dynamics blog. (Or... [[ Click on the title to access the full blog on the Cadence Community si...
May 6, 2021
Learn how correct-by-construction coding enables a more productive chip design process, as new code review tools address bugs early in the design process. The post Find Bugs Earlier Via On-the-Fly Code Checking for Productive Chip Design and Verification appeared first on Fr...
May 6, 2021
In April, we continued improvements to our new similar parts functionality as well as our search upgrades, and released several new content product pages on the website. We’ll also piggyback some of the cart upgrades we’ve rolled out in the last few months onto th...
May 4, 2021
What a difference a year can make! Oh, we're not referring to that virus that… The post Realize Live + U2U: Side by Side appeared first on Design with Calibre....

featured video

The Verification World We Know is About to be Revolutionized

Sponsored by Cadence Design Systems

Designs and software are growing in complexity. With verification, you need the right tool at the right time. Cadence® Palladium® Z2 emulation and Protium™ X2 prototyping dynamic duo address challenges of advanced applications from mobile to consumer and hyperscale computing. With a seamlessly integrated flow, unified debug, common interfaces, and testbench content across the systems, the dynamic duo offers rapid design migration and testing from emulation to prototyping. See them in action.

Click here for more information

featured paper

Optimizing an OpenCL AI Kernel for the data center using Silexica’s SLX FPGA

Sponsored by Silexica

AI applications are increasingly contributing to FPGAs being used as co-processors in data centers. Silexica's newest application note shows how SLX FPGA accelerates an AI-related face detection design example, leveraging the bottom-up flow of Xilinx’s Vitis 2020.2 and Alveo U280 accelerator card.

Click to read

featured chalk talk

Benefits and Applications of Immersion Cooling

Sponsored by Samtec

For truly high-performance systems, liquid immersion cooling is often the best solution. But, jumping into immersion cooling requires careful consideration of elements such as connectors. In this episode of Chalk Talk, Amelia Dalton chats with Brian Niehoff of Samtec about connector solutions for immersion-cooled applications.

Click here for more information about Samtec immersion cooling solutions