Skip to content

Commit 598498e

Browse files
committed
femu/ocssd: refuse a geometry the timing model cannot index
The model stamps and locks fixed-size per-chip arrays in the controller, indexed by channel * lnum_lun + lun, so it is the product that has to fit; lnum_ch=64,lnum_lun=8 indexes 511 entries into an array of 128 and writes into the rest of the controller. Both versions asserted on this during realize, which aborts QEMU over a property the user chose. Refuse the device with a message naming the limits instead. Zero is refused for the same reason: the counts divide the namespace size while the geometry is built, so lnum_ch=0 is a division fault at realize. Version 2.0 also stops building the controller once a namespace has failed.
1 parent d5a5b78 commit 598498e

4 files changed

Lines changed: 43 additions & 17 deletions

File tree

hw/femu/ocssd/oc12.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,15 +1133,6 @@ static int oc12_init_more(FemuCtrl *n)
11331133
c->num_lun = lps->num_lun;
11341134
c->num_pln = lps->num_pln;
11351135

1136-
/*
1137-
* The timing model indexes its chip array by the flat LUN id,
1138-
* ch * num_lun + lun, so it is the product that has to fit. Bounding
1139-
* each axis on its own lets a legal-looking geometry index past the
1140-
* end of the array.
1141-
*/
1142-
assert(c->num_ch <= FEMU_MAX_NUM_CHNLS &&
1143-
c->num_ch * c->num_lun <= FEMU_MAX_NUM_CHIPS);
1144-
11451136
c->num_blk = cpu_to_le16(chnl_blks) / (c->num_lun * c->num_pln);
11461137
c->num_pg = cpu_to_le16(lps->pgs_per_blk);
11471138
c->csecs = cpu_to_le16(lps->sec_size);
@@ -1293,6 +1284,10 @@ static void oc12_init(FemuCtrl *n, NvmeNamespace *ns, Error **errp)
12931284

12941285
int i;
12951286

1287+
if (!oc_timing_geometry_ok(n, errp)) {
1288+
return;
1289+
}
1290+
12961291
NVME_CAP_SET_OC(n->bar.cap, 1);
12971292
oc12_set_ctrl_str(n);
12981293

hw/femu/ocssd/oc20.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,13 +1155,6 @@ static void femu_oc20_init_id_ctrl(FemuCtrl *n, NvmeNamespace *ns,
11551155
uint8_t num_lun = n->oc_params.num_lun;
11561156
uint8_t num_pln = n->oc_params.num_pln;
11571157

1158-
/*
1159-
* The timing model indexes its chip array by the flat LUN id,
1160-
* group * num_lun + punit, so the product is what has to fit.
1161-
*/
1162-
assert(num_ch <= FEMU_MAX_NUM_CHNLS &&
1163-
num_ch * num_lun <= FEMU_MAX_NUM_CHIPS);
1164-
11651158
/*
11661159
* Byte 0: Major Version Number (MJR)
11671160
* - Value 1: OCSSD Revision 1.2
@@ -1486,9 +1479,15 @@ static void oc20_init(FemuCtrl *n, NvmeNamespace *ns, Error **errp)
14861479
{
14871480
(void)ns;
14881481

1482+
if (!oc_timing_geometry_ok(n, errp)) {
1483+
return;
1484+
}
1485+
14891486
NVME_CAP_SET_OC(n->bar.cap, 1);
14901487
oc20_set_ctrl_str(n);
1491-
oc20_init_namespaces(n, errp);
1488+
if (oc20_init_namespaces(n, errp)) {
1489+
return;
1490+
}
14921491

14931492
oc20_init_misc(n);
14941493
}

hw/femu/timing-model/timing.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
#include "../nvme.h"
22

3+
/*
4+
* The per-chip arrays this model locks and stamps are fixed-size members of the
5+
* controller, indexed by the flat LUN id -- channel * num_lun + lun -- so it is
6+
* the product that has to fit. Bounding each axis on its own lets a geometry
7+
* that looks legal stamp past the end of the array and into the rest of the
8+
* controller. The counts also divide the namespace size when the geometry is
9+
* built, so a zero is a division fault at realize rather than a wrong answer.
10+
*/
11+
bool oc_timing_geometry_ok(FemuCtrl *n, Error **errp)
12+
{
13+
unsigned ch = n->oc_params.num_ch;
14+
unsigned lun = n->oc_params.num_lun;
15+
16+
if (!ch || !lun || !n->oc_params.num_pln || !n->oc_params.secs_per_pg ||
17+
!n->oc_params.pgs_per_blk || !n->oc_params.sec_size) {
18+
error_setg(errp, "FEMU ocssd: lnum_ch, lnum_lun, lnum_pln, "
19+
"lsecs_per_pg, lpgs_per_blk and lsec_size must all be "
20+
"greater than zero");
21+
return false;
22+
}
23+
24+
if (ch > FEMU_MAX_NUM_CHNLS || ch * lun > FEMU_MAX_NUM_CHIPS) {
25+
error_setg(errp, "FEMU ocssd: lnum_ch must not exceed %d and "
26+
"lnum_ch * lnum_lun must not exceed %d, got %u and %u",
27+
FEMU_MAX_NUM_CHNLS, FEMU_MAX_NUM_CHIPS, ch, lun);
28+
return false;
29+
}
30+
31+
return true;
32+
}
33+
334
void set_latency(FemuCtrl *n)
435
{
536
if (n->flash_type == TLC) {

hw/femu/timing-model/timing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ int64_t advance_channel_timestamp(FemuCtrl *n, int ch, uint64_t now, int opcode)
77
int64_t advance_chip_timestamp(FemuCtrl *n, int lunid, uint64_t now, int opcode,
88
uint8_t page_type);
99
void set_latency(FemuCtrl *n);
10+
bool oc_timing_geometry_ok(FemuCtrl *n, Error **errp);
1011
#endif

0 commit comments

Comments
 (0)