The Mkos Memory Map

One of my first tasks in starting Mkos was to define some kind of memory map. I couldn’t get far without deciding where kernel and user programs would be loaded in memory.

I didn’t have much guidance in planning the memory map, but some areas of PC memory are reserved for the system and off limits for software use:

Physical address

Segment:offset

Description

Size (KiB)

0x00000–~0x003ff

0000:0

BIOS

~1

0x07c00–0x07dff

7c00:0

Boot loader

0.5

0xa0000–0xfffff

a000:0

System hardware

384

With that in mind, any memory from addresses 0x07e00–0x9ffff should be safe to use. At first, I just made four arbitrary reservations for kernel code/data, kernel stack, user code/data, and user stack, and started working on the boot loader and kernel.

The reservations worked until I started adding C language support to the project. OpenWatcom’s wcc compiler defines several different 16-bit memory models. The memory model of choice depends on the size of code and data in the program being compiled. Since Mkos kernel and user programs are currently limited to 64 KiB in size, we use the “small” memory model. Under the small memory model, wcc requires that the program data and stack are located in the same physical segment, and the bottom of the stack is at the top of that segment. That is, the stack grows downward from the top of the stack/data segment. wcc emits machine code that relies on this requirement. Since I’d reserved separate stack segments, the C code wouldn’t work correctly with my memory map. I reworked the memory map with wcc’s requirement in mind.

Physical add.

Segment:offset

Description

Size (KiB)

0x00000–0x07bff

0000:0

Reserved

31

0x07c00–0x07dff

7c00:0

Boot loader

0.5

0x07e00–0x07fff

7e00:0

Reserved

0.5

0x08000–0x17fff

0800:0

Kernel data/code/stack

64

0x18000–0x27fff

1800:0

User data/code/stack

64

This map simply reserves all memory through the end of the boot loader. I aligned the start of the kernel segment to 4 KiB, basically just to make the number look pretty, and placed the user segment directly after the kernel segment. This memory map worked well for several development iterations, but recently caused a new problem related to reading sectors from the floppy drive.

The BIOS call to read storage drive sectors is interrupt 0x13, function 2. The call takes a 16-bit segment:offset pointer to the memory location to start writing the data after reading it. A requirement of this call is that the write operation not cross a physical segment boundary. Segment boundaries are at every 64 KiB: 0x10000, 0x20000, 0x30000, and so on. While the user reservation at 0x18000 is intended for 64 KiB of program data, the base of that reservation is only 32 KiB away from the next physical segment boundary of 0x20000. So trying to write a floppy sector into the user area with pointer 1800:>=7e00 crosses that boundary. Then the BIOS call fails with a DMA boundary error.

To fix this problem, I aligned the kernel and user segment reservations to physical segment boundaries. The current memory map is the same as the previous one, except the kernel area now starts at 0x10000, and the user area starts at 0x20000.