|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH for 4-21 v4] xen/riscv: identify specific ISA supported by cpu
On 05.02.2025 20:00, Oleksii Kurochko wrote:
> On 2/4/25 12:47 PM, Jan Beulich wrote:
>> On 04.02.2025 08:20, Oleksii Kurochko wrote:
>>> +static int __init riscv_isa_parse_string(const char *isa,
>>> + unsigned long *out_bitmap)
>>> +{
>>> + if ( (isa[0] != 'r') && (isa[1] != 'v') )
>>> + return -EINVAL;
>>> +
>>> +#if defined(CONFIG_RISCV_32)
>>> + if ( isa[2] != '3' && isa[3] != '2' )
>>> + return -EINVAL;
>>> +#elif defined(CONFIG_RISCV_64)
>>> + if ( isa[2] != '6' && isa[3] != '4' )
>>> + return -EINVAL;
>>> +#else
>>> +# error "unsupported RISC-V bitness"
>>> +#endif
>>> +
>>> + isa += 4;
>>> +
>>> + while ( *isa )
>>> + {
>>> + const char *ext = isa++;
>>> + const char *ext_end = isa;
>>> + bool ext_err = false;
>>> +
>>> + switch ( *ext )
>>> + {
>>> + case 'x':
>>> + printk_once("Vendor extensions are ignored in riscv,isa\n");
>>> + /*
>>> + * To skip an extension, we find its end.
>>> + * As multi-letter extensions must be split from other
>>> multi-letter
>>> + * extensions with an "_", the end of a multi-letter extension
>>> will
>>> + * either be the null character or the "_" at the start of the
>>> next
>>> + * multi-letter extension.
>>> + */
>>> + for ( ; *isa && *isa != '_'; ++isa )
>>> + ;
>>> + ext_err = true;
>>> + break;
>> I was wondering why ext_end isn't updated here. Looks like that's because
>> ext_err is set to true, and the checking below the switch looks at ext_err
>> first. That's technically fine, but - to me at least - a little confusing.
>> Could setting ext_end to NULL take the role of ext_err? For the code here
>> this would then immediately clarify why ext_end isn't otherwise maintained.
>> (The "err" in the name is also somewhat odd: The log message above says
>> "ignored", and that's what the code below also does. There's nothing
>> error-ish in here; in fact there may be nothing wrong about the specific
>> vendor extension we're ignoring.)
>
> IIUC, your suggestion is to use instead of "ext_err = true" -> "ext_end =
> NULL".
Yes.
>>> + case 's':
>>> + /*
>>> + * Workaround for invalid single-letter 's' & 'u' (QEMU):
>>> + * Before QEMU 7.1 it was an issue with misa to ISA string
>>> + * conversion:
>>> +
>>> *https://patchwork.kernel.org/project/qemu-devel/patch/dee09d708405075420b29115c1e9e87910b8da55.1648270894.git.research_trasio@xxxxxxxxxxxx/#24792587
>>> + * Additional details of the workaround on Linux kernel side:
>>> +
>>> *https://lore.kernel.org/linux-riscv/ae93358e-e117-b43d-faad-772c529f846c@xxxxxxxxxxxx/#t
>>> + *
>>> + * No need to set the bit in riscv_isa as 's' & 'u' are
>>> + * not valid ISA extensions. It works unless the first
>>> + * multi-letter extension in the ISA string begins with
>>> + * "Su" and is not prefixed with an underscore.
>>> + */
>>> + if ( ext[-1] != '_' && ext[1] == 'u' )
>>> + {
>>> + ++isa;
>>> + ext_err = true;
>>> + break;
>>> + }
>>> + fallthrough;
>>> + case 'z':
>>> + /*
>>> + * Before attempting to parse the extension itself, we find
>>> its end.
>>> + * As multi-letter extensions must be split from other
>>> multi-letter
>>> + * extensions with an "_", the end of a multi-letter extension
>>> will
>>> + * either be the null character or the "_" at the start of the
>>> next
>>> + * multi-letter extension.
>>> + *
>>> + * Next, as the extensions version is currently ignored, we
>>> + * eliminate that portion. This is done by parsing backwards
>>> from
>>> + * the end of the extension, removing any numbers. This may be
>>> a
>>> + * major or minor number however, so the process is repeated
>>> if a
>>> + * minor number was found.
>>> + *
>>> + * ext_end is intended to represent the first character
>>> *after* the
>>> + * name portion of an extension, but will be decremented to
>>> the last
>>> + * character itself while eliminating the extensions version
>>> number.
>>> + * A simple re-increment solves this problem.
>>> + */
>>> + for ( ; *isa && *isa != '_'; ++isa )
>>> + if ( unlikely(!isalnum(*isa)) )
>>> + ext_err = true;
>>> +
>>> + ext_end = isa;
>>> + if ( unlikely(ext_err) )
>>> + break;
>> This, otoh, is an error. Which probably shouldn't go silently.
>
> It is actually not an error, what it means that if version is mentioned or
> not, so
> (1) rv32..._zbb_zbc
> (2) rv32..._zbb1p2_zbc
>
> For (1), ext_err = true and it means that version isn't mentioned for _zbb
> and after it
> immediately another extension name is going, so there is no any sense in
> ignoring (or parsing) version
> numbers.
> For (2), ext_err = false (because it ends on number ) so we have to ignore
> (or parse) version nubmers.
I don't follow. Why would ext_err be true for (1)? That's a well-formed
specifier, isn't it? rv32..._zbb.zbc (with the last dot meaning a literal
one, unlike the earlier ...) is an example of what would cause ext_err to
be true.
>>> + default:
>>> + /*
>>> + * Things are a little easier for single-letter extensions, as
>>> they
>>> + * are parsed forwards.
>>> + *
>>> + * After checking that our starting position is valid, we need
>>> to
>>> + * ensure that, when isa was incremented at the start of the
>>> loop,
>>> + * that it arrived at the start of the next extension.
>>> + *
>>> + * If we are already on a non-digit, there is nothing to do.
>>> Either
>>> + * we have a multi-letter extension's _, or the start of an
>>> + * extension.
>>> + *
>>> + * Otherwise we have found the current extension's major
>>> version
>>> + * number. Parse past it, and a subsequent p/minor version
>>> number
>>> + * if present. The `p` extension must not appear immediately
>>> after
>>> + * a number, so there is no fear of missing it.
>>> + */
>>> + if ( unlikely(!isalpha(*ext)) )
>>> + {
>>> + ext_err = true;
>>> + break;
>>> + }
>> Like above this also looks to be a situation that maybe better would be
>> left go entirely silently. I even wonder whether you can safely continue
>> the parsing in that case: How do you know whether what follows is indeed
>> the start of an extension name?
>
> Lets consider examples:
> (1) riscv,isa=im
> (2) riscv,isa=i1p2m
> (3) riscv,isa=i1m
>
> (4) riscv,isa=i1_m1
>
> Note: Underscores "_" may be used to separate ISA extensions to improve
> readability
> and to provide disambiguation, e.g., "RV32I2_M2_A2".
>
> (1) isa="i" so ext = "m" => no minor and major version between "i" and "m" =>
> `ext` contains the name
> of the next extension name, thereby we will break a loop and go for
> parsing of the next extension
> which is "m" in this example
> (2) isa="i" => ext="1" => algorithm will go further and will just skip minor
> and major version for
> this extension (1p2 => 1.2 version of extension "i")
> (3) just the same as (2) but will stop earlier as minor version isn't set.
>
> Note: having "_" is legal from specification point of view, but it is illegal
> to use it between single letter
> extension from device tree binding point of view.
> (4) just the same as (2) and (3) but using "_" separator, so the if above
> will just skip "_" and the next
> after "_" is an extension name again.
>
> Considering that "_" is illegal from device tree point of view between single
> letter extensions we should have
> ASSERT(*ext != "_") and then only cases (1) - (3) will be possible to have.
> Probably it also will make a sense to have an array with legal single letter
> extensions and check that *ext is
> in this array.
>
> Interesting that device tree binding doesn't cover this case:
> ```
> Because the "P" extension for Packed SIMD can be confused for the decimal
> point in a version number,
> it must be preceded by an underscore if it follows a number. For example,
> "rv32i2p2" means version
> 2.2 of RV32I, whereas "rv32i2_p2" means version 2.0 of RV32I with version 2.0
> of the P extension.
> ```
> if I correctly interpreted the
> pattern:pattern:^rv(?:64|32)imaf?d?q?c?b?k?j?p?v?h?(?:[hsxz](?:[0-9a-z])+)?(?:_[hsxz](?:[0-9a-z])+)*$
> And it also doesn't support versions for single letter extensions.
> So "rv32i2_m2_a2" or with using "p" is impossible?
> Then riscv_isa_parse_string() could be simplified ( probably when it was
> implemented in Linux kernel they don't have the binding for riscv,isa and they
> just tried to follow RISC-V specification ) for the case of single letter
> extensions (or just keep it as is to be in sync with Linux kernel).
All fine, but what about e.g.
(5) riscv,isa=i?m1
>>> +static void __init riscv_fill_hwcap_from_isa_string(void)
>>> +{
>>> + const struct dt_device_node *cpus = dt_find_node_by_path("/cpus");
>>> + const struct dt_device_node *cpu;
>>> +
>>> + if ( !cpus )
>>> + {
>>> + printk("Missing /cpus node in the device tree?\n");
>>> + return;
>>> + }
>>> +
>>> + dt_for_each_child_node(cpus, cpu)
>>> + {
>>> + DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
>>> + const char *isa;
>>> + unsigned long cpuid;
>>> +
>>> + if ( !dt_device_type_is_equal(cpu, "cpu") )
>>> + continue;
>>> +
>>> + if ( dt_get_cpuid_from_node(cpu, &cpuid) < 0 )
>>> + continue;
>>> +
>>> + if ( dt_property_read_string(cpu, "riscv,isa", &isa) )
>>> + {
>>> + printk("Unable to find \"riscv,isa\" devicetree entry "
>>> + "for DT's cpu%ld node\n", cpuid);
>>> + continue;
>>> + }
>>> +
>>> + for ( unsigned int i = 0; (isa[i] != '\0'); i++ )
>>> + if ( !isdigit(isa[i]) && (isa[i] != '_') && !islower(isa[i]) )
>>> + panic("According to DT binding riscv,isa must be
>>> lowercase\n");
>>> +
>>> + riscv_isa_parse_string(isa, this_isa);
>>> +
>>> + /*
>>> + * In the unpriv. spec is mentioned:
>>> + * A RISC-V ISA is defined as a base integer ISA, which must be
>>> + * present in any implementation, plus optional extensions to
>>> + * the base ISA.
>>> + * What means that isa should contain, at least, I or E thereby
>>> + * this_isa can't be empty too.
>>> + *
>>> + * Ensure that this_isa is not empty, so riscv_isa won't be empty
>>> + * during initialization. This prevents ending up with extensions
>>> + * not supported by one of the CPUs.
>>> + */
>>> + ASSERT(!bitmap_empty(this_isa, RISCV_ISA_EXT_MAX));
>> This is again an assertion on input we consume. Afaict it would actually
>> trigger not only on all kinds of invalid inputs, but on something valid
>> like "rv32e".
>
> In the case of "rv32e" I think it is fine that it will be asserted as in
> riscv_isa_ext[] we don't
> have 'E' extension and thereby riscv_isa_ext[] should be updated properly.
Of course, but that still doesn't want to be by way of ASSERT()ing.
> Could you please explain me again what is wrong with having ASSERT() itself
> for input we consume? If to change that
> to 'if ()' would it be better? Or it should be just moved to
> riscv_isa_parse_string() where this bitmap is filled?
It's very simple: For internal state we maintain, use assertions to check
assumptions you make. For input we get, use other error checking (which may
be panic(), where no better option exists).
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |