GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2015 Google, Inc
|
||||
|
||||
obj-y += fsp_car.o
|
||||
obj-y += fsp_common.o
|
||||
obj-y += fsp_dram.o
|
||||
obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o
|
||||
obj-y += fsp_support.o
|
||||
@@ -0,0 +1,111 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
.globl car_init
|
||||
car_init:
|
||||
/*
|
||||
* Note: ebp holds the BIST value (built-in self test) so far, but ebp
|
||||
* will be destroyed through the FSP call, thus we have to test the
|
||||
* BIST value here before we call into FSP.
|
||||
*/
|
||||
test %ebp, %ebp
|
||||
jz car_init_start
|
||||
post_code(POST_BIST_FAILURE)
|
||||
jmp die
|
||||
|
||||
car_init_start:
|
||||
post_code(POST_CAR_START)
|
||||
lea fsp_find_header_romstack, %esp
|
||||
jmp fsp_find_header
|
||||
|
||||
fsp_find_header_ret:
|
||||
/* EAX points to FSP_INFO_HEADER */
|
||||
mov %eax, %ebp
|
||||
|
||||
/* sanity test */
|
||||
cmp $CONFIG_FSP_ADDR, %eax
|
||||
jb die
|
||||
|
||||
/* calculate TempRamInitEntry address */
|
||||
mov 0x30(%ebp), %eax
|
||||
add 0x1c(%ebp), %eax
|
||||
|
||||
/* call FSP TempRamInitEntry to setup temporary stack */
|
||||
lea temp_ram_init_romstack, %esp
|
||||
jmp *%eax
|
||||
|
||||
temp_ram_init_ret:
|
||||
addl $4, %esp
|
||||
cmp $0, %eax
|
||||
jnz car_init_fail
|
||||
|
||||
post_code(POST_CAR_CPU_CACHE)
|
||||
|
||||
/*
|
||||
* The FSP TempRamInit initializes the ecx and edx registers to
|
||||
* point to a temporary but writable memory range (Cache-As-RAM).
|
||||
* ecx: the start of this temporary memory range,
|
||||
* edx: the end of this range.
|
||||
*/
|
||||
|
||||
/* stack grows down from top of CAR */
|
||||
movl %edx, %esp
|
||||
subl $4, %esp
|
||||
|
||||
xor %esi, %esi
|
||||
jmp car_init_done
|
||||
|
||||
.global fsp_init_done
|
||||
fsp_init_done:
|
||||
/*
|
||||
* We come here from fsp_continue() with eax pointing to the HOB list.
|
||||
* Save eax to esi temporarily.
|
||||
*/
|
||||
movl %eax, %esi
|
||||
|
||||
car_init_done:
|
||||
/*
|
||||
* Re-initialize the ebp (BIST) to zero, as we already reach here
|
||||
* which means we passed BIST testing before.
|
||||
*/
|
||||
xorl %ebp, %ebp
|
||||
jmp car_init_ret
|
||||
|
||||
car_init_fail:
|
||||
post_code(POST_CAR_FAILURE)
|
||||
|
||||
die:
|
||||
hlt
|
||||
jmp die
|
||||
hlt
|
||||
|
||||
/*
|
||||
* The function call before CAR initialization is tricky. It cannot
|
||||
* be called using the 'call' instruction but only the 'jmp' with
|
||||
* the help of a handcrafted stack in the ROM. The stack needs to
|
||||
* contain the function return address as well as the parameters.
|
||||
*/
|
||||
.balign 4
|
||||
fsp_find_header_romstack:
|
||||
.long fsp_find_header_ret
|
||||
|
||||
.balign 4
|
||||
temp_ram_init_romstack:
|
||||
.long temp_ram_init_ret
|
||||
.long temp_ram_init_params
|
||||
temp_ram_init_params:
|
||||
_dt_ucode_base_size:
|
||||
/* These next two fields are filled in by binman */
|
||||
.globl ucode_base
|
||||
ucode_base: /* Declared in microcode.h */
|
||||
.long 0 /* microcode base */
|
||||
.globl ucode_size
|
||||
ucode_size: /* Declared in microcode.h */
|
||||
.long 0 /* microcode size */
|
||||
.long CONFIG_SYS_MONITOR_BASE /* code region base */
|
||||
.long CONFIG_SYS_MONITOR_LEN /* code region size */
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <acpi_s3.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <rtc.h>
|
||||
#include <asm/cmos_layout.h>
|
||||
#include <asm/early_cmos.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/post.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int arch_fsp_init(void)
|
||||
{
|
||||
void *nvs;
|
||||
int stack = CONFIG_FSP_TEMP_RAM_ADDR;
|
||||
int boot_mode = BOOT_FULL_CONFIG;
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int prev_sleep_state = chipset_prev_sleep_state();
|
||||
gd->arch.prev_sleep_state = prev_sleep_state;
|
||||
#endif
|
||||
|
||||
if (!gd->arch.hob_list) {
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
nvs = fsp_prepare_mrc_cache();
|
||||
else
|
||||
nvs = NULL;
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
if (prev_sleep_state == ACPI_S3) {
|
||||
if (nvs == NULL) {
|
||||
/* If waking from S3 and no cache then */
|
||||
debug("No MRC cache found in S3 resume path\n");
|
||||
post_code(POST_RESUME_FAILURE);
|
||||
/* Clear Sleep Type */
|
||||
chipset_clear_sleep_state();
|
||||
/* Reboot */
|
||||
debug("Rebooting..\n");
|
||||
outb(SYS_RST | RST_CPU, IO_PORT_RESET);
|
||||
/* Should not reach here.. */
|
||||
panic("Reboot System");
|
||||
}
|
||||
|
||||
/*
|
||||
* DM is not available yet at this point, hence call
|
||||
* CMOS access library which does not depend on DM.
|
||||
*/
|
||||
stack = cmos_read32(CMOS_FSP_STACK_ADDR);
|
||||
boot_mode = BOOT_ON_S3_RESUME;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* The first time we enter here, call fsp_init().
|
||||
* Note the execution does not return to this function,
|
||||
* instead it jumps to fsp_continue().
|
||||
*/
|
||||
fsp_init(stack, boot_mode, nvs);
|
||||
} else {
|
||||
/*
|
||||
* The second time we enter here, adjust the size of malloc()
|
||||
* pool before relocation. Given gd->malloc_base was adjusted
|
||||
* after the call to board_init_f_init_reserve() in arch/x86/
|
||||
* cpu/start.S, we should fix up gd->malloc_limit here.
|
||||
*/
|
||||
gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/fsp/fsp_support.h>
|
||||
|
||||
int dram_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The FSP has already set up DRAM, so grab the info we need */
|
||||
ret = fsp_scan_for_ram_size();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list,
|
||||
&gd->arch.mrc_output_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function looks for the highest region of memory lower than 4GB which
|
||||
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
|
||||
* It overrides the default implementation found elsewhere which simply
|
||||
* picks the end of ram, wherever that may be. The location of the stack,
|
||||
* the relocation address, and how far U-Boot is moved by relocation are
|
||||
* set in the global data structure.
|
||||
*/
|
||||
ulong board_get_usable_ram_top(ulong total_size)
|
||||
{
|
||||
return fsp_get_usable_lowmem_top(gd->arch.hob_list);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <vbe.h>
|
||||
#include <video.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct pixel {
|
||||
u8 pos;
|
||||
u8 size;
|
||||
};
|
||||
|
||||
static const struct fsp_framebuffer {
|
||||
struct pixel red;
|
||||
struct pixel green;
|
||||
struct pixel blue;
|
||||
struct pixel rsvd;
|
||||
} fsp_framebuffer_format_map[] = {
|
||||
[pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
|
||||
[pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
|
||||
};
|
||||
|
||||
static int save_vesa_mode(struct vesa_mode_info *vesa)
|
||||
{
|
||||
const struct hob_graphics_info *ginfo;
|
||||
const struct fsp_framebuffer *fbinfo;
|
||||
|
||||
ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL);
|
||||
|
||||
/*
|
||||
* If there is no graphics info structure, bail out and keep
|
||||
* running on the serial console.
|
||||
*
|
||||
* Note: on some platforms (eg: Braswell), the FSP will not produce
|
||||
* the graphics info HOB unless you plug some cables to the display
|
||||
* interface (eg: HDMI) on the board.
|
||||
*/
|
||||
if (!ginfo) {
|
||||
debug("FSP graphics hand-off block not found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
vesa->x_resolution = ginfo->width;
|
||||
vesa->y_resolution = ginfo->height;
|
||||
vesa->bits_per_pixel = 32;
|
||||
vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4;
|
||||
vesa->phys_base_ptr = ginfo->fb_base;
|
||||
|
||||
if (ginfo->pixel_format >= pixel_bitmask) {
|
||||
debug("FSP set unknown framebuffer format: %d\n",
|
||||
ginfo->pixel_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format];
|
||||
vesa->red_mask_size = fbinfo->red.size;
|
||||
vesa->red_mask_pos = fbinfo->red.pos;
|
||||
vesa->green_mask_size = fbinfo->green.size;
|
||||
vesa->green_mask_pos = fbinfo->green.pos;
|
||||
vesa->blue_mask_size = fbinfo->blue.size;
|
||||
vesa->blue_mask_pos = fbinfo->blue.pos;
|
||||
vesa->reserved_mask_size = fbinfo->rsvd.size;
|
||||
vesa->reserved_mask_pos = fbinfo->rsvd.pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp_video_probe(struct udevice *dev)
|
||||
{
|
||||
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct vesa_mode_info *vesa = &mode_info.vesa;
|
||||
int ret;
|
||||
|
||||
printf("Video: ");
|
||||
|
||||
/* Initialize vesa_mode_info structure */
|
||||
ret = save_vesa_mode(vesa);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* The framebuffer base address in the FSP graphics info HOB reflects
|
||||
* the value assigned by the FSP. After PCI enumeration the framebuffer
|
||||
* base address may be relocated. Let's get the updated one from device.
|
||||
*
|
||||
* For IGD, it seems to be always on BAR2.
|
||||
*/
|
||||
vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
|
||||
|
||||
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
|
||||
vesa->bits_per_pixel);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
printf("No video mode configured in FSP!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id fsp_video_ids[] = {
|
||||
{ .compatible = "fsp-fb" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(fsp_video) = {
|
||||
.name = "fsp_video",
|
||||
.id = UCLASS_VIDEO,
|
||||
.of_match = fsp_video_ids,
|
||||
.probe = fsp_video_probe,
|
||||
};
|
||||
|
||||
static struct pci_device_id fsp_video_supported[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported);
|
||||
@@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: Intel
|
||||
/*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
struct fsp_header *__attribute__((optimize("O0"))) fsp_find_header(void)
|
||||
{
|
||||
/*
|
||||
* This function may be called before the a stack is established,
|
||||
* so special care must be taken. First, it cannot declare any local
|
||||
* variable using stack. Only register variable can be used here.
|
||||
* Secondly, some compiler version will add prolog or epilog code
|
||||
* for the C function. If so the function call may not work before
|
||||
* stack is ready.
|
||||
*
|
||||
* GCC 4.8.1 has been verified to be working for the following codes.
|
||||
*/
|
||||
volatile register u8 *fsp asm("eax");
|
||||
|
||||
/* Initalize the FSP base */
|
||||
fsp = (u8 *)CONFIG_FSP_ADDR;
|
||||
|
||||
/* Check the FV signature, _FVH */
|
||||
if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
|
||||
/* Go to the end of the FV header and align the address */
|
||||
fsp += ((struct fv_header *)fsp)->ext_hdr_off;
|
||||
fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
|
||||
fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
/* Check the FFS GUID */
|
||||
if (fsp &&
|
||||
((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) {
|
||||
/* Add the FFS header size to find the raw section header */
|
||||
fsp += sizeof(struct ffs_file_header);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
if (fsp &&
|
||||
((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
|
||||
/* Add the raw section header size to find the FSP header */
|
||||
fsp += sizeof(struct raw_section);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
return (struct fsp_header *)fsp;
|
||||
}
|
||||
|
||||
void fsp_continue(u32 status, void *hob_list)
|
||||
{
|
||||
post_code(POST_MRC);
|
||||
|
||||
assert(status == 0);
|
||||
|
||||
/* The boot loader main function entry */
|
||||
fsp_init_done(hob_list);
|
||||
}
|
||||
|
||||
void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
|
||||
{
|
||||
struct fsp_config_data config_data;
|
||||
fsp_init_f init;
|
||||
struct fsp_init_params params;
|
||||
struct fspinit_rtbuf rt_buf;
|
||||
struct fsp_header *fsp_hdr;
|
||||
struct fsp_init_params *params_ptr;
|
||||
#ifdef CONFIG_FSP_USE_UPD
|
||||
struct vpd_region *fsp_vpd;
|
||||
struct upd_region *fsp_upd;
|
||||
#endif
|
||||
|
||||
fsp_hdr = fsp_find_header();
|
||||
if (fsp_hdr == NULL) {
|
||||
/* No valid FSP info header was found */
|
||||
panic("Invalid FSP header");
|
||||
}
|
||||
|
||||
config_data.common.fsp_hdr = fsp_hdr;
|
||||
config_data.common.stack_top = stack_top;
|
||||
config_data.common.boot_mode = boot_mode;
|
||||
|
||||
#ifdef CONFIG_FSP_USE_UPD
|
||||
/* Get VPD region start */
|
||||
fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
|
||||
fsp_hdr->cfg_region_off);
|
||||
|
||||
/* Verify the VPD data region is valid */
|
||||
assert(fsp_vpd->sign == VPD_IMAGE_ID);
|
||||
|
||||
fsp_upd = &config_data.fsp_upd;
|
||||
|
||||
/* Copy default data from Flash */
|
||||
memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
|
||||
sizeof(struct upd_region));
|
||||
|
||||
/* Verify the UPD data region is valid */
|
||||
assert(fsp_upd->terminator == UPD_TERMINATOR);
|
||||
#endif
|
||||
|
||||
memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
|
||||
|
||||
/* Override any configuration if required */
|
||||
fsp_update_configs(&config_data, &rt_buf);
|
||||
|
||||
memset(¶ms, 0, sizeof(struct fsp_init_params));
|
||||
params.nvs_buf = nvs_buf;
|
||||
params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
|
||||
params.continuation = (fsp_continuation_f)fsp_asm_continuation;
|
||||
|
||||
init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
|
||||
params_ptr = ¶ms;
|
||||
|
||||
post_code(POST_PRE_MRC);
|
||||
|
||||
/* Load GDT for FSP */
|
||||
setup_fsp_gdt();
|
||||
|
||||
/*
|
||||
* Use ASM code to ensure the register value in EAX & EDX
|
||||
* will be passed into fsp_continue
|
||||
*/
|
||||
asm volatile (
|
||||
"pushl %0;"
|
||||
"call *%%eax;"
|
||||
".global fsp_asm_continuation;"
|
||||
"fsp_asm_continuation:;"
|
||||
"movl 4(%%esp), %%eax;" /* status */
|
||||
"movl 8(%%esp), %%edx;" /* hob_list */
|
||||
"jmp fsp_continue;"
|
||||
: : "m"(params_ptr), "a"(init)
|
||||
);
|
||||
|
||||
/*
|
||||
* Should never get here.
|
||||
* Control will continue from fsp_continue.
|
||||
* This line below is to prevent the compiler from optimizing
|
||||
* structure intialization.
|
||||
*
|
||||
* DO NOT REMOVE!
|
||||
*/
|
||||
init(¶ms);
|
||||
}
|
||||
|
||||
u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
|
||||
{
|
||||
fsp_notify_f notify;
|
||||
struct fsp_notify_params params;
|
||||
struct fsp_notify_params *params_ptr;
|
||||
u32 status;
|
||||
|
||||
if (!fsp_hdr)
|
||||
fsp_hdr = (struct fsp_header *)fsp_find_header();
|
||||
|
||||
if (fsp_hdr == NULL) {
|
||||
/* No valid FSP info header */
|
||||
panic("Invalid FSP header");
|
||||
}
|
||||
|
||||
notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
|
||||
params.phase = phase;
|
||||
params_ptr = ¶ms;
|
||||
|
||||
/*
|
||||
* Use ASM code to ensure correct parameter is on the stack for
|
||||
* FspNotify as U-Boot is using different ABI from FSP
|
||||
*/
|
||||
asm volatile (
|
||||
"pushl %1;" /* push notify phase */
|
||||
"call *%%eax;" /* call FspNotify */
|
||||
"addl $4, %%esp;" /* clean up the stack */
|
||||
: "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
|
||||
);
|
||||
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user