Search (using Google):  Web Karig

 

2 December 2003

Kernel records

I will want my system loader to gather data about the hardware for the benefit of Karig's kernel. The amount of data will vary from one machine to another, so I worked out a plan for storing variable amounts of data.

I plan to store this data in real-mode segment 0x1000, which I'm calling my record segment. Data will be gathered into blocks, each of which contains one or more records, each of which in turn contains one or more fields (discrete pieces of data). The record segment will not be larger than 64KB, so that a block size or offset can always be stored in two bytes.

The first block in the segment, the start block, contains records containing the size and location of other blocks. Thus the start block serves as the segment's directory. The start block is always at offset zero within the segment.

Start-block layout

When the record segment is first initialized, the start block (and in fact the whole record segment) is all zeroes — except for the first record (Record #0), which contains two fields:

  • Data size. This is the sum of the sizes in bytes of all blocks in the segment. After initialization, the segment contains only the start block, so the data size is 64.

  • Start-block size. This is fixed at 64 bytes for now. This makes the start block large enough to hold sixteen four-byte records.

Each of the other records in the directory contains these two fields:

  • Offset. This is the distance between the start of the segment and the start of a specific block.

  • Size. This is the size of the block in bytes.

If both fields are zero, the record is unused.

The other records in the start block will be allocated as follows:

  • Record #1: Usable memory. This record will point to a block that contains a series of records, each listing the address and size of a contiguous block of memory that Karig can use to store data.

  • Record #2: Partition sectors. This record will point to a block that contains a series of records, each listing (1) the LBA (logical block address) of the first sector of a single file fragment (Karig's partition actually being a file stored on a Windows drive) and (2) the length of the fragment in sectors.

  • Records #3 through #14 are reserved for other kinds of data that I might decide to gather for Karig in the future.

  • Record #15: Next directory block. The last record in the block serves as a link to the next block in the directory chain, if a next block is needed. Thus the directory, if it contains more entries than the first record block can hold, will be a singly-linked list.

Note that the records in the directory give no hints about the contents of the blocks in the segment. The kernel would search for a record block by number; it would know that Record #1 leads to the list of usable memory areas, that Record #2 leads to the list of writable disk areas, and so on.

Initializing the segment

The code to initialize the record segment follows:

As usual, this code runs in real mode.

[BITS 16]
main:

An "empty" record segment contains only the first record block, which contains all zeroes, except for the first two records. Record #0 contains the offset and size of all record blocks in the segment, and Record #1 contains the offset and size of just the first record block. The data in both of these records will be the same: The offset is zero (the record block begins at the very beginning of the segment) and the size is 64. All other records are unused.

So to initialize the record segment, I first fill it with zero bytes. (Note that this code points ES at the record segment.)

		cld
		mov	ax, 0x1000
		mov	es, ax
		xor	di, di
		xor	ax, ax
		mov	cx, 32768
		rep	stosw

Then I store the value 64 into the two size fields (the total-data size at offset zero, and the start-block size at offset two).

		mov	al, 64
		mov	[es:0], al
		mov	[es:2], al

If I want to change ES back to zero, so that it points at the boot segment (where the boot sector and system loader are located)), I can just execute:

		mov	es, di

Adding a block

To add a record block (and store that block's data in the segment's first record block), there are three basic steps: Alter record #0 (to reflect the new total size of all blocks); alter the new block's record; add data to the new block.

Grab the total size of all blocks (from the size field in record #0).

		mov	ax, [es:0]
		mov	bx, ax

Add to this the proposed size of the new record block. If the result overflows, then abort with an error.

		mov	cx, NEWSIZE
		add	ax, cx
		jc	.error

Otherwise, store the old total size to the offset field in the new block's record...

		mov	[es:OFFSETFIELD], bx

...and store the new total size to the size field in record #0.

		mov	[es:0], ax

Store the new block's size in the size field in the new block's record.

		mov	[es:SIZEFIELD], cx

I don't have a source-code file this time around. I'll supply one when I discuss gathering memory data.

Check the index for other entries.