Page 1 of 1
How would I go about offsetting memory for program
Posted: Sat Nov 11, 2023 1:21 am
by vinny
I am working on a kernel for a CPU that doesn’t have a MMU, so I am going for a simple single-tasking system, much like the earlier DOS systems.
However, I would like to know how I would go about loading a program and setting its offset to where it assumes its free memory starts at 0 or wherever they automatically assume their memory starts, and more so — returning back to the kernel when the program is finished.
So basically, how would I go from kernel memory -> program -> kernel memory
Would I have to keep the kernel loaded in RAM at all times? Or would I leave a bit of “kernel loading” code somewhere to jump to when the program is done.
Re: How would I go about offsetting memory for program
Posted: Sat Nov 11, 2023 3:36 am
by thewrongchristian
vinny wrote:I am working on a kernel for a CPU that doesn’t have a MMU, so I am going for a simple single-tasking system, much like the earlier DOS systems.
However, I would like to know how I would go about loading a program and setting its offset to where it assumes its free memory starts at 0 or wherever they automatically assume their memory starts, and more so — returning back to the kernel when the program is finished.
So basically, how would I go from kernel memory -> program -> kernel memory
From the sounds of it, it doesn't look like your program will be using the kernel for anything, is that right?
So your kernel sounds more like a program loader and that's it?
vinny wrote:
Would I have to keep the kernel loaded in RAM at all times? Or would I leave a bit of “kernel loading” code somewhere to jump to when the program is done.
At a minimum, you'll need to keep in memory any kernel services required by the program. If your program uses the kernel to access any devices or filesystem, then clearly that will need to be kept resident.
And if you want to be able to load and run a sequence of different programs, the kernel will require some minimal device and filesystem drivers to load said programs from whatever storage you'll be using.
If you're NOT going to be running different programs, then you're perhaps better off with a unikernel design, where the kernel and application are one and the same, and are loaded and run together as a single unit.
From a build perspective, your kernel would effectively be a library that gets linked into your application, and the resulting binary is deployed to your device as required.
Then, all you'll need to keep resident is some sort of image loader, to which your application/unikernel will return to once it's finished, ready for you to load the next unikernel application.
Re: How would I go about offsetting memory for program
Posted: Sat Nov 11, 2023 8:33 am
by vinny
Sorry — should’ve explained my kernel structure more
My kernel is loaded from a binary file from the fat32 disk, and it has a basic fat32 driver at the moment. I plan on making a simple shell to execute and run these programs that needs to be returned to when the program is done executing.
Re: How would I go about offsetting memory for program
Posted: Sat Nov 11, 2023 5:27 pm
by thewrongchristian
vinny wrote:Sorry — should’ve explained my kernel structure more
My kernel is loaded from a binary file from the fat32 disk, and it has a basic fat32 driver at the moment. I plan on making a simple shell to execute and run these programs that needs to be returned to when the program is done executing.
So, it sounds like you need an exec system call to overlay the current process with a new program.
So, your kernel could start your shell (using exec() internally,) do whatever your shell needs to do determine the next program to run, exec() that (overlaying the shell), then the program will exit().
In your kernel, exit() will just be the trigger to restart the shell using exec() again, and the process repeats.
This is basically what DOS and COMMAND.COM together does.
And of course, the benefit of using exec/exit is that you can develop your user programs on an existing posix like system.
Re: How would I go about offsetting memory for program
Posted: Sat Nov 11, 2023 5:40 pm
by Octocontrabass
Those early DOS systems just loaded programs to a fixed location (often 0x100) and then stayed mostly in memory so programs could use the kernel API for things like input and output, figuring out how much memory is available, and returning to the kernel. Using the kernel API meant the same software could run on different computers as long as the CPU was compatible.
If the shell is an ordinary program, you can free up the memory it uses while other programs are running, and allow the user to specify which program should be the shell.
Re: How would I go about offsetting memory for program
Posted: Sun Nov 12, 2023 1:22 pm
by nullplan
vinny wrote:However, I would like to know how I would go about loading a program and setting its offset to where it assumes its free memory starts at 0 or wherever they automatically assume their memory starts, and more so — returning back to the kernel when the program is finished.
All single-address-space OSes have to have all their programs be position-independent. Typically by way of an ABI that allows for that. In the case of DOS, the program was loaded to an arbitrary paragraph boundary, and then the segment registers were set up to always make it seem like the thing was running at address 0x100. DOS also had its system calls in software interrupt 0x21. Though there was no need for privilege escalation in those days, there was still a need for hardware (and lowlevel software) abstraction. It is just way nicer to tell the OS to open "foo.txt" than to write a PATA+FAT driver yourself.
For a non-x86 example I can offer my knowledge of OS-9 on the PPC, which simply has register r2 point to the data memory and r13 point to the code memory, and so the program can access constants and variable data relative to those registers safely. Note that OS-9 is multi-tasking, though, so it wants to keep code and data separate so multiple instances of the same program can share code.
So that's what you will have to do: Figure out a way for the OS to tell the program where it is. And a way for the program to make kernel calls. The rest should then follow.
Re: How would I go about offsetting memory for program
Posted: Sun Nov 12, 2023 1:38 pm
by Octocontrabass
nullplan wrote:All single-address-space OSes have to have all their programs be position-independent.
Only the ones that can run more than one program at a time. (Early DOS systems could not do that.)
Re: How would I go about offsetting memory for program
Posted: Mon Jan 15, 2024 9:36 am
by eekee
vinny wrote:I would like to know how I would go about loading a program and setting its offset to where it assumes its free memory starts at 0 or wherever they automatically assume their memory starts
The design phase of this is almost easy: you choose where the program will be loaded. A good design is to have the kernel at one end of available memory and the program at the other. This puts all the free space into one contiguous chunk between them. 8-bit computers used this scheme, having a system variable -- a predefined address holding a value -- to mark the top of memory available to the program. They could have as many as 3 system variables which might be called RAMTOP for the top of physical RAM, MEMTOP for the highest point below the (shared) screen memory, and APPMHI for the point at which allocation would overwrite drivers and stuff. Or, screen memory might be below driver memory if it changes size with different graphics modes. This is a very powerful scheme, allowing all sorts of stuff to be loaded into the driver area.
How to get programs to know where they'll be is a separate question. In assembly lanuage without a linker, you set the load and start addresses in the source code so that the assembler knows what addresses to generate. You may want an include file to do this. The include file can also give names to the locations of system variables.
With a linker, (whether assembling or compiling,) I'm not so sure since I don't use linkers. (Yet!
) Ideally, the linker would be told where your program will be loaded and its entry point. If your linker is confusing, you might find it easier to link an assembly-language function to the start of your program. In another thread,
Octocontrabass went into detail on how to do this. He was describing a bootloader loading a kernel, but it's very much the same as a single-tasking OS loading a program.
vinny wrote:— returning back to the kernel when the program is finished.
This is basically just another system call. You jump (or INT or TRAP or whatever) to a routine which cleans up and restores the shell.
vinny wrote:Would I have to keep the kernel loaded in RAM at all times? Or would I leave a bit of “kernel loading” code somewhere to jump to when the program is done.
This gets easier if you think about what your kernel is made up of. You can probably divide it into two parts, drivers and shell. The drivers should, of course, stay in memory to be used by the program. The bulk of the shell can be removed while programs are running, but you might want to keep part of the shell in memory depending on how much work the kernel delegates to the shell. Without an MMU, you have essentially infinite freedom in what part does what.