From df8d26ecca2b575949a3376b7d758c42095c51ba Mon Sep 17 00:00:00 2001 From: Yann MAGNIN Date: Sun, 13 Feb 2022 15:01:01 +0100 Subject: [PATCH] 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 --- board/fxcg50/fxcg50.ld | 84 +++- board/fxcg50/hardware.c | 6 +- .../vhex/arch => board}/fxcg50/hardware.h | 0 board/fxcg50/initialize.c | 14 +- config | 6 +- include/vhex/arch/sh7305/cpu.h | 75 ++++ include/vhex/arch/sh7305/intc.h | 413 ++++++++++++++++++ include/vhex/{mpu => arch}/sh7305/mmu.h | 0 include/vhex/{mpu => arch}/sh7305/pfc.h | 0 include/vhex/driver.h | 100 +++++ include/vhex/drivers/cpu.h | 33 ++ include/vhex/{ => drivers}/mmu.h | 0 include/vhex/hypervisor.h | 94 ++++ src/drivers/mpu/sh/sh7305/cpu/atomic.c | 37 ++ src/drivers/mpu/sh/sh7305/cpu/cpu.c | 70 +++ src/drivers/mpu/sh/sh7305/cpu/registers.s | 69 +++ src/drivers/mpu/sh/sh7305/intc/intc.c | 163 +++++++ .../mpu/sh/sh7305/{mmu.c => mmu/utlb.c} | 4 +- src/drivers/screen/R61524/r61524.c | 15 + src/hypervisor/switch.c | 64 +++ src/hypervisor/table.c | 119 +++++ src/kernel/kernel.c | 22 +- src/render/dhline.c | 4 +- vxsdk.toml | 9 + 24 files changed, 1376 insertions(+), 25 deletions(-) rename {include/vhex/arch => board}/fxcg50/hardware.h (100%) create mode 100644 include/vhex/arch/sh7305/cpu.h create mode 100644 include/vhex/arch/sh7305/intc.h rename include/vhex/{mpu => arch}/sh7305/mmu.h (100%) rename include/vhex/{mpu => arch}/sh7305/pfc.h (100%) create mode 100644 include/vhex/driver.h create mode 100644 include/vhex/drivers/cpu.h rename include/vhex/{ => drivers}/mmu.h (100%) create mode 100644 include/vhex/hypervisor.h create mode 100644 src/drivers/mpu/sh/sh7305/cpu/atomic.c create mode 100644 src/drivers/mpu/sh/sh7305/cpu/cpu.c create mode 100644 src/drivers/mpu/sh/sh7305/cpu/registers.s create mode 100644 src/drivers/mpu/sh/sh7305/intc/intc.c rename src/drivers/mpu/sh/sh7305/{mmu.c => mmu/utlb.c} (95%) create mode 100644 src/drivers/screen/R61524/r61524.c create mode 100644 src/hypervisor/switch.c create mode 100644 src/hypervisor/table.c create mode 100644 vxsdk.toml diff --git a/board/fxcg50/fxcg50.ld b/board/fxcg50/fxcg50.ld index cf81191..554b104 100644 --- a/board/fxcg50/fxcg50.ld +++ b/board/fxcg50/fxcg50.ld @@ -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/ : { diff --git a/board/fxcg50/hardware.c b/board/fxcg50/hardware.c index c7a2efe..735a289 100644 --- a/board/fxcg50/hardware.c +++ b/board/fxcg50/hardware.c @@ -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 */ diff --git a/include/vhex/arch/fxcg50/hardware.h b/board/fxcg50/hardware.h similarity index 100% rename from include/vhex/arch/fxcg50/hardware.h rename to board/fxcg50/hardware.h diff --git a/board/fxcg50/initialize.c b/board/fxcg50/initialize.c index 3b2b336..d8c5f03 100644 --- a/board/fxcg50/initialize.c +++ b/board/fxcg50/initialize.c @@ -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 + 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 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 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 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 diff --git a/config b/config index 13d57e7..d3713ff 100755 --- a/config +++ b/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:') diff --git a/include/vhex/arch/sh7305/cpu.h b/include/vhex/arch/sh7305/cpu.h new file mode 100644 index 0000000..13e8262 --- /dev/null +++ b/include/vhex/arch/sh7305/cpu.h @@ -0,0 +1,75 @@ +#ifndef __VHEX_ARCH_SH7305_CPU__ +# define __VHEX_ARCH_SH7305_CPU__ + +#include +#include + +//--- +// 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__ */ diff --git a/include/vhex/arch/sh7305/intc.h b/include/vhex/arch/sh7305/intc.h new file mode 100644 index 0000000..528ed9a --- /dev/null +++ b/include/vhex/arch/sh7305/intc.h @@ -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__ */ diff --git a/include/vhex/mpu/sh7305/mmu.h b/include/vhex/arch/sh7305/mmu.h similarity index 100% rename from include/vhex/mpu/sh7305/mmu.h rename to include/vhex/arch/sh7305/mmu.h diff --git a/include/vhex/mpu/sh7305/pfc.h b/include/vhex/arch/sh7305/pfc.h similarity index 100% rename from include/vhex/mpu/sh7305/pfc.h rename to include/vhex/arch/sh7305/pfc.h diff --git a/include/vhex/driver.h b/include/vhex/driver.h new file mode 100644 index 0000000..0d0c983 --- /dev/null +++ b/include/vhex/driver.h @@ -0,0 +1,100 @@ +#ifndef __VHEX_DRIVER__ +# define __VHEX_DRIVER__ + +#include + +/* 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__ */ diff --git a/include/vhex/drivers/cpu.h b/include/vhex/drivers/cpu.h new file mode 100644 index 0000000..abe1aa3 --- /dev/null +++ b/include/vhex/drivers/cpu.h @@ -0,0 +1,33 @@ +#ifndef __VHEX_DRIVERS_CPU__ +# define __VHEX_DRIVERS_CPU__ + +#include +#include + +/* 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__ */ diff --git a/include/vhex/mmu.h b/include/vhex/drivers/mmu.h similarity index 100% rename from include/vhex/mmu.h rename to include/vhex/drivers/mmu.h diff --git a/include/vhex/hypervisor.h b/include/vhex/hypervisor.h new file mode 100644 index 0000000..f35bdcc --- /dev/null +++ b/include/vhex/hypervisor.h @@ -0,0 +1,94 @@ +#ifndef __VHEX_HYPERVISOR__ +# define __VHEX_HYPERVISOR__ + +#include +#include + +/* declare driver structure */ +#include + +/* 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__ */ diff --git a/src/drivers/mpu/sh/sh7305/cpu/atomic.c b/src/drivers/mpu/sh/sh7305/cpu/atomic.c new file mode 100644 index 0000000..e4201d9 --- /dev/null +++ b/src/drivers/mpu/sh/sh7305/cpu/atomic.c @@ -0,0 +1,37 @@ +#include +#include + +/* 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); +} diff --git a/src/drivers/mpu/sh/sh7305/cpu/cpu.c b/src/drivers/mpu/sh/sh7305/cpu/cpu.c new file mode 100644 index 0000000..c3e2d17 --- /dev/null +++ b/src/drivers/mpu/sh/sh7305/cpu/cpu.c @@ -0,0 +1,70 @@ +#include "vhex/driver.h" +#include "vhex/arch/sh7305/cpu.h" + +#include + +/* 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); diff --git a/src/drivers/mpu/sh/sh7305/cpu/registers.s b/src/drivers/mpu/sh/sh7305/cpu/registers.s new file mode 100644 index 0000000..df7482b --- /dev/null +++ b/src/drivers/mpu/sh/sh7305/cpu/registers.s @@ -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 diff --git a/src/drivers/mpu/sh/sh7305/intc/intc.c b/src/drivers/mpu/sh/sh7305/intc/intc.c new file mode 100644 index 0000000..0e4735e --- /dev/null +++ b/src/drivers/mpu/sh/sh7305/intc/intc.c @@ -0,0 +1,163 @@ +#include "vhex/driver.h" +#include "vhex/arch/sh7305/intc.h" + +#include + +/* 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); diff --git a/src/drivers/mpu/sh/sh7305/mmu.c b/src/drivers/mpu/sh/sh7305/mmu/utlb.c similarity index 95% rename from src/drivers/mpu/sh/sh7305/mmu.c rename to src/drivers/mpu/sh/sh7305/mmu/utlb.c index f97d2c6..6901192 100644 --- a/src/drivers/mpu/sh/sh7305/mmu.c +++ b/src/drivers/mpu/sh/sh7305/mmu/utlb.c @@ -1,8 +1,8 @@ //--- // vhex:mmu:mmu - MMU driver definition and context management //--- -#include -#include +#include +#include #include /* utlb_addr() - get the P4 address of a UTLB address entry */ diff --git a/src/drivers/screen/R61524/r61524.c b/src/drivers/screen/R61524/r61524.c new file mode 100644 index 0000000..dbd7107 --- /dev/null +++ b/src/drivers/screen/R61524/r61524.c @@ -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 diff --git a/src/hypervisor/switch.c b/src/hypervisor/switch.c new file mode 100644 index 0000000..b7c6ac7 --- /dev/null +++ b/src/hypervisor/switch.c @@ -0,0 +1,64 @@ +#include +#include + +#include + +/* 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(); +} diff --git a/src/hypervisor/table.c b/src/hypervisor/table.c new file mode 100644 index 0000000..5f983c8 --- /dev/null +++ b/src/hypervisor/table.c @@ -0,0 +1,119 @@ +//--- +// hypervisor:table - world allocator +//--- +#include + +#include +#include + +/* 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; +} diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 925388f..c4ac665 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -1,4 +1,5 @@ #include +#include //--- // 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); } diff --git a/src/render/dhline.c b/src/render/dhline.c index 551ca6a..baa9561 100644 --- a/src/render/dhline.c +++ b/src/render/dhline.c @@ -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 */ diff --git a/vxsdk.toml b/vxsdk.toml new file mode 100644 index 0000000..a031c36 --- /dev/null +++ b/vxsdk.toml @@ -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'