http://www.embedded.com/design/opensource/208700267?pgno=2

Getting down to basics: Running Linux on a 32-/64-bit RISC architecture: Part 3
What Happens on a System Call



Embedded.com

Useful things to remember:
1) Where kernel is built to run: The MIPS Linux kernel's code and data are built to run in kseg0; virtual addresses from 0x8000.0000 upward. Addresses in this region are just a window onto the low 512 Mbytes of physical memory, requiring no TLB management.

2) Exception entry points: In most MIPS CPUs to date, these are hard-wired near the bottom of kseg0. The latest CPUs may provide the EBase register to allow them to be relocated, mainly so that multiple memory-sharing CPUs can use different exception handlers without the trouble of special-casing memory decoding.

In the Linux kernel, even when there are multiple CPUs, they should all run the same exception-handling code, so this is unlikely to be used for Linux.

3) Where user programs are built to run: MIPS Linux applications (which are run in low-privilege "usermode") have virtual addresses from 0 through 07FFF.FFFF. Addresses in this region are accessible in user mode and translated through the TLB.

The main program of an application is built to run starting somewhere near zero. Not quite zero—one or more pages from virtual address zero are never mapped, so that an attempt to use a null pointer will be caught as a memory-management error.

The library components of an application, though, are loaded incrementally into user space at load time or even later. That's possible because a library component is built to be position-independent and is adjusted to fit the place that the loader finds for it.

4) User stack and heap: An application's stack is initially set to the top of user-accessible memory (about 2GB up in virtual space) and grows down. TheOS detects accesses to not-yet-mapped memory near the lowest stack entries it has allocated and automatically maps more pages to let the stack grow.

Meanwhile, new shared libraries or explicit user data allocated with malloc() and its descendants are growing up from the bottom of the user space. So long as the sum of all these remains less than 2 GB, all is well: This restriction is rarely onerous in any but the biggest servers.

5) Physical memory up to 512 MB: Can be accessed cached through kseg0 and uncached through kseg1. Historically, the Linux kernel assumed it had direct access to all the physical memory of the machine.

For smaller MIPS systems that use 512 MB or less of physical memory range, that's true; in this case, all memory is directly accessible (cached) in kseg0 and (uncached) in kseg1.

6) Physical memory over 512 MB is "high memory": Now, 512 MB is no longer enough, even for embedded systems. Linux has an architecture independent concept of high memory—physical memory that requires special, architecture-dependent handling, and for a 32-bit Linux/MIPS system physical memory above 512 MB is high memory. When we need to access it, we'll create appropriate translation entries and have them copied into the TLB on demand.

Early MIPS CPUs sought applications in UNIX workstations and servers, so the MIPS memory-management hardware was conceived as the minimum hardware that could hope to providememory management for BSD UNIX. The BSD UNIX system was the first UNIX OS to provide real paged virtual memory on the DEC VAX minicomputer.

The VAX was in many ways the model for the 32-bit paged-translation virtual-memory architectures that have dominated computing ever since; perhaps it's not surprising that there are some echoes of the VAX memory-management organization in MIPS.

But this is a RISC, and the MIPS hardware does much less. In particular, many problems that the VAX (or an x86) solves with microcode are left to software by the MIPS system.

In this series, we'll start close to where MIPS started, with the requirements of a basic UNIX-like OS and its virtual memory system; but this time the OS is Linux. We'll show how the essence of the MIPS hardware is a reasonable response to that requirement.

What's Memory Translation For?
Memory translation hardware (while we're being general we'll call it MMU for memory management unit) serves several distinct purposes, listed below (Given just how much the MMU contributes, it's remarkable that a fairly workable minimal Linux system such as ucLinux can be built without it—but that's another story):

1) Hiding and protection: User-privilege programs can only access data whose program address is in the kuseg memory region (lower program addresses). Such a program can only get at the memory regions that the OS allows.

Moreover, each page can be individually specified as writable or write protected; the OS can even stop a program from accidentally overwriting its code.

2) Allocating contiguous memory to programs: With the MMU, the OS can build contiguous program space out of physically scattered pages of memory, allowing us to allocate memory froma simple pool of fixed-size pages.

3) Extending the address range: Some CPUs can't directly access their full potential physical memory range. MIPS32 CPUs, despite their genuine 32-bit architecture, arrange their address map so that the unmapped address space windows kseg0 and kseg1 (which don't depend on the MMUtables to translate addresses) are windows onto the first 512MBof physical memory. If you need a memory map that extends further, you must go through the MMU.

4) Making the memory map suit your program: With the MMU, your program can use the addresses that suit it. In a big OS, there may be many copies of the same program running, and it's much easier for them all to be using the same program addresses.

5) Demand paging: Programs can run as if all the memory resources they neededwere already allocated, but the OS can actually give them out only as needed. A program accessing an unallocated memory region will get an exception that the OS can process; the OS then loads appropriate data into memory and lets the program continue.

In theory (and sometimes in computer science textbooks), it says that demand paging is useful because it allows you to run a larger program than will fit into physical memory.

This is true, if you've got a lot of time to spare, but in reality if a program really needs to use more memory than is available it will keep displacing bits of itself from memory and will run very, very slowly.

But demand paging is still very useful, because large programs are filled with vast hinterlands of code thatwon't get run, at least on this execution.

Perhaps your program has built-in support for a data format you're not using today; perhaps it has extensive error handling, but those errors are rare. In a demand-paged system, the chunks of the program that aren't used need never be read in. And it will start up fast, which will please an impatient user.

6) Relocation: The addresses of program entry points and predeclared data are fixed at program compile/build time—though this is much less true for position-independent code, used for all Linux's shared libraries and most applications. The MMU allows the program to be run anywhere in physical memory.

The essence of the Linux memory manager's job is to provide each program with its own memory space. Well, really Linux has separate concepts: Threads are what is scheduled, and address spaces (memory maps) are the protection units. Many threads in a thread group can share one address space.


Reply via email to