Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <kernel_internal.h>
#include <kernel_structs.h>
#include <debug/tracing.h>
#include <ksched.h>
#include <irq_offload.h>
#include "xuk.h"

/* Always pick a lowest priority interrupt for scheduling IPI's, by
 * definition they're done on behalf of thread mode code and should
 * never preempt a true device interrupt
 */
#define SCHED_IPI_VECTOR 0x20

struct device;

struct z_arch_esf_t {
};

void z_new_thread(struct k_thread *t, k_thread_stack_t *stack,
		 size_t sz, k_thread_entry_t entry,
		 void *p1, void *p2, void *p3,
		 int prio, unsigned int opts)
{
	void *args[] = { entry, p1, p2, p3 };
	int nargs = 4;
	int eflags = 0x200;
	char *base = Z_THREAD_STACK_BUFFER(stack);
	char *top = base + sz;

	z_new_thread_init(t, base, sz, prio, opts);

	t->switch_handle = (void *)xuk_setup_stack((long) top,
						   (void *)z_thread_entry,
						   eflags, (long *)args,
						   nargs);
}

void k_cpu_idle(void)
{
	z_sys_trace_idle();
	__asm__ volatile("sti; hlt");
}

void z_unhandled_vector(int vector, int err, struct xuk_entry_frame *f)
{
	/* Yes, there are five regsiters missing.  See notes on
	 * xuk_entry_frame/xuk_stack_frame.
	 */
	z_fatal_print("*** FATAL ERROR vector %d code %d", vector, err);
	z_fatal_print("***  RIP %d:0x%llx RSP %d:0x%llx RFLAGS 0x%llx",
		      (int)f->cs, f->rip, (int)f->ss, f->rsp, f->rflags);
	z_fatal_print("***  RAX 0x%llx RCX 0x%llx RDX 0x%llx RSI 0x%llx RDI 0x%llx",
		      f->rax, f->rcx, f->rdx, f->rsi, f->rdi);
	z_fatal_print("***  R8 0x%llx R9 0x%llx R10 0x%llx R11 0x%llx",
		      f->r8, f->r9, f->r10, f->r11);

	/* FIXME: Why isn't xuk_entry_frame a z_arch_esf_t? */
	z_fatal_error(x86_64_except_reason, NULL);
}

void z_isr_entry(void)
{
	z_arch_curr_cpu()->nested++;
}

void *z_isr_exit_restore_stack(void *interrupted)
{
	bool nested = (--z_arch_curr_cpu()->nested) > 0;
	void *next = z_get_next_switch_handle(interrupted);

	return (nested || next == interrupted) ? NULL : next;
}

volatile struct {
	void (*fn)(int, void*);
	void *arg;
} cpu_init[CONFIG_MP_NUM_CPUS];

/* Called from Zephyr initialization */
void z_arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
		     void (*fn)(int, void *), void *arg)
{
	xuk_start_cpu(cpu_num, (int)(sz + (char *)stack));

	cpu_init[cpu_num].arg = arg;

	/* This is our flag to the spinning CPU.  Do this last */
	cpu_init[cpu_num].fn = fn;
}

#ifdef CONFIG_IRQ_OFFLOAD
static irq_offload_routine_t offload_fn;
static void *offload_arg;

static void irq_offload_handler(void *arg, int err)
{
	ARG_UNUSED(arg);
	ARG_UNUSED(err);
	offload_fn(offload_arg);
}

void irq_offload(irq_offload_routine_t fn, void *arg)
{
	offload_fn = fn;
	offload_arg = arg;
	__asm__ volatile("int %0" : : "i"(CONFIG_IRQ_OFFLOAD_VECTOR));
}
#endif

/* Default.  Can be overridden at link time by a timer driver */
void __weak x86_apic_timer_isr(void *arg, int code)
{
	ARG_UNUSED(arg);
	ARG_UNUSED(code);
}

static void sched_ipi_handler(void *arg, int err)
{
	ARG_UNUSED(arg);
	ARG_UNUSED(err);
#ifdef CONFIG_SMP
	z_sched_ipi();
#endif
}

void z_arch_sched_ipi(void)
{
	_apic.ICR_HI = (struct apic_icr_hi) {};
	_apic.ICR_LO = (struct apic_icr_lo) {
		.delivery_mode = FIXED,
		.vector = SCHED_IPI_VECTOR,
		.shorthand = NOTSELF,
	};
}


/* Called from xuk layer on actual CPU start */
void z_cpu_start(int cpu)
{
	xuk_set_f_ptr(cpu, &_kernel.cpus[cpu]);

	/* Set up the timer ISR, but ensure the timer is disabled */
	xuk_set_isr(INT_APIC_LVT_TIMER, 13, x86_apic_timer_isr, 0);
	_apic.INIT_COUNT = 0U;

	xuk_set_isr(XUK_INT_RAW_VECTOR(SCHED_IPI_VECTOR),
		    -1, sched_ipi_handler, 0);

#ifdef CONFIG_IRQ_OFFLOAD
	xuk_set_isr(XUK_INT_RAW_VECTOR(CONFIG_IRQ_OFFLOAD_VECTOR),
		    -1, irq_offload_handler, 0);
#endif

	if (cpu <= 0) {
		/* The SMP CPU startup function pointers act as init
		 * flags.  Zero them here because this code is running
		 * BEFORE .bss is zeroed!  Should probably move that
		 * out of z_cstart() for this architecture...
		 */
		for (int i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
			cpu_init[i].fn = 0;
		}

		/* Enter Zephyr */
		z_cstart();

	} else if (cpu < CONFIG_MP_NUM_CPUS) {
		/* SMP initialization.  First spin, waiting for
		 * z_arch_start_cpu() to be called from the main CPU
		 */
		while (!cpu_init[cpu].fn) {
		}

		/* Enter Zephyr, which will switch away and never return */
		cpu_init[cpu].fn(0, cpu_init[cpu].arg);
	}

	/* Spin forever as a fallback */
	while (1) {
	}
}

int z_arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
			       void (*routine)(void *parameter), void *parameter,
			       u32_t flags)
{
	ARG_UNUSED(flags);
	__ASSERT(priority >= 2U && priority <= 15U,
		 "APIC interrupt priority must be 2-15");

	xuk_set_isr(irq, priority, (void *)routine, parameter);
	return 0;
}

void z_arch_irq_disable(unsigned int irq)
{
	xuk_set_isr_mask(irq, 1);
}

void z_arch_irq_enable(unsigned int irq)
{
	xuk_set_isr_mask(irq, 0);
}

void x86_apic_set_timeout(u32_t cyc_from_now)
{
	_apic.INIT_COUNT = cyc_from_now;
}

int x86_64_except_reason;