Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Yes, that's segmentation. And I was just wondering if you could call that an MMU. It's not the same result as an MMU across the board, since you have no paging: You cannot freely rearrange pages in virtual memory with their frames in physical memory, you can only "segment" linear portions of physical memory. You also cannot fault in pages on-demand at their time of being accessed, only when the whole segment is loaded (i.e. the selector written into the segment register).

If an MMU is something that sits between memory and the rest of the CPU, and abstracts physical addressing away, then I argue the 286 did not have an MMU. Segmented access is more of a mandatory addressing mode, where you always access relatively to a segment's base, and have to keep within the limit.

OS/2 is another OS that made use of pre-paging 286 protected mode.



Wikipedia refers to it as having on-chip MMU "capabilities", albeit only supporting segmentation and not paging:

"The 286 was the first of the x86 CPU family to support protected virtual-address mode, commonly called "protected mode". In addition, it was the first commercially available microprocessor with on-chip MMU capabilities (systems using the contemporaneous Motorola 68010 and NS320xx could be equipped with an optional MMU controller). This would allow IBM compatibles to have advanced multitasking OSes for the first time and compete in the Unix-dominated[citation needed] server/workstation market."


Thanks, but I don't think I would call what the 286 had "virtual-address mode". Intel themselves referred to "virtual addresses" only in the context of paging in their 386 manual. Addresses relative to segmentation were called "logical addresses", and they translate to "linear addresses".

That Wikipedia passage seems to be inaccurate, or a least not use the common terminology.


Intel did you use that term to describe segmented addressing, at least in the 286 manual [1]:

""In Protected Virtual Address Mode, the 80286 provides an advanced architecture that retains substantial compatibility with the 8086 and other processors in the 8086 family.""

""In Protected Mode, application programs deal exclusively with virtual addresses; programs have no access whatsoever to the actual physical addresses generated by the processor. As discussed in Chapter 2, an address is specified by a program in terms of two components: (1) a 16-bit effective address offset that determines the displacement, in bytes, of a location within a segment; and (2) a 16-bit segment selector that uniquely references a particular segment.""

[1] https://ragestorm.net/downloads/286intel.txt


You're right, I searched wrong in my 386 manual. So apparently they did call it virtual addressing.

I'm still not sure if I like calling that an "MMU". To me, an MMU acts on every memory access. But at the end of the day, who cares what I think what words mean. :)

The critical distinction to me is that segmentation is not much more than, effectively, an indexed addressing mode, where the base is the segment base that has been loaded into the descriptor cache when the segment register was loaded. Nothing happens at the time of actually accessing memory, except for the limit check.


> Intel themselves referred to "virtual addresses" only in the context of paging in their 386 manual.

This is not correct.

Intel referred to the 286's memory management as virtual memory:

Quote from the 286's manual - 4th paragraph of section 1.1

> Segmentation also lends itself to efficient implementation of sophisticated memory management, virtual memory, and memory protection.

The 286 protected-mode segmentation is dramatically different from the segmentation on the 8086.

Virtual memory is merely the ability to use logical addresses that don't necessarily have a direct relationship with a physical address. Your OS can relocate data in physical memory without an application having to know.

On the 286, this is facilitated via the Global and Local Descriptor tables. When you loaded a segment register, it would first check whether your current permission level could access that segment. Exception if you couldn't. Then it would check if the segment was marked as present. Exception if it wasn't present. Then it would load the base address (a full 24 bit physical address) and the size into internal, hidden registers. The hardware would bounds check accesses to segments.

The GDT contained segments that were common to the whole OS, and the LDT contained "process" specific segments.

Intel advertised the 286 as having 1 GiB of virtual address space per process - up to 8K entries in GDT, up to 8K entries in LDT, up to a 64K segment size 16K * 64K = 1G.

Some examples of virtual memory behaviors:

A process tries to allocate more memory but the system is out of physical memory. The system decides to swap another process's memory to disk to make room. The data from the other process is written to disk, the descriptor table entry is marked as not present, and the physical memory is free'd back to the OS. The new process's allocation is placed there. When the old process switches back in, accesses throw a segment-not-present exception and the OS must retrieve the segment from disk. It finds physical memory to back the segment, copies the data from disk, updates the base address in the descriptor table, and reloads the segment register. As far as the process is concerned, nothing has changed. The pointer values are still all the same yet the physical address is different.

On a modern processor, this would be done on a page by page basis, on the 286, you had to do it a whole segment at a time. If your segment's size is 256 bytes, awesome, if it was 64 KiB, that sucks. You also can't punch holes in the middle of segments.

Another example:

Your process has a heap from which it performs dynamic allocations. This heap is pointed to by a descriptor in your LDT. You want to make another allocation but you've exhausted your heap. You call into the OS to grow the size of the heap. Unfortunately, a different object was placed in physical memory right after your heap, so, you find a new spot in memory large enough for the new heap. You copy the contents of the heap to the new address and update the descriptor. Again, the program is none the wiser.

This is a major weak point for the 286. On a modern processor, you'd just map more pages in because with a paged MMU, contiguous virtual addresses don't have to be physically contiguous.

Obviously, this is very different to today's demand paged MMUs (well, the last 37 years anyhow) and it's not nearly as flexible, but it's still virtual memory.

That's not even getting into the neat hardware multitasking thing that automated switching all these registers around (which existed on the 386 but was never used because it assumed you'd still want to use segmentation and spent time and memory backing up registers and doing permission checks most software didn't want).

IMHO - the only reason the 286 is merely a footnote in history is because Intel didn't provide a way to cleanly switch from protected mode back into real mode. Intel erroneously assumed applications and operating systems would just be rewritten for the 286's native mode. It was a huge step forward from the 8086 but the largest userbase of Intel CPUs (DOS users) never saw the improvements beyond a simple speed boost.


>hardware multitasking

The main reason this wasn't used more is that is isn't a good fit for what an OS needs to do.

Jumping to a TSS or task gate saves the current context and restores that for the new task, in one atomic operation. But most kernels save the registers on syscall/interrupt entry (while remaining in the context of the current task), and restore a possibly different context on exit when the current one gets suspended.

There is no way to do "half of a task switch" with the TSS mechanism, so it would always do useless work:

    kernel code does:
        push user registers
        handle syscall / interrupt
        select new task and jump to it
    CPU does:
        save context A (mostly garbage values at this point)
        restore context B (containing previously saved garbage)
    kernel code does:
        pop user registers
        return to ring 3


Ah, that's a good point.


I am very well aware of how segmentation works on the 286 and up. The critical difference I see to a paged MMU is that everything is determined "upfront": When you load the segment register with a selector, that causes the CPU to fetch the descriptor, and to load base and limit in the descriptor cache (the hidden portion of the segment registers, can be accessed with LOADALL). Every subsequent memory access to that segment is basically just an indexed addressing mode with that base as the index register.

In a paged MMU, it's more apt to say that it sits "between" CPU and memory. The frame in physical memory to be accessed is different for every page, and faults can happen while resolving that page. It happens during access, not upfront.

I concede that Intel already called this "virtual memory", I searched in my 386 manual wrong.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: