summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c
blob: becb02677a108d8c90d5f8a4439239c7d09381aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * librm: a library for interfacing to real-mode code
 *
 * Michael Brown <mbrown@fensystems.co.uk>
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stdint.h>
#include <ipxe/profile.h>
#include <realmode.h>
#include <pic8259.h>

/*
 * This file provides functions for managing librm.
 *
 */

/** The interrupt wrapper */
extern char interrupt_wrapper[];

/** The interrupt vectors */
static struct interrupt_vector intr_vec[NUM_INT];

/** The interrupt descriptor table */
struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) ));

/** The interrupt descriptor table register */
struct idtr idtr = {
	.limit = ( sizeof ( idt ) - 1 ),
};

/** Timer interrupt profiler */
static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };

/** Other interrupt profiler */
static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };

/**
 * Allocate space on the real-mode stack and copy data there from a
 * user buffer
 *
 * @v data		User buffer
 * @v size		Size of stack data
 * @ret sp		New value of real-mode stack pointer
 */
uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
	userptr_t rm_stack;
	rm_sp -= size;
	rm_stack = real_to_user ( rm_ss, rm_sp );
	memcpy_user ( rm_stack, 0, data, 0, size );
	return rm_sp;
};

/**
 * Deallocate space on the real-mode stack, optionally copying back
 * data to a user buffer.
 *
 * @v data		User buffer
 * @v size		Size of stack data
 */
void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
	if ( data ) {
		userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
		memcpy_user ( rm_stack, 0, data, 0, size );
	}
	rm_sp += size;
};

/**
 * Set interrupt vector
 *
 * @v intr		Interrupt number
 * @v vector		Interrupt vector, or NULL to disable
 */
void set_interrupt_vector ( unsigned int intr, void *vector ) {
	struct interrupt_descriptor *idte;

	idte = &idt[intr];
	idte->segment = VIRTUAL_CS;
	idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
	idte->low = ( ( ( uint32_t ) vector ) & 0xffff );
	idte->high = ( ( ( uint32_t ) vector ) >> 16 );
}

/**
 * Initialise interrupt descriptor table
 *
 */
void init_idt ( void ) {
	struct interrupt_vector *vec;
	unsigned int intr;

	/* Initialise the interrupt descriptor table and interrupt vectors */
	for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
		vec = &intr_vec[intr];
		vec->pushal = PUSHAL_INSN;
		vec->movb = MOVB_INSN;
		vec->intr = intr;
		vec->jmp = JMP_INSN;
		vec->offset = ( ( uint32_t ) interrupt_wrapper -
				( uint32_t ) vec->next );
		set_interrupt_vector ( intr, vec );
	}
	DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
	       intr_vec, sizeof ( intr_vec[0] ),
	       virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );

	/* Initialise the interrupt descriptor table register */
	idtr.base = virt_to_phys ( idt );
}

/**
 * Determine interrupt profiler (for debugging)
 *
 * @v intr		Interrupt number
 * @ret profiler	Profiler
 */
static struct profiler * interrupt_profiler ( int intr ) {

	switch ( intr ) {
	case IRQ_INT ( 0 ) :
		return &timer_irq_profiler;
	default:
		return &other_irq_profiler;
	}
}

/**
 * Interrupt handler
 *
 * @v intr		Interrupt number
 */
void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int intr ) {
	struct profiler *profiler = interrupt_profiler ( intr );
	uint32_t discard_eax;

	/* Reissue interrupt in real mode */
	profile_start ( profiler );
	__asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
					   "\n1:\n\t"
					   "int $0x00\n\t" )
			       : "=a" ( discard_eax ) : "0" ( intr ) );
	profile_stop ( profiler );
	profile_exclude ( profiler );
}

PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
PROVIDE_UACCESS_INLINE ( librm, userptr_add );
PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
PROVIDE_UACCESS_INLINE ( librm, memmove_user );
PROVIDE_UACCESS_INLINE ( librm, memset_user );
PROVIDE_UACCESS_INLINE ( librm, strlen_user );
PROVIDE_UACCESS_INLINE ( librm, memchr_user );