My Haiku RISC-V port progress

I implemented my own MMU code in both “efi” and “riscv” platforms, there are no machine mode and page table setup code in kernel anymore. There are some tricky things such as arch_enter_kernel() function code should be identity mapped, otherwise it will crash after setting SATP register. Also boot loader maps MMIO registers of some devices such as UART, PLIC, CLINT.

Page table initialization code looks very nice:

static void
SetupPageTable()
{
	sPageTable = AllocPhysPage();

	PreallocKernelRange();

	// Physical memory mapping
	gKernelArgs.arch_args.physMap.size
		= gKernelArgs.physical_memory_range[0].size;
	gKernelArgs.arch_args.physMap.start = KERNEL_TOP + 1
		- gKernelArgs.arch_args.physMap.size;
	MapRange(gKernelArgs.arch_args.physMap.start,
		gKernelArgs.physical_memory_range[0].start,
		gKernelArgs.arch_args.physMap.size,
		(1 << pteRead) | (1 << pteWrite));

	// Boot loader
	MapRangeIdentity((addr_t)gMemBase, &gStackEnd - gMemBase,
		(1 << pteRead) | (1 << pteWrite) | (1 << pteExec));

	// Memory regions
	MemoryRegion* region;
	for (region = sRegions; region != NULL; region = region->next) {
		uint64 flags = 0;
		if ((region->protection & B_READ_AREA) != 0)
			flags |= (1 << pteRead);
		if ((region->protection & B_WRITE_AREA) != 0)
			flags |= (1 << pteWrite);
		if ((region->protection & B_EXECUTE_AREA) != 0)
			flags |= (1 << pteExec);
		MapRange(region->virtAdr, region->physAdr, region->size, flags);
	}

	// Devices
	MapAddrRange(gKernelArgs.arch_args.clint, (1 << pteRead) | (1 << pteWrite));
	MapAddrRange(gKernelArgs.arch_args.htif, (1 << pteRead) | (1 << pteWrite));
	MapAddrRange(gKernelArgs.arch_args.plic, (1 << pteRead) | (1 << pteWrite));
	if (gKernelArgs.arch_args.uart.kind != kUartKindNone) {
		MapAddrRange(gKernelArgs.arch_args.uart.regs,
			(1 << pteRead) | (1 << pteWrite));
	}
}

EFI version:

uint64
arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
	efi_memory_descriptor *memory_map, size_t descriptor_size,
	uint32_t descriptor_version)
{
	sPageTable = mmu_allocate_page();
	memset(VirtFromPhys(sPageTable), 0, B_PAGE_SIZE);

	PreallocKernelRange();

	gKernelArgs.arch_args.num_virtual_ranges_to_keep = 0;

	FillPhysicalMemoryMap(memory_map_size, memory_map, descriptor_size, descriptor_version);

	addr_range physMemRange;
	GetPhysMemRange(physMemRange);
	printf("physMemRange: 0x%" B_PRIxADDR ", 0x%" B_PRIxSIZE "\n", physMemRange.start, physMemRange.size);

	// Physical memory mapping
	gKernelArgs.arch_args.physMap.start = KERNEL_TOP + 1 - physMemRange.size;
	gKernelArgs.arch_args.physMap.size = physMemRange.size;
	MapRange(gKernelArgs.arch_args.physMap.start, physMemRange.start, physMemRange.size, (1 << pteRead) | (1 << pteWrite));

	// Devices
	MapAddrRange(gKernelArgs.arch_args.clint, (1 << pteRead) | (1 << pteWrite));
	MapAddrRange(gKernelArgs.arch_args.htif, (1 << pteRead) | (1 << pteWrite));
	MapAddrRange(gKernelArgs.arch_args.plic, (1 << pteRead) | (1 << pteWrite));
	if (gKernelArgs.arch_args.uart.kind != kUartKindNone) {
		MapAddrRange(gKernelArgs.arch_args.uart.regs,
			(1 << pteRead) | (1 << pteWrite));
	}

	for (size_t i = 0; i < memory_map_size / descriptor_size; ++i) {
		efi_memory_descriptor* entry = &memory_map[i];
		switch (entry->Type) {
		case EfiLoaderCode:
		case EfiLoaderData:
			MapRange(entry->VirtualStart, entry->PhysicalStart, entry->NumberOfPages * B_PAGE_SIZE, (1 << pteRead) | (1 << pteWrite) | (1 << pteExec));
			break;
		default:
			;
		}
	}

	return GetSatp();
}
6 Likes