mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-26 22:52:34 +02:00
Refine Flash Component descriptor handling
Possible values as well as encodings have changed in newer chipsets as follows. - Pre-PCH (i.e. ICH) chipsets had a maximum frequency of 33 MHz for all operations - Since Cougar Point the chipsets support dual output fast reads (encoded in bit 30). - Flash component density encoding has changed from 3 to 4 bits with Lynx Point, currently allowing for up to 64 MB chips. Corresponding to flashrom svn r1843. Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at> Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
This commit is contained in:
parent
9e3a6984da
commit
2ba9f6ebe5
@ -45,14 +45,16 @@
|
||||
#define min(a, b) (a < b) ? a : b
|
||||
#endif
|
||||
|
||||
void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity)
|
||||
void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity, bool print_vcl)
|
||||
{
|
||||
print(verbosity, "BES=0x%x, ", (reg_val & VSCC_BES) >> VSCC_BES_OFF);
|
||||
print(verbosity, "WG=%d, ", (reg_val & VSCC_WG) >> VSCC_WG_OFF);
|
||||
print(verbosity, "WSR=%d, ", (reg_val & VSCC_WSR) >> VSCC_WSR_OFF);
|
||||
print(verbosity, "WEWS=%d, ", (reg_val & VSCC_WEWS) >> VSCC_WEWS_OFF);
|
||||
print(verbosity, "EO=0x%x, ", (reg_val & VSCC_EO) >> VSCC_EO_OFF);
|
||||
print(verbosity, "VCL=%d\n", (reg_val & VSCC_VCL) >> VSCC_VCL_OFF);
|
||||
print(verbosity, "EO=0x%x", (reg_val & VSCC_EO) >> VSCC_EO_OFF);
|
||||
if (print_vcl)
|
||||
print(verbosity, ", VCL=%d", (reg_val & VSCC_VCL) >> VSCC_VCL_OFF);
|
||||
print(verbosity, "\n");
|
||||
}
|
||||
|
||||
#define getFCBA(cont) (((cont)->FLMAP0 << 4) & 0x00000ff0)
|
||||
@ -64,7 +66,7 @@ void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity)
|
||||
void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc)
|
||||
{
|
||||
prettyprint_ich_descriptor_content(&desc->content);
|
||||
prettyprint_ich_descriptor_component(desc);
|
||||
prettyprint_ich_descriptor_component(cs, desc);
|
||||
prettyprint_ich_descriptor_region(desc);
|
||||
prettyprint_ich_descriptor_master(&desc->master);
|
||||
#ifdef ICH_DESCRIPTORS_FROM_DUMP
|
||||
@ -98,28 +100,97 @@ void prettyprint_ich_descriptor_content(const struct ich_desc_content *cont)
|
||||
msg_pdbg2("\n");
|
||||
}
|
||||
|
||||
void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc)
|
||||
static const char *pprint_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx)
|
||||
{
|
||||
if (idx > 1) {
|
||||
msg_perr("Only ICH SPI component index 0 or 1 are supported yet.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (desc->content.NC == 0 && idx > 0)
|
||||
return "unused";
|
||||
|
||||
static const char * const size_str[] = {
|
||||
"512 kB", /* 0000 */
|
||||
"1 MB", /* 0001 */
|
||||
"2 MB", /* 0010 */
|
||||
"4 MB", /* 0011 */
|
||||
"8 MB", /* 0100 */
|
||||
"16 MB", /* 0101 */ /* Maximum up to Lynx Point (excl.) */
|
||||
"32 MB", /* 0110 */
|
||||
"64 MB", /* 0111 */
|
||||
};
|
||||
|
||||
switch (cs) {
|
||||
case CHIPSET_ICH8:
|
||||
case CHIPSET_ICH9:
|
||||
case CHIPSET_ICH10:
|
||||
case CHIPSET_5_SERIES_IBEX_PEAK:
|
||||
case CHIPSET_6_SERIES_COUGAR_POINT:
|
||||
case CHIPSET_7_SERIES_PANTHER_POINT: {
|
||||
uint8_t size_enc;
|
||||
if (idx == 0) {
|
||||
size_enc = desc->component.old.comp1_density;
|
||||
} else {
|
||||
size_enc = desc->component.old.comp2_density;
|
||||
}
|
||||
if (size_enc > 5)
|
||||
return "reserved";
|
||||
return size_str[size_enc];
|
||||
}
|
||||
case CHIPSET_8_SERIES_LYNX_POINT:
|
||||
case CHIPSET_8_SERIES_LYNX_POINT_LP:
|
||||
case CHIPSET_8_SERIES_WELLSBURG: {
|
||||
uint8_t size_enc;
|
||||
if (idx == 0) {
|
||||
size_enc = desc->component.new.comp1_density;
|
||||
} else {
|
||||
size_enc = desc->component.new.comp2_density;
|
||||
}
|
||||
if (size_enc > 7)
|
||||
return "reserved";
|
||||
return size_str[size_enc];
|
||||
}
|
||||
case CHIPSET_ICH_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *pprint_freq(enum ich_chipset cs, uint8_t value)
|
||||
{
|
||||
static const char * const freq_str[8] = {
|
||||
"20 MHz", /* 000 */
|
||||
"33 MHz", /* 001 */
|
||||
"reserved", /* 010 */
|
||||
"reserved", /* 011 */
|
||||
"50 MHz", /* 100 */
|
||||
"50 MHz", /* 100 */ /* New since Ibex Peak */
|
||||
"reserved", /* 101 */
|
||||
"reserved", /* 110 */
|
||||
"reserved" /* 111 */
|
||||
};
|
||||
static const char * const size_str[8] = {
|
||||
"512 kB", /* 000 */
|
||||
" 1 MB", /* 001 */
|
||||
" 2 MB", /* 010 */
|
||||
" 4 MB", /* 011 */
|
||||
" 8 MB", /* 100 */
|
||||
" 16 MB", /* 101 */
|
||||
"reserved", /* 110 */
|
||||
"reserved", /* 111 */
|
||||
};
|
||||
|
||||
switch (cs) {
|
||||
case CHIPSET_ICH8:
|
||||
case CHIPSET_ICH9:
|
||||
case CHIPSET_ICH10:
|
||||
if (value > 1)
|
||||
return "reserved";
|
||||
case CHIPSET_5_SERIES_IBEX_PEAK:
|
||||
case CHIPSET_6_SERIES_COUGAR_POINT:
|
||||
case CHIPSET_7_SERIES_PANTHER_POINT:
|
||||
case CHIPSET_8_SERIES_LYNX_POINT:
|
||||
case CHIPSET_8_SERIES_LYNX_POINT_LP:
|
||||
case CHIPSET_8_SERIES_WELLSBURG:
|
||||
return freq_str[value];
|
||||
case CHIPSET_ICH_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void prettyprint_ich_descriptor_component(enum ich_chipset cs, const struct ich_descriptors *desc)
|
||||
{
|
||||
|
||||
msg_pdbg2("=== Component Section ===\n");
|
||||
msg_pdbg2("FLCOMP 0x%08x\n", desc->component.FLCOMP);
|
||||
@ -127,24 +198,21 @@ void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc)
|
||||
msg_pdbg2("\n");
|
||||
|
||||
msg_pdbg2("--- Details ---\n");
|
||||
msg_pdbg2("Component 1 density: %s\n",
|
||||
size_str[desc->component.comp1_density]);
|
||||
msg_pdbg2("Component 1 density: %s\n", pprint_density(cs, desc, 0));
|
||||
if (desc->content.NC)
|
||||
msg_pdbg2("Component 2 density: %s\n",
|
||||
size_str[desc->component.comp2_density]);
|
||||
msg_pdbg2("Component 2 density: %s\n", pprint_density(cs, desc, 1));
|
||||
else
|
||||
msg_pdbg2("Component 2 is not used.\n");
|
||||
msg_pdbg2("Read Clock Frequency: %s\n",
|
||||
freq_str[desc->component.freq_read]);
|
||||
msg_pdbg2("Read ID and Status Clock Freq.: %s\n",
|
||||
freq_str[desc->component.freq_read_id]);
|
||||
msg_pdbg2("Write and Erase Clock Freq.: %s\n",
|
||||
freq_str[desc->component.freq_write]);
|
||||
msg_pdbg2("Fast Read is %ssupported.\n",
|
||||
desc->component.fastread ? "" : "not ");
|
||||
if (desc->component.fastread)
|
||||
msg_pdbg2("Read Clock Frequency: %s\n", pprint_freq(cs, desc->component.common.freq_read));
|
||||
msg_pdbg2("Read ID and Status Clock Freq.: %s\n", pprint_freq(cs, desc->component.common.freq_read_id));
|
||||
msg_pdbg2("Write and Erase Clock Freq.: %s\n", pprint_freq(cs, desc->component.common.freq_write));
|
||||
msg_pdbg2("Fast Read is %ssupported.\n", desc->component.common.fastread ? "" : "not ");
|
||||
if (desc->component.common.fastread)
|
||||
msg_pdbg2("Fast Read Clock Frequency: %s\n",
|
||||
freq_str[desc->component.freq_fastread]);
|
||||
pprint_freq(cs, desc->component.common.freq_fastread));
|
||||
if (cs > CHIPSET_6_SERIES_COUGAR_POINT)
|
||||
msg_pdbg2("Dual Output Fast Read Support: %sabled\n",
|
||||
desc->component.new.dual_output ? "dis" : "en");
|
||||
if (desc->component.FLILL == 0)
|
||||
msg_pdbg2("No forbidden opcodes.\n");
|
||||
else {
|
||||
@ -273,7 +341,7 @@ static void prettyprint_ich_descriptor_straps_56_pciecs(uint8_t conf, uint8_t of
|
||||
msg_pdbg2("PCI Express Port Configuration Strap %d: ", off+1);
|
||||
|
||||
off *= 4;
|
||||
switch(conf){
|
||||
switch (conf){
|
||||
case 0:
|
||||
msg_pdbg2("4x1 Ports %d-%d (x1)", 1+off, 4+off);
|
||||
break;
|
||||
@ -630,7 +698,7 @@ void prettyprint_ich_descriptor_upper_map(const struct ich_desc_upper_map *umap)
|
||||
msg_pdbg2(" "); /* indention */
|
||||
prettyprint_rdid(jid);
|
||||
msg_pdbg2(" "); /* indention */
|
||||
prettyprint_ich_reg_vscc(vscc, 0);
|
||||
prettyprint_ich_reg_vscc(vscc, 0, false);
|
||||
}
|
||||
msg_pdbg2("\n");
|
||||
}
|
||||
@ -723,29 +791,57 @@ int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struc
|
||||
#else /* ICH_DESCRIPTORS_FROM_DUMP */
|
||||
|
||||
/** Returns the integer representation of the component density with index
|
||||
idx in bytes or 0 if a correct size can not be determined. */
|
||||
int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx)
|
||||
\em idx in bytes or -1 if the correct size can not be determined. */
|
||||
int getFCBA_component_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx)
|
||||
{
|
||||
uint8_t size_enc;
|
||||
|
||||
switch(idx) {
|
||||
case 0:
|
||||
size_enc = desc->component.comp1_density;
|
||||
break;
|
||||
case 1:
|
||||
if (desc->content.NC == 0)
|
||||
return 0;
|
||||
size_enc = desc->component.comp2_density;
|
||||
break;
|
||||
default:
|
||||
if (idx > 1) {
|
||||
msg_perr("Only ICH SPI component index 0 or 1 are supported yet.\n");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (size_enc > 5) {
|
||||
msg_perr("Density of ICH SPI component with index %d is invalid. Encoded density is 0x%x.\n",
|
||||
idx, size_enc);
|
||||
|
||||
if (desc->content.NC == 0 && idx > 0)
|
||||
return 0;
|
||||
|
||||
uint8_t size_enc;
|
||||
uint8_t size_max;
|
||||
|
||||
switch (cs) {
|
||||
case CHIPSET_ICH8:
|
||||
case CHIPSET_ICH9:
|
||||
case CHIPSET_ICH10:
|
||||
case CHIPSET_5_SERIES_IBEX_PEAK:
|
||||
case CHIPSET_6_SERIES_COUGAR_POINT:
|
||||
case CHIPSET_7_SERIES_PANTHER_POINT:
|
||||
if (idx == 0) {
|
||||
size_enc = desc->component.old.comp1_density;
|
||||
} else {
|
||||
size_enc = desc->component.old.comp2_density;
|
||||
}
|
||||
size_max = 5;
|
||||
break;
|
||||
case CHIPSET_8_SERIES_LYNX_POINT:
|
||||
case CHIPSET_8_SERIES_LYNX_POINT_LP:
|
||||
case CHIPSET_8_SERIES_WELLSBURG:
|
||||
if (idx == 0) {
|
||||
size_enc = desc->component.new.comp1_density;
|
||||
} else {
|
||||
size_enc = desc->component.new.comp2_density;
|
||||
}
|
||||
size_max = 7;
|
||||
break;
|
||||
case CHIPSET_ICH_UNKNOWN:
|
||||
default:
|
||||
msg_pwarn("Density encoding is unknown on this chipset.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (size_enc > size_max) {
|
||||
msg_perr("Density of ICH SPI component with index %d is invalid."
|
||||
"Encoded density is 0x%x while maximum allowed is 0x%x.\n",
|
||||
idx, size_enc, size_max);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (1 << (19 + size_enc));
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
||||
#define ICH_FREG_BASE(flreg) (((flreg) << 12) & 0x01fff000)
|
||||
#define ICH_FREG_LIMIT(flreg) (((flreg) >> 4) & 0x01fff000)
|
||||
|
||||
void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity);
|
||||
void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity, bool print_vcl);
|
||||
|
||||
struct ich_desc_content {
|
||||
uint32_t FLVALSIG; /* 0x00 */
|
||||
@ -102,17 +102,44 @@ struct ich_desc_content {
|
||||
struct ich_desc_component {
|
||||
union { /* 0x00 */
|
||||
uint32_t FLCOMP; /* Flash Components Register */
|
||||
/* FLCOMP encoding on various generations:
|
||||
*
|
||||
* Chipset/Generation max_speed dual_output density
|
||||
* [MHz] bits max. bits
|
||||
* ICH8: 33 N/A 5 0:2, 3:5
|
||||
* ICH9: 33 N/A 5 0:2, 3:5
|
||||
* ICH10: 33 N/A 5 0:2, 3:5
|
||||
* Ibex Peak/5: 50 N/A 5 0:2, 3:5
|
||||
* Cougar Point/6: 50 30 5 0:2, 3:5
|
||||
* Patsburg: 50 30 5 0:2, 3:5
|
||||
* Panther Point/7 50 30 5 0:2, 3:5
|
||||
* Lynx Point/8: 50 30 7 0:3, 4:7
|
||||
* Wildcat Point/9: 50 ?? (multi I/O) ? ?:?, ?:?
|
||||
*/
|
||||
struct {
|
||||
uint32_t comp1_density :3,
|
||||
comp2_density :3,
|
||||
:11,
|
||||
uint32_t :17,
|
||||
freq_read :3,
|
||||
fastread :1,
|
||||
freq_fastread :3,
|
||||
freq_write :3,
|
||||
freq_read_id :3,
|
||||
:2;
|
||||
};
|
||||
} common;
|
||||
struct {
|
||||
uint32_t comp1_density :3,
|
||||
comp2_density :3,
|
||||
:11,
|
||||
:13,
|
||||
:2;
|
||||
} old;
|
||||
struct {
|
||||
uint32_t comp1_density :4, /* new since Lynx Point/8 */
|
||||
comp2_density :4,
|
||||
:9,
|
||||
:13,
|
||||
dual_output :1, /* new since Cougar Point/6 */
|
||||
:1;
|
||||
} new;
|
||||
};
|
||||
union { /* 0x04 */
|
||||
uint32_t FLILL; /* Flash Invalid Instructions Register */
|
||||
@ -555,7 +582,7 @@ struct ich_descriptors {
|
||||
void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc);
|
||||
|
||||
void prettyprint_ich_descriptor_content(const struct ich_desc_content *content);
|
||||
void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc);
|
||||
void prettyprint_ich_descriptor_component(enum ich_chipset cs, const struct ich_descriptors *desc);
|
||||
void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc);
|
||||
void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
|
||||
|
||||
@ -568,7 +595,7 @@ int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struc
|
||||
#else /* ICH_DESCRIPTORS_FROM_DUMP */
|
||||
|
||||
int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
|
||||
int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx);
|
||||
int getFCBA_component_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx);
|
||||
|
||||
#endif /* ICH_DESCRIPTORS_FROM_DUMP */
|
||||
#endif /* __ICH_DESCRIPTORS_H__ */
|
||||
|
30
ichspi.c
30
ichspi.c
@ -1737,7 +1737,7 @@ int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen)
|
||||
tmp = mmio_readl(ich_spibar + ICH8_REG_VSCC);
|
||||
msg_pdbg("0xC1: 0x%08x (VSCC)\n", tmp);
|
||||
msg_pdbg("VSCC: ");
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, true);
|
||||
} else {
|
||||
ichspi_bbar = mmio_readl(ich_spibar + ICH9_REG_BBAR);
|
||||
msg_pdbg("0xA0: 0x%08x (BBAR)\n",
|
||||
@ -1747,12 +1747,12 @@ int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen)
|
||||
tmp = mmio_readl(ich_spibar + ICH9_REG_LVSCC);
|
||||
msg_pdbg("0xC4: 0x%08x (LVSCC)\n", tmp);
|
||||
msg_pdbg("LVSCC: ");
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, true);
|
||||
|
||||
tmp = mmio_readl(ich_spibar + ICH9_REG_UVSCC);
|
||||
msg_pdbg("0xC8: 0x%08x (UVSCC)\n", tmp);
|
||||
msg_pdbg("UVSCC: ");
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
|
||||
prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, false);
|
||||
|
||||
tmp = mmio_readl(ich_spibar + ICH9_REG_FPB);
|
||||
msg_pdbg("0xD0: 0x%08x (FPB)\n", tmp);
|
||||
@ -1762,10 +1762,9 @@ int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen)
|
||||
|
||||
msg_pdbg("\n");
|
||||
if (desc_valid) {
|
||||
if (read_ich_descriptors_via_fdo(ich_spibar, &desc) ==
|
||||
ICH_RET_OK)
|
||||
prettyprint_ich_descriptors(CHIPSET_ICH_UNKNOWN,
|
||||
&desc);
|
||||
if (read_ich_descriptors_via_fdo(ich_spibar, &desc) == ICH_RET_OK)
|
||||
prettyprint_ich_descriptors(ich_gen, &desc);
|
||||
|
||||
/* If the descriptor is valid and indicates multiple
|
||||
* flash devices we need to use hwseq to be able to
|
||||
* access the second flash device.
|
||||
@ -1791,8 +1790,21 @@ int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen)
|
||||
"valid. Aborting.\n");
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
hwseq_data.size_comp0 = getFCBA_component_density(&desc, 0);
|
||||
hwseq_data.size_comp1 = getFCBA_component_density(&desc, 1);
|
||||
|
||||
int tmpi = getFCBA_component_density(ich_generation, &desc, 0);
|
||||
if (tmpi < 0) {
|
||||
msg_perr("Could not determine density of flash component %d.\n", 0);
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
hwseq_data.size_comp0 = tmpi;
|
||||
|
||||
tmpi = getFCBA_component_density(ich_generation, &desc, 1);
|
||||
if (tmpi < 0) {
|
||||
msg_perr("Could not determine density of flash component %d.\n", 1);
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
hwseq_data.size_comp1 = tmpi;
|
||||
|
||||
register_opaque_master(&opaque_master_ich_hwseq);
|
||||
} else {
|
||||
register_spi_master(&spi_master_ich9);
|
||||
|
@ -113,7 +113,7 @@ static void usage(char *argv[], char *error)
|
||||
"where <image file name> points to an image of the contents of the SPI flash.\n"
|
||||
"In case the image is really in descriptor mode %s\n"
|
||||
"will pretty print some of the contained information.\n"
|
||||
"To also print the data stored in the descriptor strap you have to indicate\n"
|
||||
"To also print the data stored in the descriptor straps you have to indicate\n"
|
||||
"the chipset series with the '-c' parameter and one of the possible arguments:\n"
|
||||
"\t- \"ich8\",\n"
|
||||
"\t- \"ich9\",\n"
|
||||
@ -121,6 +121,7 @@ static void usage(char *argv[], char *error)
|
||||
"\t- \"5\" or \"ibex\" for Intel's 5 series chipsets,\n"
|
||||
"\t- \"6\" or \"cougar\" for Intel's 6 series chipsets,\n"
|
||||
"\t- \"7\" or \"panther\" for Intel's 7 series chipsets.\n"
|
||||
"\t- \"8\" or \"lynx\" for Intel's 8 series chipsets.\n"
|
||||
"If '-d' is specified some regions such as the BIOS image as seen by the CPU or\n"
|
||||
"the GbE blob that is required to initialize the GbE are also dumped to files.\n",
|
||||
argv[0], argv[0]);
|
||||
@ -198,6 +199,9 @@ int main(int argc, char *argv[])
|
||||
else if ((strcmp(csn, "7") == 0) ||
|
||||
(strcmp(csn, "panther") == 0))
|
||||
cs = CHIPSET_7_SERIES_PANTHER_POINT;
|
||||
else if ((strcmp(csn, "8") == 0) ||
|
||||
(strcmp(csn, "lynx") == 0))
|
||||
cs = CHIPSET_8_SERIES_LYNX_POINT;
|
||||
}
|
||||
|
||||
ret = read_ich_descriptors_from_dump(buf, len, &desc);
|
||||
|
Loading…
x
Reference in New Issue
Block a user