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:
Yann MAGNIN 2022-02-13 15:01:01 +01:00
parent 2fbee9cdeb
commit df8d26ecca
24 changed files with 1376 additions and 25 deletions

View File

@ -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/ : {

View File

@ -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 */

View File

@ -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
View File

@ -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:')

View File

@ -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__ */

View File

@ -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__ */

100
include/vhex/driver.h Normal file
View File

@ -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__ */

View File

@ -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__ */

94
include/vhex/hypervisor.h Normal file
View File

@ -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__ */

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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

64
src/hypervisor/switch.c Normal file
View File

@ -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();
}

119
src/hypervisor/table.c Normal file
View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 */

9
vxsdk.toml Normal file
View File

@ -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'