VxKernel 0.3.0 : Worlds
@add <> include/vhex/arch/sh7305/cpu : add CPU hardware information <> include/vhex/arch/sh7305/intc : add INTC hardware information <> include/vhex/driver : add driver information <> include/vhex/hypervisor : add hypervisor API <> include/vhex/drivers/cpu : add CPU API <> src/drivers/mpu/sh/sh7305/cpu/atomic : add CPU atomic primitives <> src/drivers/mpu/sh/sh7305/cpu/cpu : add CPU driver for the hypervisor <> src/drivers/mpu/sh/sh7305/cpu/register : add CPU registers abstraction <> src/drivers/mpu/sh/sh7305/intc/intc : add INTC driver for the hypervisor <> src/hypervisor/switch : add world switch <> src/hypervisor/table : add world management @update <> vxsdk.toml : update the project version <> src/kernel/kernel : hypervisor bootstrap (worlds switch between Casio / Vhex) <> board/fxcg50/fxcg50.ld : drivers blocks @fix <> board/fxcg50/fxcg50.ld : generate the VRAM information (300ko -> 130ko) <> board/fxcg50/fxcg50.ld : isolate BSS information <> board/fxcg50/initialize : comments indentation <> config : board and arch detection (os.walk()) <> gitignore : allow vxsdk.toml update
This commit is contained in:
parent
2fbee9cdeb
commit
df8d26ecca
|
@ -30,6 +30,23 @@ SECTIONS
|
|||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_edtors = . ;
|
||||
|
||||
} > userram
|
||||
|
||||
/* vhex's interrupt handler blocks (.vhex.blocks)
|
||||
Although vhex's blocks end up in VBR space, they are relocated at
|
||||
startup by the library/drivers, so we store them here for now */
|
||||
.vhex.blocks : {
|
||||
KEEP(*(.vhex.blocks));
|
||||
} > userram
|
||||
|
||||
/* Exposed driver interfaces (.vhex.drivers)
|
||||
The driver information is required to start and configure the
|
||||
driver, even if the symbols are not referenced */
|
||||
.vhex.drivers : {
|
||||
_vhex_drivers_start = . ;
|
||||
KEEP(*(SORT_BY_NAME(.vhex.drivers.*)));
|
||||
_vhex_drivers_end = . ;
|
||||
} > userram
|
||||
|
||||
/* Read-only sections */
|
||||
|
@ -66,16 +83,77 @@ SECTIONS
|
|||
/* Data sections */
|
||||
*(.data);
|
||||
*(.data.*);
|
||||
*(COMMON);
|
||||
}
|
||||
|
||||
.bss ALIGN(4) : {
|
||||
/* bss section included to avoid missaligned segment */
|
||||
*(.bss);
|
||||
*(.bss.*);
|
||||
*(.dynbss)
|
||||
*(COMMON);
|
||||
|
||||
PROVIDE(___sram_start = ALIGN(1024));
|
||||
/* dynamic BSS information (move me ?) */
|
||||
*(.dynbss)
|
||||
|
||||
/* Video RAM symbols
|
||||
The video RAM contains a full pixel sized frame for the
|
||||
screen. Its size is 396x224 and each pixel depth is 16its,
|
||||
so (3996 * 224) * 2 = 177408
|
||||
*/
|
||||
_vhex_vram = ALIGN(4);
|
||||
|
||||
/* create the VRAM gap */
|
||||
. = _vhex_vram + 177408;
|
||||
|
||||
} > userram
|
||||
|
||||
/* Vhex VBR management geometry
|
||||
|
||||
Due to the SH3/SH4 VBR system, we have some restrictions:
|
||||
|
||||
The handlers should reside at VBR relative position, in P1 or P2
|
||||
protected space (0x8* or 0xA*). For our case, the bootloader will
|
||||
relocalise the kernel at the P1 area using the Casio's UTLB
|
||||
information. So, we don't have to wories about this here.
|
||||
|
||||
There are 3 vectors offset called by the processor : VBR + 0x100,
|
||||
0x400 and 0x600. The first offset is involved when an exception
|
||||
occur, the second when a memory (TLB) error is detected and the last
|
||||
when an interruptions is detected.
|
||||
|
||||
All exception and memory interuption will be handled "normaly" : a
|
||||
simple handler will be involved which will try to resolve/fix the
|
||||
problem, if nothing can be fixed, a panic screen will be displayed.
|
||||
|
||||
However, regarding interuptions, we will use a power-full and
|
||||
complexe technique to compact as most as possible the VBR size. To
|
||||
explain the technique we need to know that each interruption start at
|
||||
offset VBR + 0x600 and each interrupt source is "gapped" by 0x20 (
|
||||
for exemple the 0x400 is for TMU0, 0x420 is for TMU1 and 0x440 is for
|
||||
TMU2) Moreover, we have some "hole" in the "interrup map".
|
||||
So, the idea is to write all interrupt handler in "blocks" of 32
|
||||
bytes, and, for those that 32 bytes is to short, can use hole of the
|
||||
interrupt map. */
|
||||
.vhex.vbr : ALIGN(4) {
|
||||
|
||||
_vhex_vbr = . - 0x100;
|
||||
*(.vhex.exch.pretext) ;
|
||||
*(.vhex.exch)
|
||||
|
||||
. = _vhex_vbr + 0x400;
|
||||
*(.vhex.tlbh.pretext) ;
|
||||
*(.vhex.tlbh) ;
|
||||
|
||||
. = _vhex_vbr + 0x600;
|
||||
*(.vhex.interrupth.pretext) ;
|
||||
*(.vhex.interrupth) ;
|
||||
|
||||
/* interrupt block entry */
|
||||
__vhex_interrupth_start = ALIGN(4);
|
||||
|
||||
/* static ram start (for kmalloc) */
|
||||
PROVIDE(___sram_start = ALIGN(4) + 4096);
|
||||
|
||||
} > userram
|
||||
|
||||
/* unwanted section */
|
||||
/DISCARD/ : {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//---
|
||||
// vhex:core:hardware - Platform information and hardware detection
|
||||
//---
|
||||
#include "vhex/mpu/sh7305/pfc.h"
|
||||
#include "vhex/arch/fxcg50/hardware.h"
|
||||
#include "vhex/arch/sh7305/pfc.h"
|
||||
#include "board/fxcg50/hardware.h"
|
||||
#include "vhex/hardware.h"
|
||||
#include "vhex/mmu.h"
|
||||
#include "vhex/drivers/mmu.h"
|
||||
|
||||
|
||||
/* Holds information about the current platform */
|
||||
|
|
|
@ -109,15 +109,15 @@ static void kmalloc_init(void)
|
|||
+---------------+ <-- variable
|
||||
|
||||
For the memory discovery. This an "early" exotic step for the vxkernel
|
||||
because we need to detect the RAM geometry to configure the "kmalloc" module
|
||||
(which provide memory management API like kmalloc(), kfree(), ...). For more
|
||||
information about this part is in <board/fxcg50/hardware.c>
|
||||
because we need to detect the RAM geometry to configure the "kmalloc" module
|
||||
(which provide memory management API like kmalloc(), kfree(), ...). For more
|
||||
information about this part is in <board/fxcg50/hardware.c>
|
||||
|
||||
The function initialize is the "real" first code executed by the kernel. Its
|
||||
role is to perform some hardware detection, create and initialize the
|
||||
hypervisor, bootstrap the vxkernel world (see <include/hypervisor.h> for
|
||||
details) which will initialize the device, then involve the "user" main()
|
||||
routine. */
|
||||
role is to perform some hardware detection, create and initialize the
|
||||
hypervisor, bootstrap the vxkernel world (see <include/hypervisor.h> for
|
||||
details) which will initialize the device, then involve the "user" main()
|
||||
routine. */
|
||||
void initialize(void)
|
||||
{
|
||||
/* Detect hardware information : This part is important because it will
|
||||
|
|
6
config
6
config
|
@ -82,10 +82,8 @@ def parse_arguments():
|
|||
#
|
||||
|
||||
def board_check(file, board_list):
|
||||
(boards,_,_) = os.walk('../board')
|
||||
(archs,_,_) = os.walk('../src/drivers/mpu')
|
||||
boards = boards[1]
|
||||
archs = archs[1]
|
||||
boards = list(os.walk('../board'))[0][1]
|
||||
archs = list(os.walk('../src/drivers/mpu'))[0][1]
|
||||
|
||||
if not board_list:
|
||||
print('board available:')
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef __VHEX_ARCH_SH7305_CPU__
|
||||
# define __VHEX_ARCH_SH7305_CPU__
|
||||
|
||||
#include <vhex/defs/types.h>
|
||||
#include <vhex/defs/attributes.h>
|
||||
|
||||
//---
|
||||
// Access to CPU registers
|
||||
//---
|
||||
|
||||
/* cpu_get_vbr() : get the VBR register */
|
||||
extern uintptr_t cpu_get_vbr(void);
|
||||
|
||||
/* cpu_set_vbr() : set the new VBR address */
|
||||
extern void cpu_set_vbr(uint32_t VBR);
|
||||
|
||||
|
||||
|
||||
|
||||
/* cpu_cpuopm_t : Bits of the CPU Operation Mode */
|
||||
typedef lword_union(cpu_cpuopm_t,
|
||||
uint32_t : 22;
|
||||
uint32_t _HIGH : 4; /* SOULD be 1 */
|
||||
uint32_t RABD : 1; /* Speculative fetching */
|
||||
uint32_t : 1;
|
||||
uint32_t INTMU : 1; /* Interrupt mode */
|
||||
uint32_t : 3;
|
||||
);
|
||||
|
||||
/* cpu_get_cpuopm() : read the CPU Operation Mode register. */
|
||||
cpu_cpuopm_t cpu_get_cpuopm(void);
|
||||
|
||||
/* cpu_set_cpuopm() : Update the CPU Operation Mode register.
|
||||
After a write, the register is re-read and an (icbi) instruction is executed
|
||||
to apply the change. Non-writable bits should be left to their initial value
|
||||
during a write. */
|
||||
extern void cpu_set_cpuopm(cpu_cpuopm_t cpuopm);
|
||||
|
||||
|
||||
|
||||
|
||||
/* cpu_str_t: Bits of the Status Register */
|
||||
typedef lword_union(cpu_sr_t,
|
||||
uint32_t : 1;
|
||||
uint32_t MD : 1; /* Processing mode */
|
||||
uint32_t RB : 1; /* Register bank */
|
||||
uint32_t BL : 1; /* Exception / Interrupt Block */
|
||||
uint32_t RC : 12; /* Repeat counter */
|
||||
uint32_t : 3;
|
||||
uint32_t DSP : 1; /* Enable DSP */
|
||||
uint32_t DMY : 1; /* Y pointer modulo addressing */
|
||||
uint32_t DMX : 1; /* X pointer module addressing */
|
||||
uint32_t M : 1; /* M bit (used in div0u, ...) */
|
||||
uint32_t Q : 1; /* Q bit (used in div0u, ...) */
|
||||
uint32_t IMASK : 4; /* Interrupt Mask */
|
||||
uint32_t RF : 2; /* Repeat flags */
|
||||
uint32_t S : 1; /* S bit (used with MAC operation) */
|
||||
uint32_t T : 1; /* T but (true/false, condition, ...) */
|
||||
);
|
||||
|
||||
/* cpu_get_sr() : get the Status Register */
|
||||
extern cpu_sr_t cpu_get_sr(void);
|
||||
|
||||
/* cpu_set_st() : write the SR register.
|
||||
When writing, only "permanent" bits are set:
|
||||
* MD, RB, BL, DSP, IMASK are set.
|
||||
* M, Q, S and T are not set to preserve the behavior of ongoing divisions
|
||||
and tests. You can change T with (sett) and (clrt).
|
||||
* RC, DMY, DMX and DF are not set: use (setrc), (setdmx), (setdmy), and
|
||||
(clrdmxy). DF is preserved for old-style (setrc) loops to work. */
|
||||
extern void cpu_set_sr(cpu_sr_t sr);
|
||||
|
||||
|
||||
|
||||
#endif /* __VHEX_ARCH_SH7305_CPU__ */
|
|
@ -0,0 +1,413 @@
|
|||
#ifndef __VHEX_ARCH_SH7305_INTC__
|
||||
# define __VHEX_ARCH_SH7305_INTC__
|
||||
|
||||
|
||||
/* sh7305_intc_ipc - Interrupt Priority Controller
|
||||
Some of the fields have been left unnamed because they correspond to SH7724
|
||||
peripheral modules that are *very* unlikely to even exist in the SH7305, let
|
||||
alone by of any use to us */
|
||||
typedef volatile struct {
|
||||
word_union(IPRA,
|
||||
uint16_t TMU0_0 :4; /* TMU0 Channel 0 */
|
||||
uint16_t TMU0_1 :4; /* TMU0 Channel 1 */
|
||||
uint16_t TMU0_2 :4; /* TMU0 Channel 2 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRB,
|
||||
uint16_t ADC : 4; /* A/D Converter */
|
||||
uint16_t _1 : 4; /* Unknown (TODO) */
|
||||
uint16_t _2 : 4; /* Unknown (TODO) */
|
||||
uint16_t : 4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRC,
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
uint16_t SPU :4; /* SPU's DSP0 and DSP1 */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRD,
|
||||
uint16_t :4;
|
||||
uint16_t _MMCIF :4; /* SH7724: MultiMedia Card Interface */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRE,
|
||||
uint16_t DMAC0A :4; /* Direct Memory Access Controller 0 */
|
||||
uint16_t :4;
|
||||
uint16_t ETMU3 :4; /* Extra TMU 3 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRF,
|
||||
uint16_t KEYSC :4; /* Key Scan Interface */
|
||||
uint16_t DMACOB :4; /* DMAC0 transfer/error info */
|
||||
uint16_t USB0_1 :4; /* USB controller */
|
||||
uint16_t CMT :4; /* Compare Match Timer */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRG,
|
||||
uint16_t _SCIF0 :4; /* SH7724: SCIF0 transfer/error info */
|
||||
uint16_t ETMU1 :4; /* Extra TMU 1 */
|
||||
uint16_t ETMU2 :4; /* Extra TMU 2 */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRH,
|
||||
uint16_t _MSIOF0:4; /* SH7724: Sync SCIF channel 0 */
|
||||
uint16_t _MSIOF1:4; /* SH7724: Sync SCIF channel 1 */
|
||||
uint16_t _1 :4; /* Unknown (TODO) */
|
||||
uint16_t _2 :4; /* Unknown (TODO) */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRI,
|
||||
uint16_t ETMU4 :4; /* Extra TMU 4 */
|
||||
uint16_t :4;
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRJ,
|
||||
uint16_t ETMU0 :4; /* Extra TMU 0 */
|
||||
uint16_t _1 :4; /* Unknown (TODO) */
|
||||
uint16_t FSI :4; /* FIFO-Buffered Serial Interface */
|
||||
uint16_t _2 :4; /* Unknown (TODO) */
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRK,
|
||||
uint16_t RTC :4; /* Real-Time Clock */
|
||||
uint16_t SDC :4; /* SD Card Controller */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
word_union(IPRL,
|
||||
uint16_t ETMU5 :4; /* Extra TMU 5 */
|
||||
uint16_t _ :4; /* Unknown (TODO) */
|
||||
uint16_t :4;
|
||||
uint16_t :4;
|
||||
);
|
||||
} VPACKED(4) sh7305_intc_ipc_t;
|
||||
|
||||
|
||||
|
||||
|
||||
/* sh7305_intc_masks_t - Interrupt mask management
|
||||
Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks.
|
||||
Writing 0 is ignored; reading from IMCRs yields undefined values */
|
||||
typedef volatile struct {
|
||||
byte_union(IMR0,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
uint8_t _2 : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR1,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t DEI3 : 1; /* DMAC0 channel 3 */
|
||||
uint8_t DEI2 : 1; /* DMAC0 channel 2 */
|
||||
uint8_t DEI1 : 1; /* DMAC0 channel 1 */
|
||||
uint8_t DEI0 : 1; /* DMAC0 channel 0 */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR2,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
uint8_t ETUNI3 : 1; /* ETMU3 */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR3,
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
uint8_t _2 : 1; /* Unknown (TODO) */
|
||||
uint8_t _3 : 1; /* Unknown (TODO) */
|
||||
uint8_t DSP0 : 1; /* SPU DSP1 */
|
||||
uint8_t DSP1 : 1; /* SPU DSP0 */
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR4,
|
||||
uint8_t : 1;
|
||||
uint8_t TUNI0 : 1; /* TMU0 timer 0 */
|
||||
uint8_t TUNI1 : 1; /* TMU0 timer 1 */
|
||||
uint8_t TUNI2 : 1; /* TMU0 timer 2 */
|
||||
uint8_t ADC : 1; /* A/D Converter */
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _ : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR5,
|
||||
uint8_t KEYI : 1; /* KEYSC */
|
||||
uint8_t DADERR : 1; /* DMAC0 Address error */
|
||||
uint8_t DEI5 : 1; /* DMAC Channel 5 */
|
||||
uint8_t DEI4 : 1; /* DMAC Channel 4 */
|
||||
uint8_t : 1;
|
||||
uint8_t ETMU2 : 1; /* ETMU 2 */
|
||||
uint8_t ETMU1 : 1; /* ETMU 1 */
|
||||
uint8_t _ : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR6,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t ETUNI4 : 1; /* Extra Timer 4 */
|
||||
uint8_t ETUNI0 : 1; /* Extra Timer 0 */
|
||||
uint8_t : 1;
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR7,
|
||||
uint8_t DTE0I : 1; /* I2C0 Transmit */
|
||||
uint8_t WAITT0I : 1; /* I2C0 Wait */
|
||||
uint8_t TACK0I : 1; /* I2C0 Non-ACK */
|
||||
uint8_t AL0I : 1; /* I2C0 Arb. lost */
|
||||
uint8_t TEINTE : 1; /* FCNTL Transfer end (SH7780)*/
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
uint8_t _2 : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR8,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t ETUNI5 : 1; /* Extra Timer 5 */
|
||||
uint8_t FSI : 1; /* Fifo Serial Interface */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR9,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t CMT : 1; /* CMT */
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t USI : 1; /* USB */
|
||||
uint8_t : 1;
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR10,
|
||||
uint8_t const : 1;
|
||||
uint8_t const : 1;
|
||||
uint8_t SDC : 1; /* SD Card */
|
||||
uint8_t _ : 1; /* Unknown (TODO) */
|
||||
uint8_t : 1;
|
||||
uint8_t ATI : 1; /* RTC alarm */
|
||||
uint8_t PRI : 1; /* RTC periodic interrupt */
|
||||
uint8_t CUI : 1; /* RTC carry */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR11,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _ : 1; /* Unknown (TODO) */
|
||||
);
|
||||
pad(3);
|
||||
|
||||
byte_union(IMR12,
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t _0 : 1; /* Unknown (TODO) */
|
||||
uint8_t _1 : 1; /* Unknown (TODO) */
|
||||
uint8_t _2 : 1; /* Unknown (TODO) */
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t : 1;
|
||||
);
|
||||
} VPACKED(4) sh7305_intc_masks_t;
|
||||
|
||||
|
||||
|
||||
|
||||
/* sh7305_intc_userimask_t - User Interrupt Mask
|
||||
Sets the minimum required level for interrupts to be accepted.
|
||||
|
||||
WARNING: Writing to this register is only allowed when the upper bits of the
|
||||
operand (ie. the new value of USERIMASK) are 0xa5; otherwise, the write is
|
||||
ignored. To modify the value of this register, do not access the bit field
|
||||
directly, backup the variable and modify it:
|
||||
|
||||
void set_user_imask(int new_level)
|
||||
{
|
||||
sh7305_intc_userimask_t mask = *(INTC._7305.USERIMASK);
|
||||
mask._0xa5 = 0xa5;
|
||||
mask.UIMASK = new_level & 0x0f;
|
||||
*(INTC._7305.USERIMASK) = mask;
|
||||
}
|
||||
*/
|
||||
typedef volatile lword_union(sh7305_intc_userimask_t,
|
||||
uint32_t _0xa5 :8; /* Always set to 0xa5 before writing */
|
||||
uint32_t :16;
|
||||
uint32_t UIMASK :4; /* User Interrupt Mask Level */
|
||||
uint32_t :4;
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
/* sh7305_icr0_t - Interrupt Control register 0
|
||||
Configure the input signal detection mode. Note that this register differ a
|
||||
bit from the SH7724 because the LVLMODE bit which allow us to select the
|
||||
interrupt source retention seems removed in the SH7305 (emulator) */
|
||||
typedef volatile word_union(sh7305_intc_icr0_t,
|
||||
uint16_t NMIL : 1; /* NMI Input level */
|
||||
uint16_t MAI : 1; /* NMI Interrupt Mask */
|
||||
uint16_t : 4;
|
||||
uint16_t NMIB : 1; /* NMI Block Mode */
|
||||
uint16_t NMIE : 1; /* NMI Edge Select */
|
||||
uint16_t _HIGH : 2; /* should be 1 */
|
||||
uint16_t : 6;
|
||||
);
|
||||
|
||||
/* sh7305_icr1_t - Interrupt Control register 1
|
||||
Configure the detection mode for the external interrupt pins IRQ0 to IRQ3.
|
||||
Note that the register differ from the SH7724 because we don't have the IRQ4,
|
||||
IRQ5, IRQ6 and IRQ7. */
|
||||
typedef volatile word_union(sh7305_intc_icr1_t,
|
||||
uint16_t IRQ0S : 2; /* IRQ0 Sense select */
|
||||
uint16_t IRQ1S : 2; /* IRQ1 Sense select */
|
||||
uint16_t IRQ2S : 2; /* IRQ2 Sense select */
|
||||
uint16_t IRQ3S : 2; /* IRQ3 Sense select */
|
||||
uint16_t : 8;
|
||||
);
|
||||
|
||||
/* sh7305_intpri00_t - Interrupt Priority register 0
|
||||
Configure the interrupt level for the signal from pins IRQ0 to IRQ3. Note
|
||||
that the register differ from the SH7724 because we don't have IRQ4 to IQ7.*/
|
||||
typedef volatile lword_union(sh7305_intc_intpri00_t,
|
||||
uint32_t IRQ0 : 4; /* IRQ0 priority level */
|
||||
uint32_t IRQ1 : 4; /* IRQ0 priority level */
|
||||
uint32_t IRQ2 : 4; /* IRQ0 priority level */
|
||||
uint32_t IRQ3 : 4; /* IRQ0 priority level */
|
||||
uint32_t : 16;
|
||||
);
|
||||
|
||||
/* sh7305_intc_intreq00_t - Interrupt request 0 */
|
||||
typedef volatile byte_union(sh7305_intc_intreq00_t,
|
||||
uint8_t IRQ0 : 1; /* IRQ0 Interrupt Request */
|
||||
uint8_t IRQ1 : 1; /* IRQ0 Interrupt Request */
|
||||
uint8_t IRQ2 : 1; /* IRQ0 Interrupt Request */
|
||||
uint8_t IRQ3 : 1; /* IRQ0 Interrupt Request */
|
||||
uint8_t : 4;
|
||||
);
|
||||
|
||||
/* sh7305_intc_intmsk00_t - Interrupt mask 0 */
|
||||
typedef volatile byte_union(sh7305_intc_intmsk00_t,
|
||||
uint8_t IRQ0 : 1; /* IRQ0 Interrupt Mask */
|
||||
uint8_t IRQ1 : 1; /* IRQ0 Interrupt Mask */
|
||||
uint8_t IRQ2 : 1; /* IRQ0 Interrupt Mask */
|
||||
uint8_t IRQ3 : 1; /* IRQ0 Interrupt Mask */
|
||||
uint8_t : 4;
|
||||
);
|
||||
|
||||
/* sh7305_intc_intmskclr00_t - Interrupt mask clear 0 */
|
||||
typedef volatile byte_union(sh7305_intc_intmskclr00_t,
|
||||
uint8_t IRQ0 : 1; /* IRQ0 Interrupt Mask Clear */
|
||||
uint8_t IRQ1 : 1; /* IRQ0 Interrupt Mask Clear */
|
||||
uint8_t IRQ2 : 1; /* IRQ0 Interrupt Mask Clear */
|
||||
uint8_t IRQ3 : 1; /* IRQ0 Interrupt Mask Clear */
|
||||
uint8_t : 4;
|
||||
);
|
||||
|
||||
/* sh7305_intc_nmifcr_t - NMI Flags control register
|
||||
This register contains the NMI interrupt flags that can be cleared. Note that
|
||||
the emultor set the NMIFL bit in NMIL. */
|
||||
typedef volatile word_union(sh7305_intc_nmifcr_t,
|
||||
uint16_t NMIL : 1; /* NMI Input Level */
|
||||
uint16_t : 14;
|
||||
uint16_t NMIFL : 1; /* NMI Interrupt Request */
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* sh7305_intc - the SH7305 interrupt controller */
|
||||
struct sh7305_intc
|
||||
{
|
||||
/* Interrupt control register */
|
||||
sh7305_intc_icr0_t *ICR0;
|
||||
|
||||
/* external IRQ pins configuration */
|
||||
sh7305_intc_icr1_t *ICR1;
|
||||
sh7305_intc_intpri00_t *INTPRI00;
|
||||
sh7305_intc_intreq00_t *INTREQ00;
|
||||
sh7305_intc_intmsk00_t *INTMSK00;
|
||||
sh7305_intc_intmskclr00_t *INTMSKCLR00;
|
||||
|
||||
/* NMI configuration / flags */
|
||||
sh7305_intc_nmifcr_t *NMIFCR;
|
||||
|
||||
/* Interrupt priority registers */
|
||||
union {
|
||||
sh7305_intc_ipc_t *_;
|
||||
volatile uint16_t *IPR;
|
||||
};
|
||||
|
||||
/* Interrupt mask & mask clear registers */
|
||||
union {
|
||||
sh7305_intc_masks_t *_MSK;
|
||||
uint8_t *MSK;
|
||||
};
|
||||
union {
|
||||
sh7305_intc_masks_t *_MSKCLR;
|
||||
uint8_t *MSKCLR;
|
||||
};
|
||||
|
||||
/* Other registers */
|
||||
sh7305_intc_userimask_t *USERIMASK;
|
||||
|
||||
} VPACKED(4);
|
||||
|
||||
/* Provided by intc/intc.c */
|
||||
extern struct sh7305_intc SH7305_INTC;
|
||||
|
||||
#endif /* __VHEX_ARCH_SH7305_INTC__ */
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef __VHEX_DRIVER__
|
||||
# define __VHEX_DRIVER__
|
||||
|
||||
#include <vhex/defs/types.h>
|
||||
|
||||
/* Device drivers */
|
||||
struct vhex_driver
|
||||
{
|
||||
/* Driver name */
|
||||
char const *name;
|
||||
|
||||
// "Hypervisor" calls
|
||||
|
||||
/* Determine whether the device is powered. If NULL, the device is
|
||||
assumed to be permanently powered. */
|
||||
bool (*hpowered)(void);
|
||||
/* Power on the device; this should allow register access to save the
|
||||
peripheral state, with minimal state changes. Cannot be NULL if
|
||||
hpowered() can return false. */
|
||||
void (*hpoweron)(void);
|
||||
/* Power off the device; Cannot be NULL if hpowered() can return
|
||||
false. */
|
||||
void (*hpoweroff)(void);
|
||||
|
||||
/* Save the hardware state; the (state) pointer points to a 4-aligned
|
||||
region of (state_size) bytes. */
|
||||
void (*hsave)(void *state);
|
||||
/* Restore a hardware state previously saved by hsave(). */
|
||||
void (*hrestore)(void const *state);
|
||||
|
||||
// Standard calls
|
||||
|
||||
/* Initialize the hardware for the driver to work in vhex. Usually
|
||||
installs interrupt handlers and configures registers. May be NULL. */
|
||||
void (*configure)(void *state);
|
||||
|
||||
// Internal information
|
||||
|
||||
/* Size of the peripheral's hardware state (assumed 4-aligned) */
|
||||
uint16_t state_size;
|
||||
|
||||
/* default flags for the driver */
|
||||
int default_flag;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Driver is used by default (will be configured) */
|
||||
VHEX_DRV_USED = 0x00,
|
||||
|
||||
/* Driver is not used by the world */
|
||||
VHEX_DRV_UNUSED = 0x01,
|
||||
|
||||
/* Driver does not require hardware state saves during world switches */
|
||||
VHEX_DRV_SHARED = 0x02,
|
||||
};
|
||||
|
||||
|
||||
/* VHEX_DECLARE_DRIVER(): Declare a driver to the kernel
|
||||
|
||||
Use this macro to declare a driver by passing it the name of a
|
||||
vhex_world_core structure. This macro moves the structure to the
|
||||
.vhex.drivers.* sections, which are automatically traversed at startup.
|
||||
|
||||
The level argument represents the priority level: lower numbers mean that
|
||||
drivers will be loaded sooner. This numbering allows a primitive form of
|
||||
dependency for drivers. You need to specify a level which is strictly
|
||||
higher than the level of all the drivers you depend on.
|
||||
|
||||
The level number *MUST HAVE EXACTLY 2 DIGITS*, as it is used as a string in
|
||||
the section name and the linker then sorts by name. If your driver has a
|
||||
level lower than 10, you must add a leading 0. */
|
||||
#define VHEX_DECLARE_DRIVER(level, name) \
|
||||
VSECTION(".vhex.drivers." #level) extern struct vhex_driver name;
|
||||
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Internal driver control
|
||||
//
|
||||
// The following data is exposed for introspection and debugging purposes; it
|
||||
// is not part of the vhex API. There is *no stability guarantee* that the
|
||||
// following types and functions will remain unchanged in future minor and
|
||||
// patch versions.
|
||||
//---
|
||||
|
||||
/* Number of drivers in the (vhex_drivers) array */
|
||||
#define vhex_driver_count() \
|
||||
((struct vhex_driver *)&vhex_drivers_end \
|
||||
- (struct vhex_driver *)&vhex_drivers_start)
|
||||
|
||||
/* driver table */
|
||||
#define vhex_driver_table() \
|
||||
((struct vhex_driver *)&vhex_drivers_start)
|
||||
|
||||
/* provided by the linker script */
|
||||
extern uintptr_t vhex_drivers_start;
|
||||
extern uintptr_t vhex_drivers_end;
|
||||
|
||||
#endif /* __VHEX_DRIVER__ */
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __VHEX_DRIVERS_CPU__
|
||||
# define __VHEX_DRIVERS_CPU__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* cpu_atomic_start(): Enter atomic mode
|
||||
|
||||
This function enters "atomic mode", a mode where distractions to the CPU
|
||||
(mainly interrupts) are disabled. This is useful when doing critical
|
||||
operations on the hardware, because it ensures that no other code will see
|
||||
any intermediate state between the start and end of the atomic mode, thereby
|
||||
making the sequence atomic to other code.
|
||||
|
||||
Atomic mode disables interrupts with IMASK=15, however it does not set BL=1
|
||||
because exceptions never occur on their own (and it is desirable to have
|
||||
panic reports if the atomic code is buggy), and TLB misses are almost always
|
||||
desirable. If you want to set BL=1, you can do so with cpu_setSR().
|
||||
|
||||
This function uses a mutex so atomic mode can be started within atomic code;
|
||||
every cpu_atomic_start() must be paired with exactly one cpu_atomic_end().
|
||||
Entering atomic mode several times does not affect the CPU state, however
|
||||
atomic mode will be exited only after all exits have been completed.
|
||||
|
||||
Once atomic mod is exited the original value of IMASK at the first call to
|
||||
cpu_atomic_start() is restored. */
|
||||
extern void cpu_atomic_start(void);
|
||||
|
||||
/* cpu_atomic_end(): Exit atomic mode
|
||||
There should be exactly one cpu_atomic_end() for each cpu_atomic_start(). */
|
||||
extern void cpu_atomic_end(void);
|
||||
|
||||
#endif /* __VHEX_DRIVERS_CPU__ */
|
|
@ -0,0 +1,94 @@
|
|||
#ifndef __VHEX_HYPERVISOR__
|
||||
# define __VHEX_HYPERVISOR__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* declare driver structure */
|
||||
#include <vhex/driver.h>
|
||||
|
||||
/* real Vhex world with driver bitmap and meta information for the hypervisor */
|
||||
struct vhex_world {
|
||||
/* world driver information */
|
||||
struct {
|
||||
struct vhex_driver *context;
|
||||
int flags;
|
||||
} *driver;
|
||||
|
||||
/* internal information used by the hypervisor */
|
||||
char const * name;
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
/* define world status states */
|
||||
#define HYP_WORLD_STATUS_STATE_UNUSED (1 << 0)
|
||||
#define HYP_WORLD_STATUS_STATE_UNINIT (1 << 1)
|
||||
#define HYP_WORLD_STATUS_STATE_USED (1 << 2)
|
||||
|
||||
/* define world status flags */
|
||||
#define HYP_WORLD_STATUS_FLAG_UNDELETABLE (1 << 0)
|
||||
|
||||
/* hypervisor world status helper */
|
||||
|
||||
#define HYP_WORLD_STATUS_SET(flags,state) \
|
||||
(((flags & 0x0f) << 4) | ((state & 0x0f) << 0))
|
||||
|
||||
#define HYP_WORLD_STATUS_SET_STATE(status, state) \
|
||||
do { \
|
||||
status &= 0x0f; \
|
||||
status |= (state & 0x0f) << 0; \
|
||||
} while (0);
|
||||
|
||||
#define HYP_WORLD_STATUS_SET_FLAGS(status, flags) \
|
||||
do { \
|
||||
status &= 0xf0; \
|
||||
status |= (state & 0x0f) << 4; \
|
||||
} while (0);
|
||||
|
||||
/* hypervisor world status getter */
|
||||
|
||||
#define HYP_WORLD_STATUS_FLAGS(status) ((status & 0xf0) >> 4)
|
||||
#define HYP_WORLD_STATUS_STATE(status) ((status & 0x0f) >> 0)
|
||||
|
||||
|
||||
/* hypervisor driver status */
|
||||
enum {
|
||||
HYP_WORLD_DRV_UNUSED = VHEX_DRV_UNUSED,
|
||||
HYP_WORLD_DRV_USED = VHEX_DRV_USED,
|
||||
};
|
||||
|
||||
/* hyp_word_t : hypervisor world ID */
|
||||
typedef int hyp_world_t;
|
||||
|
||||
//---
|
||||
// User-level API
|
||||
//---
|
||||
|
||||
/* possible returned value */
|
||||
enum {
|
||||
hyp_world_already_exists = -1,
|
||||
hyp_world_not_found = -2,
|
||||
hyp_world_bad_id = -3,
|
||||
hyp_world_currently_in_use = -4,
|
||||
hyp_world_undeletable = -5,
|
||||
};
|
||||
|
||||
/* hypervisor_world_new() : create a new world with its name */
|
||||
extern hyp_world_t hypervisor_world_new(
|
||||
char const * const restrict name,
|
||||
int flags
|
||||
);
|
||||
|
||||
/* hypervisor_world_find() : find a world ID using its name */
|
||||
extern hyp_world_t hypervisor_world_find(char const * const restrict name);
|
||||
|
||||
/* hypervisor_world_get() : get a word using its ID */
|
||||
extern struct vhex_world *hypervisor_world_get(hyp_world_t id);
|
||||
|
||||
/* hypervisor_world_switch() : perform a world switch */
|
||||
extern int hypervisor_world_switch(hyp_world_t out, hyp_world_t in);
|
||||
|
||||
/* hypervisor_world_delete() : remove world */
|
||||
extern hyp_world_t hypervisor_world_delete(hyp_world_t id);
|
||||
|
||||
#endif /* __VHEX_HYPERVISOR__ */
|
|
@ -0,0 +1,37 @@
|
|||
#include <vhex/drivers/cpu.h>
|
||||
#include <vhex/arch/sh7305/cpu.h>
|
||||
|
||||
/* Value of IMASK when atomic mode is entered */
|
||||
static int saved_IMASK = 0;
|
||||
/* Number of atomic mode levels */
|
||||
static unsigned int atomic_level = 0;
|
||||
|
||||
/* cpu_atomic_start(): Enter atomic mode */
|
||||
void cpu_atomic_start(void)
|
||||
{
|
||||
/* There is no access problem to IMASK here because interrupts must
|
||||
preserve and restore it */
|
||||
cpu_sr_t SR = cpu_get_sr();
|
||||
cpu_sr_t SR2 = SR;
|
||||
SR2.IMASK = 15;
|
||||
cpu_set_sr(SR2);
|
||||
|
||||
/* Now that we're alone, atomically update the atomic level */
|
||||
if(atomic_level == 0) saved_IMASK = SR.IMASK;
|
||||
atomic_level++;
|
||||
}
|
||||
|
||||
/* cpu_atomic_end(): Exit atomic mode */
|
||||
void cpu_atomic_end(void)
|
||||
{
|
||||
cpu_sr_t SR = cpu_get_sr();
|
||||
|
||||
/* Update atomic_level before restoring interrupts */
|
||||
atomic_level--;
|
||||
if(atomic_level == 0) {
|
||||
SR.IMASK = saved_IMASK;
|
||||
saved_IMASK = 0;
|
||||
}
|
||||
|
||||
cpu_set_sr(SR);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
#include "vhex/driver.h"
|
||||
#include "vhex/arch/sh7305/cpu.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* define the private CPU context structure */
|
||||
struct cpu_ctx {
|
||||
uintptr_t vbr;
|
||||
cpu_cpuopm_t cpuopm;
|
||||
cpu_sr_t sr;
|
||||
};
|
||||
|
||||
/* hardware configuration call */
|
||||
|
||||
/* __cpu_configure() : configure the CPU */
|
||||
static void __cpu_configure(struct cpu_ctx *state)
|
||||
{
|
||||
extern uintptr_t vhex_vbr;
|
||||
|
||||
memset(state, 0x00, sizeof(struct cpu_ctx));
|
||||
|
||||
/* by default, use the vhex VBR */
|
||||
state->vbr = (uintptr_t)&vhex_vbr;
|
||||
|
||||
/* configure the CPU Operation Mode
|
||||
<> enable speculative execution (rip Spectre)
|
||||
<> enable SR.IMASK update when an interruption occur */
|
||||
state->cpuopm.RABD = 0;
|
||||
state->cpuopm.INTMU = 1;
|
||||
state->cpuopm._HIGH = 0b1111;
|
||||
|
||||
/* configure the Status Register
|
||||
<> force the privileged mode
|
||||
<> enable DSP
|
||||
<> allow interruptions */
|
||||
state->sr.MD = 1;
|
||||
state->sr.RB = 0;
|
||||
state->sr.BL = 0;
|
||||
state->sr.DSP = 1;
|
||||
state->sr.IMASK = 0b0000;
|
||||
}
|
||||
|
||||
/* hardware hypervisor call */
|
||||
|
||||
/* __cpu_hsave() : save hardware information */
|
||||
static void __cpu_hsave(struct cpu_ctx *state)
|
||||
{
|
||||
state->vbr = cpu_get_vbr();
|
||||
state->cpuopm = cpu_get_cpuopm();
|
||||
state->sr = cpu_get_sr();
|
||||
}
|
||||
|
||||
/* __cpu_hrestore() : restore hardware information */
|
||||
static void __cpu_hrestore(struct cpu_ctx *state)
|
||||
{
|
||||
cpu_set_vbr(state->vbr);
|
||||
cpu_set_cpuopm(state->cpuopm);
|
||||
cpu_set_sr(state->sr);
|
||||
}
|
||||
|
||||
/* declare the CPU driver */
|
||||
|
||||
struct vhex_driver drv_cpu = {
|
||||
.name = "CPU",
|
||||
.hsave = (void*)&__cpu_hsave,
|
||||
.hrestore = (void*)&__cpu_hrestore,
|
||||
.configure = (void*)&__cpu_configure,
|
||||
.state_size = sizeof(struct cpu_ctx)
|
||||
};
|
||||
VHEX_DECLARE_DRIVER(16, drv_cpu);
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
** Access to primary registers used in the CPU
|
||||
*/
|
||||
|
||||
.text
|
||||
.global _cpu_get_vbr
|
||||
.global _cpu_set_vbr
|
||||
.global _cpu_set_cpuopm
|
||||
.global _cpu_get_cpuopm
|
||||
.global _cpu_get_sr
|
||||
.global _cpu_set_sr
|
||||
|
||||
/* cpu_set_vbr(): Change VBR address */
|
||||
_cpu_set_vbr:
|
||||
ldc r4, vbr
|
||||
rts
|
||||
nop
|
||||
|
||||
_cpu_get_vbr:
|
||||
stc vbr, r0
|
||||
rts
|
||||
nop
|
||||
|
||||
_cpu_set_cpuopm:
|
||||
/* Set CPUOPM as requested */
|
||||
mov.l 1f, r0
|
||||
mov.l r4, @r0
|
||||
|
||||
/* Read CPUOPM again */
|
||||
mov.l @r0, r5
|
||||
|
||||
/* Invalidate a cache address */
|
||||
mov #-96, r0
|
||||
shll16 r0
|
||||
shll8 r0
|
||||
icbi @r0
|
||||
|
||||
rts
|
||||
nop
|
||||
|
||||
_cpu_get_cpuopm:
|
||||
mov.l 1f, r0
|
||||
rts
|
||||
mov.l @r0, r0
|
||||
|
||||
.align 4
|
||||
1: .long 0xff2f0000
|
||||
|
||||
_cpu_get_sr:
|
||||
stc sr, r0
|
||||
rts
|
||||
nop
|
||||
|
||||
_cpu_set_sr:
|
||||
/* Set only MD, RB, BL, DSP and IMASK */
|
||||
mov.l 1f, r0
|
||||
not r0, r1
|
||||
stc sr, r2
|
||||
|
||||
and r1, r2
|
||||
and r0, r4
|
||||
or r4, r2
|
||||
|
||||
ldc r2, sr
|
||||
rts
|
||||
nop
|
||||
|
||||
.align 4
|
||||
1: .long 0x700010f0
|
|
@ -0,0 +1,163 @@
|
|||
#include "vhex/driver.h"
|
||||
#include "vhex/arch/sh7305/intc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Construct the Interrupt Controller */
|
||||
struct sh7305_intc sh7305_INTC = {
|
||||
.ICR0 = (void *)0xa4140000,
|
||||
.ICR1 = (void *)0xa414001c,
|
||||
.INTPRI00 = (void *)0xa4140010,
|
||||
.INTREQ00 = (void *)0xa4140024,
|
||||
.INTMSK00 = (void *)0xa4140044,
|
||||
.INTMSKCLR00 = (void *)0xa4140064,
|
||||
.NMIFCR = (void *)0xa41400c0,
|
||||
|
||||
.IPR = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
.USERIMASK = (void *)0xa4700000,
|
||||
|
||||
// PINTCR = (void *)0xa40501dc,
|
||||
// PINTSR = (void *)0xa40501ea,
|
||||
};
|
||||
|
||||
|
||||
/* define the private INTerrupt Controller context structure */
|
||||
struct intc_ctx {
|
||||
uint16_t ipr[12];
|
||||
uint8_t imr[13];
|
||||
sh7305_intc_userimask_t userimask;
|
||||
sh7305_intc_icr0_t icr0;
|
||||
sh7305_intc_icr1_t icr1;
|
||||
sh7305_intc_intpri00_t intpri00;
|
||||
sh7305_intc_intreq00_t intreq00;
|
||||
sh7305_intc_intmsk00_t intmsk00;
|
||||
sh7305_intc_nmifcr_t nmifcr;
|
||||
};
|
||||
|
||||
/* hardware configuration call */
|
||||
|
||||
/* __intc_configure() : configure the Interrupt Controller
|
||||
|
||||
Note that we don't touch to the NMI configuration because this part is
|
||||
obscure. I have done some reverse-ingenering in this part, but Casio seems
|
||||
use this part oddly. So, to avoid any probleme with Non-Maskable Interrupt,
|
||||
we will use the Casio's configuration.
|
||||
|
||||
So, by default, mask all interrupt and enable all interrupt is the user
|
||||
interrupt mask */
|
||||
static void __intc_configure(struct intc_ctx *state)
|
||||
{
|
||||
memset(state, 0x00, sizeof(struct intc_ctx));
|
||||
|
||||
for (int i = 0; i < 12; ++i)
|
||||
state->ipr[i] = 0x0000;
|
||||
|
||||
for (int i = 0; i < 13; ++i)
|
||||
state->imr[i] = 0x00;
|
||||
|
||||
state->userimask._0xa5 = 0xa5;
|
||||
state->userimask.UIMASK = 0x00;
|
||||
|
||||
/* interrupt control register 0 (NMI)
|
||||
<> enable NMI interruption when a input level is low
|
||||
<> keep NMI interrupt pending while the SR.BL=1
|
||||
<> interrupt request is detected on falling edge */
|
||||
state->icr0.MAI = 0;
|
||||
state->icr0.NMIB = 0;
|
||||
state->icr0.NMIE = 0;
|
||||
state->icr0._HIGH = 0b11;
|
||||
|
||||
/* Interrupt control register 1 (IRQ)
|
||||
<> interrupt request is detected on low-level of IRQx input */
|
||||
state->icr1.IRQ0S = 0b10;
|
||||
state->icr1.IRQ1S = 0b10;
|
||||
state->icr1.IRQ2S = 0b10;
|
||||
state->icr1.IRQ3S = 0b10;
|
||||
|
||||
/* Interrupt priority (IRQ)
|
||||
<> mask all IRQx interrupt */
|
||||
state->intpri00.IRQ0 = 0b0000;
|
||||
state->intpri00.IRQ1 = 0b0000;
|
||||
state->intpri00.IRQ2 = 0b0000;
|
||||
state->intpri00.IRQ3 = 0b0000;
|
||||
|
||||
/* Interrupt request (IRQ)
|
||||
<> clear all pending interrupt request */
|
||||
state->intreq00.IRQ0 = 0;
|
||||
state->intreq00.IRQ1 = 0;
|
||||
state->intreq00.IRQ2 = 0;
|
||||
state->intreq00.IRQ3 = 0;
|
||||
|
||||
/* Interurpt mask (IRQ)
|
||||
<> mask all IRQ interrupt */
|
||||
state->intmsk00.IRQ0 = 1;
|
||||
state->intmsk00.IRQ1 = 1;
|
||||
state->intmsk00.IRQ2 = 1;
|
||||
state->intmsk00.IRQ3 = 1;
|
||||
|
||||
/* NMI Interrupt flagsi (clear) */
|
||||
state->nmifcr.NMIFL = 0;
|
||||
}
|
||||
|
||||
|
||||
/* hardware hypervisor call */
|
||||
|
||||
/* __intc_hsave() : save hardware information */
|
||||
static void __intc_hsave(struct intc_ctx *state)
|
||||
{
|
||||
for (int i = 0; i < 12; ++i)
|
||||
state->ipr[i] = sh7305_INTC.IPR[i << 1];
|
||||
|
||||
for (int i = 0; i < 13; ++i)
|
||||
state->imr[i] = sh7305_INTC.MSK[i << 2];
|
||||
|
||||
state->userimask = *sh7305_INTC.USERIMASK;
|
||||
|
||||
state->icr0 = *sh7305_INTC.ICR0;
|
||||
state->icr1 = *sh7305_INTC.ICR1;
|
||||
state->intpri00 = *sh7305_INTC.INTPRI00;
|
||||
state->intreq00 = *sh7305_INTC.INTREQ00;
|
||||
state->intmsk00 = *sh7305_INTC.INTMSK00;
|
||||
}
|
||||
|
||||
/* __intc_hrestore() : restore hardware information */
|
||||
static void __intc_hrestore(struct intc_ctx *state)
|
||||
{
|
||||
for (int i = 0; i < 12; ++i)
|
||||
sh7305_INTC.IPR[i << 1] = state->ipr[i];
|
||||
|
||||
for (int i = 0; i < 13; ++i) {
|
||||
sh7305_INTC.MSKCLR[i << 2] = 0xff;
|
||||
sh7305_INTC.MSK[i << 2] = state->imr[i];
|
||||
}
|
||||
|
||||
state->userimask._0xa5 = 0xa5;
|
||||
*sh7305_INTC.USERIMASK = state->userimask;
|
||||
|
||||
*sh7305_INTC.ICR0 = state->icr0;
|
||||
*sh7305_INTC.ICR1 = state->icr1;
|
||||
*sh7305_INTC.INTPRI00 = state->intpri00;
|
||||
*sh7305_INTC.INTREQ00 = state->intreq00;
|
||||
*sh7305_INTC.INTMSKCLR00 = (sh7305_intc_intmskclr00_t){
|
||||
.IRQ0 = 1,
|
||||
.IRQ1 = 1,
|
||||
.IRQ2 = 1,
|
||||
.IRQ3 = 1,
|
||||
};
|
||||
*sh7305_INTC.INTMSK00 = state->intmsk00;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* declare the INTC driver */
|
||||
|
||||
struct vhex_driver drv_intc = {
|
||||
.name = "INTC",
|
||||
.hsave = (void*)&__intc_hsave,
|
||||
.hrestore = (void*)&__intc_hrestore,
|
||||
.configure = (void*)&__intc_configure,
|
||||
.state_size = sizeof(struct intc_ctx)
|
||||
};
|
||||
VHEX_DECLARE_DRIVER(01, drv_intc);
|
|
@ -1,8 +1,8 @@
|
|||
//---
|
||||
// vhex:mmu:mmu - MMU driver definition and context management
|
||||
//---
|
||||
#include <vhex/mmu.h>
|
||||
#include <vhex/mpu/sh7305/mmu.h>
|
||||
#include <vhex/drivers/mmu.h>
|
||||
#include <vhex/arch/sh7305/mmu.h>
|
||||
#include <vhex/defs/attributes.h>
|
||||
|
||||
/* utlb_addr() - get the P4 address of a UTLB address entry */
|
|
@ -0,0 +1,15 @@
|
|||
#if 0
|
||||
|
||||
|
||||
|
||||
/* declare the CPU driver */
|
||||
|
||||
struct vhex_driver drv_r61524 = {
|
||||
.name = "R61524",
|
||||
.hsave = (void*)&__r61524_hsave,
|
||||
.hrestore = (void*)&__r61524_hrestore,
|
||||
.configure = (void*)&__r61524_configure,
|
||||
.state_size = sizeof(struct r61524_ctx)
|
||||
};
|
||||
VHEX_DECLARE_DRIVER(16, drv_r61524);
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
#include <vhex/hypervisor.h>
|
||||
#include <vhex/drivers/cpu.h>
|
||||
|
||||
#include <vhex/display.h>
|
||||
|
||||
/* hypervisor_world_switch() : perform a world switch */
|
||||
//TODO: handle shared drivers
|
||||
int hypervisor_world_switch(hyp_world_t out, hyp_world_t in)
|
||||
{
|
||||
struct vhex_world *win;
|
||||
struct vhex_world *wout;
|
||||
struct vhex_driver *dtable;
|
||||
|
||||
|
||||
wout = hypervisor_world_get(out);
|
||||
win = hypervisor_world_get(in);
|
||||
|
||||
#if 0
|
||||
dclear(0xffff);
|
||||
dprint(0, 0, C_BLACK, "je suis dans le worlds-sitch !");
|
||||
dprint(0, 11, C_BLACK, "> out = %d (%p)", out, wout);
|
||||
dprint(0, 22, C_BLACK, "> in = %d (%p)", in, win);
|
||||
dprint(0, 33, C_BLACK, "> nb vhex drivers = %d", vhex_driver_count());
|
||||
dupdate();
|
||||
while (1) { __asm__ volatile ("sleep"); }
|
||||
#endif
|
||||
|
||||
if (wout == NULL || win == NULL)
|
||||
return (hyp_world_not_found);
|
||||
if (in == out)
|
||||
return (0);
|
||||
|
||||
|
||||
cpu_atomic_start();
|
||||
dtable = vhex_driver_table();
|
||||
for (int i = 0; i < vhex_driver_count(); ++i) {
|
||||
if (dtable[i].hpowered != NULL
|
||||
&& dtable[i].hpowered() == false) {
|
||||
wout->driver[i].flags = HYP_WORLD_DRV_UNUSED;
|
||||
continue;
|
||||
}
|
||||
dtable[i].hsave(wout->driver[i].context);
|
||||
wout->driver[i].flags = HYP_WORLD_DRV_USED;
|
||||
}
|
||||
for (int i = 0; i < vhex_driver_count(); ++i) {
|
||||
if (HYP_WORLD_STATUS_STATE(win->status)
|
||||
== HYP_WORLD_STATUS_STATE_UNINIT) {
|
||||
if (dtable[i].configure != NULL)
|
||||
dtable[i].configure(win->driver[i].context);
|
||||
win->driver[i].flags = dtable[i].default_flag;
|
||||
}
|
||||
if (win->driver[i].flags == HYP_WORLD_DRV_UNUSED) {
|
||||
if (dtable[i].hpowered() == true)
|
||||
dtable[i].hpoweroff();
|
||||
continue;
|
||||
}
|
||||
if (dtable[i].hpowered != NULL && dtable[i].hpowered() == false)
|
||||
dtable[i].hpoweron();
|
||||
dtable[i].hrestore(win->driver[i].context);
|
||||
}
|
||||
HYP_WORLD_STATUS_SET_STATE(win->status, HYP_WORLD_STATUS_STATE_USED);
|
||||
HYP_WORLD_STATUS_SET_STATE(wout->status, HYP_WORLD_STATUS_STATE_USED);
|
||||
cpu_atomic_end();
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
//---
|
||||
// hypervisor:table - world allocator
|
||||
//---
|
||||
#include <vhex/hypervisor.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* internal world table */
|
||||
static struct {
|
||||
struct vhex_world *table;
|
||||
size_t table_slot;
|
||||
} hyp = {
|
||||
.table = NULL,
|
||||
.table_slot = 0
|
||||
};
|
||||
|
||||
/* hypervisor_world_new() : create a new world with its name */
|
||||
hyp_world_t hypervisor_world_new(char const * const restrict name, int flags)
|
||||
{
|
||||
struct vhex_driver *dtable;
|
||||
hyp_world_t id;
|
||||
|
||||
/* try to find free world */
|
||||
id = 0xb0cad0;
|
||||
if (hyp.table != NULL) {
|
||||
for (size_t i = 0; i < hyp.table_slot; ++i) {
|
||||
if (HYP_WORLD_STATUS_STATE(hyp.table[i].status)
|
||||
== HYP_WORLD_STATUS_STATE_UNUSED) {
|
||||
id = i;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(hyp.table[i].name, name) == 0)
|
||||
return (hyp_world_already_exists);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* if no world is found, update the table */
|
||||
if (id == 0xb0cad0) {
|
||||
id = hyp.table_slot;
|
||||
|
||||
if (hyp.table_slot == 0)
|
||||
hyp.table_slot = 1;
|
||||
|
||||
hyp.table = reallocarray(
|
||||
hyp.table,
|
||||
hyp.table_slot * 2,
|
||||
sizeof(struct vhex_world)
|
||||
);
|
||||
for (size_t i = hyp.table_slot; i < hyp.table_slot * 2; ++i) {
|
||||
hyp.table[i].status = HYP_WORLD_STATUS_STATE_UNUSED;
|
||||
hyp.table[i].driver = NULL;
|
||||
hyp.table[i].name = NULL;
|
||||
}
|
||||
|
||||
hyp.table_slot = hyp.table_slot * 2;
|
||||
}
|
||||
|
||||
/* initialize the world */
|
||||
hyp.table[id].name = strdup(name);
|
||||
hyp.table[id].status = HYP_WORLD_STATUS_SET(
|
||||
flags,
|
||||
HYP_WORLD_STATUS_STATE_UNINIT
|
||||
);
|
||||
if (hyp.table[id].driver == NULL) {
|
||||
hyp.table[id].driver = malloc(
|
||||
vhex_driver_count() * sizeof(*hyp.table[id].driver)
|
||||
);
|
||||
dtable = vhex_driver_table();
|
||||
for (int i = 0; i < vhex_driver_count(); ++i) {
|
||||
hyp.table[id].driver[i].context = malloc(
|
||||
dtable[i].state_size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (id);
|
||||
}
|
||||
|
||||
|
||||
/* hypervisor_world_find() : find a world using its name */
|
||||
hyp_world_t hypervisor_world_find(char const * const restrict name)
|
||||
{
|
||||
if (hyp.table != NULL) {
|
||||
for (size_t i = 0; i < hyp.table_slot; ++i) {
|
||||
if (strcmp(hyp.table[i].name, name) == 0)
|
||||
return (i);
|
||||
}
|
||||
}
|
||||
return (hyp_world_not_found);
|
||||
}
|
||||
|
||||
/* hypervisor_world_get() : get a word using its ID */
|
||||
struct vhex_world *hypervisor_world_get(hyp_world_t id)
|
||||
{
|
||||
if (id < 0 || (size_t)id >= hyp.table_slot)
|
||||
return (NULL);
|
||||
if (HYP_WORLD_STATUS_STATE(hyp.table[id].status)
|
||||
== HYP_WORLD_STATUS_STATE_UNUSED) {
|
||||
return (NULL);
|
||||
}
|
||||
return (&hyp.table[id]);
|
||||
}
|
||||
|
||||
|
||||
/* hypervisor_world_delete() : remove world */
|
||||
hyp_world_t hypervisor_world_delete(hyp_world_t id)
|
||||
{
|
||||
if (id < 0 || (size_t)id >= hyp.table_slot)
|
||||
return (hyp_world_bad_id);
|
||||
|
||||
if (HYP_WORLD_STATUS_FLAGS(hyp.table[id].status)
|
||||
== HYP_WORLD_STATUS_FLAG_UNDELETABLE) {
|
||||
return (hyp_world_undeletable);
|
||||
}
|
||||
|
||||
hyp.table[id].status = HYP_WORLD_STATUS_STATE_UNUSED;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include <vhex/kernel.h>
|
||||
#include <vhex/hypervisor.h>
|
||||
|
||||
//---
|
||||
// Initialization and unloading
|
||||
|
@ -7,13 +8,26 @@
|
|||
/* kinit(): Install and start vhex */
|
||||
void kinit(void)
|
||||
{
|
||||
//TODO: initialize hypervisor
|
||||
//TODO: perform a world-switch "in"
|
||||
hyp_world_t casio = hypervisor_world_new(
|
||||
"casio",
|
||||
HYP_WORLD_STATUS_FLAG_UNDELETABLE
|
||||
);
|
||||
hyp_world_t vhex = hypervisor_world_new(
|
||||
"vhex",
|
||||
HYP_WORLD_STATUS_FLAG_UNDELETABLE
|
||||
);
|
||||
|
||||
hypervisor_world_switch(casio, vhex);
|
||||
}
|
||||
|
||||
/* kquit(): Quit vhex and give back control to the system */
|
||||
void kquit(void)
|
||||
{
|
||||
//TODO: perform a world-switch "out"
|
||||
//TODO: free'd allocated world information
|
||||
hyp_world_t casio = hypervisor_world_find("casio");
|
||||
hyp_world_t vhex = hypervisor_world_find("vhex");
|
||||
|
||||
hypervisor_world_switch(vhex, casio);
|
||||
|
||||
hypervisor_world_delete(casio);
|
||||
hypervisor_world_delete(vhex);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
/* Vhex vram */
|
||||
extern uint16_t vhex_vram[];
|
||||
|
||||
/* gint_dhline(): Optimized horizontal line */
|
||||
/* dhline(): Optimized horizontal line */
|
||||
void dhline(int x1, int x2, int y, int color)
|
||||
{
|
||||
/* Order and bounds */
|
||||
|
@ -34,7 +34,7 @@ void dhline(int x1, int x2, int y, int color)
|
|||
while(end > start) *--end = op;
|
||||
}
|
||||
|
||||
/* gint_dvline(): Optimized vertical line */
|
||||
/* dvline(): Optimized vertical line */
|
||||
void dvline(int y1, int y2, int x, int color)
|
||||
{
|
||||
/* Order and bounds */
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = 'vxkernel'
|
||||
version = '0.3.0'
|
||||
|
||||
[build]
|
||||
configure = 'mkdir -p build && cd build && ../config --board=fxcg50 --format=static --verbose'
|
||||
build = 'cd build && make'
|
||||
install = 'cd build && make install'
|
||||
uninstall = 'cd build && make uninstall'
|
Loading…
Reference in New Issue