Skip to content

Adding Architectures

Petr Tesarik edited this page Jul 4, 2023 · 8 revisions

Adding Architectures

This is a step-by-step guide to add support for a new target architecture. If you add an architecture, you do not have to implement everything described here, but the library may offer less features and/or worse performance for your architecture.

Page Table Translation

This step is a pre-requisite for everything else.

First, choose a name for the translation format(s). Use a generic name based on the name of the ISA rather than a specific implementation. Add suffixes to distinguish between multiple translation variants if necessary.

Second, write a test case for the target address translation. Of course, the test will fail at this point, but you can use it to track your progress. More importantly, you can use this test case to make sure that already implemented features do not break as you write more code. Name your test case like tests/addrxlat-newarch-variant. Start with this dummy template:

#! /bin/sh
pf="newarch:12,9,9"
list="0:0"  # dummy
. "$srcdir"/addrxlat-common

Don't forget to make the script executable and add it to the test_scripts variable in test/Makefile.am. If you run the test suite now, the new test should fail like this:

Checking 0... Unknown PTE format: newarch:12,9,9
ERROR
Cannot translate 0

Note: You can run a single test case from the test suite with a command like srcdir=. ./addrxlat-newarch-variant from the tests directory. However, if you take this shortcut, remember to rebuild the library and tests/addrxlat after every change, e.g. with (cd .. && make) && make addrxlat.

Next, add an identifier for your architecture's translation format to the addrxlat_pte_format_t enum in include/libkdumpfile/addrxlat.h.in. Add a string representation of the translation format to the pte_format_names array in src/addrxlat/step.c. The output of your test case should change to:

Checking 0... Address translation failed: Unknown PTE format
FAILED

You can start implementing actual address translation now. If your architecture offers various formats of a page table entry, you may want to start with the simplest variant and gradually expand your code.

Always start by adding a test case. Change the dummy page table and list of addresses in tests/addrxlat-newarch-variant to add the expected page translation result, e.g.:

ptes="-e 0x000:0x1000"  # PGD[0] -> 1000
ptes="$ptes -e 0x1000:0xa000"  # PGD[0] -> PTE[0] -> a000

list="0x123:0xa123"  # PGT[0] -> PTE[0]

Next, create a new file src/addrxlat/newarch.c. Assuming your target architecture does nothing fancy, you can modify this very minimal stub:

#include "addrxlat-priv.h"

/** Page mask. */
#define PAGE_MASK		ADDR_MASK(12)

/** New architecture page table step function.
 * @param step  Current step state.
 * @returns     Error status.
 */
addrxlat_status
pgt_newarch(addrxlat_step_t *step)
{
	addrxlat_pte_t pte;
	addrxlat_status status;

	status = read_pte32(step, &pte);
	if (status != ADDRXLAT_OK)
		return status;

	step->base.as = step->meth->target_as;
	step->base.addr = pte & ~PAGE_MASK;
	if (step->remain == 1)
		step->elemsz = 1;

	return ADDRXLAT_OK;
}

Note that these are just the bare bones of a step functions. The function does not even check a present (or valid) bit, required by all architectures known to me.

Add the above file to libaddrxlat_la_SOURCES in src/addrxlat/Makefile.am.

Add the declaration of your page table step function to src/addrxlat/addrxlat-priv.h (sorted alphabetically):

INTERNAL_DECL(addrxlat_next_step_fn, pgt_newarch, );

To use the generic page table translation helpers, let the library know the size of a single page table entry by extending pteval_shift() in src/addrxlat/addrxlat-priv.h. Make it return 2 for 32-bit entries and 3 for 64-bit entries.

Next, extend the switches in first_step_pgt() and next_step_pgt(). The latter should obviously call pgt_newarch(step), but the former may be less clear:

  • Call first_step_pgt_generic(step, addr) if your architecture uses all bits of the translation input address, or if unused bits are ignored.
  • Call first_step_uaddr(step, addr) if unused bits must be zero.
  • Call first_step_saddr(step, addr) if unused bits must be a copy of the highest used bit (the sign bit).
  • Write a new helper if none of the above applies.

When you're done with this part, make a commit. These commits are titled like Add newarch page table translation.

Linux Support

This section also deals with the generic OS handling. If you intend to support only a non-Linux operating system, you will also need to implement the generic infrastructure described here.

First, start with a test case. The generic base name follows the pattern of xlat-linux-newarch-version-variant, where -variant may be missing. Each test case uses the following files:

  • script (no suffix),
  • expected results (.expect suffix),
  • (optional) symbolic values (.sym suffix),
  • (optional) memory content (.data suffix).

Add the script to the test_scripts variable and the other file names to the dist_check_DATA variable in tests/Makefile.am.

The script file defines options for addrxlat_sys_os_init(). A minimal base for Linux would be:

#! /bin/bash
opts=(
	arch=newarch
	ostype=linux
)
. "$srcdir"/xlat-os-common

Note: You can run a single test case from the test suite with a command like srcdir=. ./xlat-linux-newarch-version from the tests directory. However, if you take this shortcut, remember to rebuild the library and tests/xlat-os after every change, e.g. with (cd .. && make) && make xlat-os.

Initially, this test should fail with:

Checking... OS map failed: Unsupported architecture
ERROR

Good. Let's add the base infrastructure. Add a stub initialization function to src/addrxlat/newarch.c:

/** Initialize a translation map for a newarch OS.
 * @param ctl  Initialization data.
 * @returns    Error status.
 */
addrxlat_status
sys_newarch(struct os_init_data *ctl)
{
	switch (ctl->os_type) {
	default:
		return set_error(ctl->ctx, ADDRXLAT_ERR_NOTIMPL,
				 "OS type not implemented");
	}
}

Also put a declaration in src/addrxlat/addrxlat-priv.h:

INTERNAL_DECL(sys_arch_fn, sys_newarch, );

Finally, add a check to the conditional in addrxlat_sys_os_init() at src/addrxlat/sys.c. FIXME: This should be somehow merged into the pte_format_names array.

Verify that the new function is used by the test case. The failure message should change to:

Checking... OS map failed: OS type not implemented
ERROR

Last, implement the actual logic. This depends on how address translation is set up in the kernel for your specific architecture. The goal is to initialize a working translation system. This usually means setting up page table translation using the initial page table hierarchy (swapper_pg_dir). If you also set up the linear 1:1 direct mapping with sys_set_layout(), the corresponding reverse mapping is created automatically, and the translation system can be used to translate from physical addresses to virtual addresses. This is useful if your target format can handle only virtual addresses.

Do not forget to set up the translation between machine physical and kernel physical addresses. In most cases, this is simply an identity mapping covering all physical addresses, and there is a helper for that: sys_set_physmaps().

The .expect file for the test case is a dump of the resulting translation system. It contains:

  • translation methods (prefixed with @)
  • translation mappings (input AS -> output AS)

If symbol values are required for the test, provide them in a .syms file. If dump memory content is required, provide it in a .data file.

Have a look at some of the already implemented architectures for inspiration.

Clone this wiki locally