Merge branch 'master' of postpeta.pccluster.org:mckernel
This commit is contained in:
@ -311,9 +311,45 @@ static void enable_page_protection_fault(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int no_execute_available = 0;
|
||||||
|
|
||||||
|
static void enable_no_execute(void)
|
||||||
|
{
|
||||||
|
unsigned long efer;
|
||||||
|
|
||||||
|
if (!no_execute_available) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
efer = rdmsr(MSR_EFER);
|
||||||
|
#define IA32_EFER_NXE (1UL << 11)
|
||||||
|
efer |= IA32_EFER_NXE;
|
||||||
|
wrmsr(MSR_EFER, efer);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_no_execute(void)
|
||||||
|
{
|
||||||
|
uint32_t edx;
|
||||||
|
extern void enable_ptattr_no_execute(void);
|
||||||
|
|
||||||
|
/* check Execute Disable Bit available bit */
|
||||||
|
asm ("cpuid" : "=d" (edx) : "a" (0x80000001) : "%rbx", "%rcx");
|
||||||
|
no_execute_available = (edx & (1 << 20))? 1: 0;
|
||||||
|
kprintf("no_execute_available: %d\n", no_execute_available);
|
||||||
|
|
||||||
|
if (no_execute_available) {
|
||||||
|
enable_ptattr_no_execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void init_cpu(void)
|
void init_cpu(void)
|
||||||
{
|
{
|
||||||
enable_page_protection_fault();
|
enable_page_protection_fault();
|
||||||
|
enable_no_execute();
|
||||||
init_fpu();
|
init_fpu();
|
||||||
init_lapic();
|
init_lapic();
|
||||||
init_syscall();
|
init_syscall();
|
||||||
@ -330,6 +366,8 @@ void setup_x86(void)
|
|||||||
|
|
||||||
init_page_table();
|
init_page_table();
|
||||||
|
|
||||||
|
check_no_execute();
|
||||||
|
|
||||||
init_cpu();
|
init_cpu();
|
||||||
|
|
||||||
kprintf("setup_x86 done.\n");
|
kprintf("setup_x86 done.\n");
|
||||||
|
|||||||
@ -114,6 +114,7 @@ enum ihk_mc_pt_attribute {
|
|||||||
PTATTR_WRITABLE = 0x02,
|
PTATTR_WRITABLE = 0x02,
|
||||||
PTATTR_USER = 0x04,
|
PTATTR_USER = 0x04,
|
||||||
PTATTR_LARGEPAGE = 0x80,
|
PTATTR_LARGEPAGE = 0x80,
|
||||||
|
PTATTR_NO_EXECUTE = 0x8000000000000000,
|
||||||
PTATTR_UNCACHABLE = 0x10000,
|
PTATTR_UNCACHABLE = 0x10000,
|
||||||
PTATTR_FOR_USER = 0x20000,
|
PTATTR_FOR_USER = 0x20000,
|
||||||
};
|
};
|
||||||
@ -142,8 +143,8 @@ static inline uintptr_t pte_get_phys(pte_t *ptep)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct page_table;
|
struct page_table;
|
||||||
void set_pte(pte_t *ppte, unsigned long phys, int attr);
|
void set_pte(pte_t *ppte, unsigned long phys, enum ihk_mc_pt_attribute attr);
|
||||||
pte_t *get_pte(struct page_table *pt, void *virt, int attr);
|
pte_t *get_pte(struct page_table *pt, void *virt, enum ihk_mc_pt_attribute attr);
|
||||||
|
|
||||||
void *early_alloc_page(void);
|
void *early_alloc_page(void);
|
||||||
void *get_last_early_heap(void);
|
void *get_last_early_heap(void);
|
||||||
@ -156,5 +157,5 @@ void *map_fixed_area(unsigned long phys, unsigned long size, int uncachable);
|
|||||||
#define AP_TRAMPOLINE_SIZE 0x4000
|
#define AP_TRAMPOLINE_SIZE 0x4000
|
||||||
|
|
||||||
/* Local is cachable */
|
/* Local is cachable */
|
||||||
#define IHK_IKC_QUEUE_PT_ATTR (PTATTR_WRITABLE | PTATTR_UNCACHABLE)
|
#define IHK_IKC_QUEUE_PT_ATTR (PTATTR_NO_EXECUTE | PTATTR_WRITABLE | PTATTR_UNCACHABLE)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -219,7 +219,15 @@ static struct page_table *__alloc_new_pt(enum ihk_mc_ap_flag ap_flag)
|
|||||||
* but L2 and L1 do not!
|
* but L2 and L1 do not!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ATTR_MASK (PTATTR_WRITABLE | PTATTR_USER | PTATTR_ACTIVE)
|
static enum ihk_mc_pt_attribute attr_mask = PTATTR_WRITABLE | PTATTR_USER | PTATTR_ACTIVE;
|
||||||
|
#define ATTR_MASK attr_mask
|
||||||
|
|
||||||
|
void enable_ptattr_no_execute(void)
|
||||||
|
{
|
||||||
|
attr_mask |= PTATTR_NO_EXECUTE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static unsigned long attr_to_l4attr(enum ihk_mc_pt_attribute attr)
|
static unsigned long attr_to_l4attr(enum ihk_mc_pt_attribute attr)
|
||||||
{
|
{
|
||||||
@ -266,7 +274,7 @@ static unsigned long attr_to_l1attr(enum ihk_mc_pt_attribute attr)
|
|||||||
| ((uint64_t)(l1i) << PTL1_SHIFT) \
|
| ((uint64_t)(l1i) << PTL1_SHIFT) \
|
||||||
)
|
)
|
||||||
|
|
||||||
void set_pte(pte_t *ppte, unsigned long phys, int attr)
|
void set_pte(pte_t *ppte, unsigned long phys, enum ihk_mc_pt_attribute attr)
|
||||||
{
|
{
|
||||||
if (attr & PTATTR_LARGEPAGE) {
|
if (attr & PTATTR_LARGEPAGE) {
|
||||||
*ppte = phys | attr_to_l2attr(attr) | PFL2_SIZE;
|
*ppte = phys | attr_to_l2attr(attr) | PFL2_SIZE;
|
||||||
@ -285,7 +293,7 @@ void set_pte(pte_t *ppte, unsigned long phys, int attr)
|
|||||||
* and returns a pointer to the PTE corresponding to the
|
* and returns a pointer to the PTE corresponding to the
|
||||||
* virtual address.
|
* virtual address.
|
||||||
*/
|
*/
|
||||||
pte_t *get_pte(struct page_table *pt, void *virt, int attr, enum ihk_mc_ap_flag ap_flag)
|
pte_t *get_pte(struct page_table *pt, void *virt, enum ihk_mc_pt_attribute attr, enum ihk_mc_ap_flag ap_flag)
|
||||||
{
|
{
|
||||||
int l4idx, l3idx, l2idx, l1idx;
|
int l4idx, l3idx, l2idx, l1idx;
|
||||||
unsigned long v = (unsigned long)virt;
|
unsigned long v = (unsigned long)virt;
|
||||||
@ -341,7 +349,7 @@ pte_t *get_pte(struct page_table *pt, void *virt, int attr, enum ihk_mc_ap_flag
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int __set_pt_page(struct page_table *pt, void *virt, unsigned long phys,
|
static int __set_pt_page(struct page_table *pt, void *virt, unsigned long phys,
|
||||||
int attr)
|
enum ihk_mc_pt_attribute attr)
|
||||||
{
|
{
|
||||||
int l4idx, l3idx, l2idx, l1idx;
|
int l4idx, l3idx, l2idx, l1idx;
|
||||||
unsigned long v = (unsigned long)virt;
|
unsigned long v = (unsigned long)virt;
|
||||||
@ -1666,21 +1674,25 @@ void *map_fixed_area(unsigned long phys, unsigned long size, int uncachable)
|
|||||||
{
|
{
|
||||||
unsigned long poffset, paligned;
|
unsigned long poffset, paligned;
|
||||||
int i, npages;
|
int i, npages;
|
||||||
int flag = PTATTR_WRITABLE | PTATTR_ACTIVE;
|
|
||||||
void *v = (void *)fixed_virt;
|
void *v = (void *)fixed_virt;
|
||||||
|
enum ihk_mc_pt_attribute attr;
|
||||||
|
|
||||||
poffset = phys & (PAGE_SIZE - 1);
|
poffset = phys & (PAGE_SIZE - 1);
|
||||||
paligned = phys & PAGE_MASK;
|
paligned = phys & PAGE_MASK;
|
||||||
npages = (poffset + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
npages = (poffset + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||||
|
|
||||||
|
attr = PTATTR_WRITABLE | PTATTR_ACTIVE;
|
||||||
|
#if 0 /* In the case of LAPIC MMIO, something will happen */
|
||||||
|
attr |= PTATTR_NO_EXECUTE;
|
||||||
|
#endif
|
||||||
if (uncachable) {
|
if (uncachable) {
|
||||||
flag |= PTATTR_UNCACHABLE;
|
attr |= PTATTR_UNCACHABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
kprintf("map_fixed: %lx => %p (%d pages)\n", paligned, v, npages);
|
kprintf("map_fixed: %lx => %p (%d pages)\n", paligned, v, npages);
|
||||||
|
|
||||||
for (i = 0; i < npages; i++) {
|
for (i = 0; i < npages; i++) {
|
||||||
if(__set_pt_page(init_pt, (void *)fixed_virt, paligned, flag)){
|
if(__set_pt_page(init_pt, (void *)fixed_virt, paligned, attr)){
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1695,7 +1707,7 @@ void *map_fixed_area(unsigned long phys, unsigned long size, int uncachable)
|
|||||||
|
|
||||||
void init_low_area(struct page_table *pt)
|
void init_low_area(struct page_table *pt)
|
||||||
{
|
{
|
||||||
set_pt_large_page(pt, 0, 0, PTATTR_WRITABLE);
|
set_pt_large_page(pt, 0, 0, PTATTR_NO_EXECUTE|PTATTR_WRITABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_vsyscall_area(struct page_table *pt)
|
static void init_vsyscall_area(struct page_table *pt)
|
||||||
|
|||||||
@ -65,6 +65,7 @@ struct program_load_desc {
|
|||||||
int cpu;
|
int cpu;
|
||||||
int pid;
|
int pid;
|
||||||
int err;
|
int err;
|
||||||
|
int stack_prot;
|
||||||
unsigned long entry;
|
unsigned long entry;
|
||||||
unsigned long user_start;
|
unsigned long user_start;
|
||||||
unsigned long user_end;
|
unsigned long user_end;
|
||||||
|
|||||||
@ -159,6 +159,7 @@ struct program_load_desc *load_elf(FILE *fp, char **interp_pathp)
|
|||||||
fseek(fp, hdr.e_phoff, SEEK_SET);
|
fseek(fp, hdr.e_phoff, SEEK_SET);
|
||||||
j = 0;
|
j = 0;
|
||||||
desc->num_sections = nhdrs;
|
desc->num_sections = nhdrs;
|
||||||
|
desc->stack_prot = PROT_READ | PROT_WRITE | PROT_EXEC; /* default */
|
||||||
for (i = 0; i < hdr.e_phnum; i++) {
|
for (i = 0; i < hdr.e_phnum; i++) {
|
||||||
if (fread(&phdr, sizeof(phdr), 1, fp) < 1) {
|
if (fread(&phdr, sizeof(phdr), 1, fp) < 1) {
|
||||||
__eprintf("Loading phdr failed (%d)\n", i);
|
__eprintf("Loading phdr failed (%d)\n", i);
|
||||||
@ -205,6 +206,12 @@ struct program_load_desc *load_elf(FILE *fp, char **interp_pathp)
|
|||||||
load_addr = phdr.p_vaddr - phdr.p_offset;
|
load_addr = phdr.p_vaddr - phdr.p_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (phdr.p_type == PT_GNU_STACK) {
|
||||||
|
desc->stack_prot = PROT_NONE;
|
||||||
|
desc->stack_prot |= (phdr.p_flags & PF_R)? PROT_READ: 0;
|
||||||
|
desc->stack_prot |= (phdr.p_flags & PF_W)? PROT_WRITE: 0;
|
||||||
|
desc->stack_prot |= (phdr.p_flags & PF_X)? PROT_EXEC: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
desc->pid = getpid();
|
desc->pid = getpid();
|
||||||
desc->entry = hdr.e_entry;
|
desc->entry = hdr.e_entry;
|
||||||
|
|||||||
@ -69,13 +69,16 @@ static int process_msg_prepare_process(unsigned long rphys)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
uintptr_t interp_obase = -1;
|
uintptr_t interp_obase = -1;
|
||||||
uintptr_t interp_nbase = -1;
|
uintptr_t interp_nbase = -1;
|
||||||
|
enum ihk_mc_pt_attribute attr;
|
||||||
|
|
||||||
|
attr = PTATTR_NO_EXECUTE | PTATTR_WRITABLE | PTATTR_FOR_USER;
|
||||||
|
|
||||||
sz = sizeof(struct program_load_desc)
|
sz = sizeof(struct program_load_desc)
|
||||||
+ sizeof(struct program_image_section) * 16;
|
+ sizeof(struct program_image_section) * 16;
|
||||||
npages = ((rphys + sz - 1) >> PAGE_SHIFT) - (rphys >> PAGE_SHIFT) + 1;
|
npages = ((rphys + sz - 1) >> PAGE_SHIFT) - (rphys >> PAGE_SHIFT) + 1;
|
||||||
|
|
||||||
phys = ihk_mc_map_memory(NULL, rphys, sz);
|
phys = ihk_mc_map_memory(NULL, rphys, sz);
|
||||||
if((p = ihk_mc_map_virtual(phys, npages, PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
if((p = ihk_mc_map_virtual(phys, npages, attr)) == NULL){
|
||||||
ihk_mc_unmap_memory(NULL, phys, sz);
|
ihk_mc_unmap_memory(NULL, phys, sz);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -264,7 +267,7 @@ static int process_msg_prepare_process(unsigned long rphys)
|
|||||||
args_envs_rp = ihk_mc_map_memory(NULL, (unsigned long)p->args, p->args_len);
|
args_envs_rp = ihk_mc_map_memory(NULL, (unsigned long)p->args, p->args_len);
|
||||||
dkprintf("args_envs_rp: 0x%lX\n", args_envs_rp);
|
dkprintf("args_envs_rp: 0x%lX\n", args_envs_rp);
|
||||||
if((args_envs_r = (char *)ihk_mc_map_virtual(args_envs_rp, args_envs_npages,
|
if((args_envs_r = (char *)ihk_mc_map_virtual(args_envs_rp, args_envs_npages,
|
||||||
PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
attr)) == NULL){
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
dkprintf("args_envs_r: 0x%lX\n", args_envs_r);
|
dkprintf("args_envs_r: 0x%lX\n", args_envs_r);
|
||||||
@ -285,7 +288,7 @@ static int process_msg_prepare_process(unsigned long rphys)
|
|||||||
args_envs_rp = ihk_mc_map_memory(NULL, (unsigned long)p->envs, p->envs_len);
|
args_envs_rp = ihk_mc_map_memory(NULL, (unsigned long)p->envs, p->envs_len);
|
||||||
dkprintf("args_envs_rp: 0x%lX\n", args_envs_rp);
|
dkprintf("args_envs_rp: 0x%lX\n", args_envs_rp);
|
||||||
if((args_envs_r = (char *)ihk_mc_map_virtual(args_envs_rp, args_envs_npages,
|
if((args_envs_r = (char *)ihk_mc_map_virtual(args_envs_rp, args_envs_npages,
|
||||||
PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
attr)) == NULL){
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
dkprintf("args_envs_r: 0x%lX\n", args_envs_r);
|
dkprintf("args_envs_r: 0x%lX\n", args_envs_r);
|
||||||
@ -363,6 +366,9 @@ static void process_msg_init_acked(struct ihk_ikc_channel_desc *c, unsigned long
|
|||||||
{
|
{
|
||||||
struct ikc_scd_init_param *param = (void *)pphys;
|
struct ikc_scd_init_param *param = (void *)pphys;
|
||||||
struct syscall_params *lparam;
|
struct syscall_params *lparam;
|
||||||
|
enum ihk_mc_pt_attribute attr;
|
||||||
|
|
||||||
|
attr = PTATTR_NO_EXECUTE | PTATTR_WRITABLE | PTATTR_FOR_USER;
|
||||||
|
|
||||||
lparam = &cpu_local_var(scp);
|
lparam = &cpu_local_var(scp);
|
||||||
if(cpu_local_var(syscall_channel2) == c)
|
if(cpu_local_var(syscall_channel2) == c)
|
||||||
@ -372,7 +378,7 @@ static void process_msg_init_acked(struct ihk_ikc_channel_desc *c, unsigned long
|
|||||||
REQUEST_PAGE_COUNT * PAGE_SIZE);
|
REQUEST_PAGE_COUNT * PAGE_SIZE);
|
||||||
if((lparam->request_va = ihk_mc_map_virtual(lparam->request_pa,
|
if((lparam->request_va = ihk_mc_map_virtual(lparam->request_pa,
|
||||||
REQUEST_PAGE_COUNT,
|
REQUEST_PAGE_COUNT,
|
||||||
PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
attr)) == NULL){
|
||||||
// TODO:
|
// TODO:
|
||||||
panic("ENOMEM");
|
panic("ENOMEM");
|
||||||
}
|
}
|
||||||
@ -383,7 +389,7 @@ static void process_msg_init_acked(struct ihk_ikc_channel_desc *c, unsigned long
|
|||||||
PAGE_SIZE);
|
PAGE_SIZE);
|
||||||
if((lparam->doorbell_va = ihk_mc_map_virtual(lparam->doorbell_pa,
|
if((lparam->doorbell_va = ihk_mc_map_virtual(lparam->doorbell_pa,
|
||||||
DOORBELL_PAGE_COUNT,
|
DOORBELL_PAGE_COUNT,
|
||||||
PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
attr)) == NULL){
|
||||||
// TODO:
|
// TODO:
|
||||||
panic("ENOMEM");
|
panic("ENOMEM");
|
||||||
}
|
}
|
||||||
@ -392,7 +398,7 @@ static void process_msg_init_acked(struct ihk_ikc_channel_desc *c, unsigned long
|
|||||||
lparam->post_pa = ihk_mc_map_memory(NULL, param->post_page,
|
lparam->post_pa = ihk_mc_map_memory(NULL, param->post_page,
|
||||||
PAGE_SIZE);
|
PAGE_SIZE);
|
||||||
if((lparam->post_va = ihk_mc_map_virtual(lparam->post_pa, 1,
|
if((lparam->post_va = ihk_mc_map_virtual(lparam->post_pa, 1,
|
||||||
PTATTR_WRITABLE | PTATTR_FOR_USER)) == NULL){
|
attr)) == NULL){
|
||||||
// TODO:
|
// TODO:
|
||||||
panic("ENOMEM");
|
panic("ENOMEM");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,6 +109,7 @@ struct program_load_desc {
|
|||||||
int cpu;
|
int cpu;
|
||||||
int pid;
|
int pid;
|
||||||
int err;
|
int err;
|
||||||
|
int stack_prot;
|
||||||
unsigned long entry;
|
unsigned long entry;
|
||||||
unsigned long user_start;
|
unsigned long user_start;
|
||||||
unsigned long user_end;
|
unsigned long user_end;
|
||||||
|
|||||||
@ -347,6 +347,7 @@ int update_process_page_table(struct process *process,
|
|||||||
|
|
||||||
attr = flag | PTATTR_USER | PTATTR_FOR_USER;
|
attr = flag | PTATTR_USER | PTATTR_FOR_USER;
|
||||||
attr |= (range->flag & VR_PROT_WRITE)? PTATTR_WRITABLE: 0;
|
attr |= (range->flag & VR_PROT_WRITE)? PTATTR_WRITABLE: 0;
|
||||||
|
attr |= (range->flag & VR_PROT_EXEC)? 0: PTATTR_NO_EXECUTE;
|
||||||
|
|
||||||
p = range->start;
|
p = range->start;
|
||||||
while (p < range->end) {
|
while (p < range->end) {
|
||||||
@ -689,6 +690,10 @@ enum ihk_mc_pt_attribute vrflag_to_ptattr(unsigned long flag)
|
|||||||
attr |= PTATTR_WRITABLE;
|
attr |= PTATTR_WRITABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(flag & VR_PROT_EXEC)) {
|
||||||
|
attr |= PTATTR_NO_EXECUTE;
|
||||||
|
}
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1197,7 +1202,9 @@ static int do_page_fault_process(struct process *proc, void *fault_addr0, uint64
|
|||||||
|
|
||||||
if (((range->flag & VR_PROT_MASK) == VR_PROT_NONE)
|
if (((range->flag & VR_PROT_MASK) == VR_PROT_NONE)
|
||||||
|| ((reason & PF_WRITE)
|
|| ((reason & PF_WRITE)
|
||||||
&& !(range->flag & VR_PROT_WRITE))) {
|
&& !(range->flag & VR_PROT_WRITE))
|
||||||
|
|| ((reason & PF_INSTR)
|
||||||
|
&& !(range->flag & VR_PROT_EXEC))) {
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
kprintf("[%d]do_page_fault_process(%p,%lx,%lx):"
|
kprintf("[%d]do_page_fault_process(%p,%lx,%lx):"
|
||||||
"access denied. %d\n",
|
"access denied. %d\n",
|
||||||
@ -1325,8 +1332,8 @@ int init_process_stack(struct process *process, struct program_load_desc *pn,
|
|||||||
start = end - size;
|
start = end - size;
|
||||||
|
|
||||||
vrflag = VR_STACK | VR_DEMAND_PAGING;
|
vrflag = VR_STACK | VR_DEMAND_PAGING;
|
||||||
vrflag |= VR_PROT_READ | VR_PROT_WRITE | VR_PROT_EXEC;
|
vrflag |= PROT_TO_VR_FLAG(pn->stack_prot);
|
||||||
vrflag |= VRFLAG_PROT_TO_MAXPROT(vrflag);
|
vrflag |= VR_MAXPROT_READ | VR_MAXPROT_WRITE | VR_MAXPROT_EXEC;
|
||||||
#define NOPHYS ((uintptr_t)-1)
|
#define NOPHYS ((uintptr_t)-1)
|
||||||
if ((rc = add_process_memory_range(process, start, end, NOPHYS,
|
if ((rc = add_process_memory_range(process, start, end, NOPHYS,
|
||||||
vrflag, NULL, 0)) != 0) {
|
vrflag, NULL, 0)) != 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user