Changeset View
Standalone View
sys/dev/virtio/mmio/virtio_mmio_cmdline.c
- This file was added.
/*- | |||||
* Copyright (c) 2022 Colin Percival | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/limits.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/rman.h> | |||||
#include <machine/bus.h> | |||||
#include <machine/resource.h> | |||||
#include <dev/virtio/mmio/virtio_mmio.h> | |||||
/* Parse <size>@<baseaddr>:<irq>[:<id>] and add a child. */ | |||||
static void | |||||
parsearg(driver_t *driver, device_t parent, char * arg) | |||||
{ | |||||
jrtc27: No space (same for p below) | |||||
device_t child; | |||||
char * p; | |||||
unsigned long sz; | |||||
unsigned long baseaddr; | |||||
Not Done Inline ActionsUnsure if these should have better types rather than adopting the everything-is-a-long Linux-y approach (and please just call this one size) jrtc27: Unsure if these should have better types rather than adopting the everything-is-a-long Linux-y… | |||||
Done Inline ActionsI would have preferred to use size_t but we don't have any strtosize_t function available. Out the the types we could parse integers into, unsigned long seemed like the best option. cperciva: I would have preferred to use `size_t` but we don't have any strtosize_t function available. | |||||
unsigned long irq; | |||||
unsigned long id; | |||||
/* <size> */ | |||||
sz = strtoul(arg, &p, 0); | |||||
if ((sz == 0) || (sz == ULONG_MAX)) | |||||
goto bad; | |||||
switch (*p) { | |||||
case 'E': case 'e': | |||||
sz <<= 10; | |||||
/* FALLTHROUGH */ | |||||
case 'P': case 'p': | |||||
sz <<= 10; | |||||
/* FALLTHROUGH */ | |||||
case 'T': case 't': | |||||
sz <<= 10; | |||||
/* FALLTHROUGH */ | |||||
case 'G': case 'g': | |||||
sz <<= 10; | |||||
/* FALLTHROUGH */ | |||||
case 'M': case 'm': | |||||
sz <<= 10; | |||||
/* FALLTHROUGH */ | |||||
case 'K': case 'k': | |||||
sz <<= 10; | |||||
p++; | |||||
break; | |||||
} | |||||
Not Done Inline ActionsSurely there's already a routine to do this stuff in the kernel.... imp: Surely there's already a routine to do this stuff in the kernel.... | |||||
Done Inline ActionsI thought there would be, but I couldn't find one! I think *parsing* stuff like this in the kernel is far less common than *printing* it. cperciva: I thought there would be, but I couldn't find one! I think *parsing* stuff like this in the… | |||||
/* @<baseaddr> */ | |||||
if (*p++ != '@') | |||||
goto bad; | |||||
baseaddr = strtoul(p, &p, 0); | |||||
if ((baseaddr == 0) || (baseaddr == ULONG_MAX)) | |||||
goto bad; | |||||
Not Done Inline ActionsULONG_MAX is only an error if errno is ERANGE jrtc27: ULONG_MAX is only an error if errno is ERANGE | |||||
Done Inline ActionsWe don't have errno in the kernel. There's no way to distinguish between an error and a "real" ULONG_MAX in libkern's strtoul... but it doesn't really matter here since a real ULONG_MAX means someone gave us nonsense anyway. cperciva: We don't have errno in the kernel. There's no way to distinguish between an error and a "real"… | |||||
/* :<irq> */ | |||||
if (*p++ != ':') | |||||
goto bad; | |||||
irq = strtoul(p, &p, 0); | |||||
if ((irq == 0) || (irq == ULONG_MAX)) | |||||
goto bad; | |||||
/* Optionally, :<id> */ | |||||
if (*p) { | |||||
if (*p++ != ':') | |||||
goto bad; | |||||
id = strtoul(p, &p, 0); | |||||
if ((id == 0) || (id == ULONG_MAX)) | |||||
goto bad; | |||||
} else { | |||||
id = 0; | |||||
} | |||||
/* Should have reached the end of the string. */ | |||||
if (*p) | |||||
goto bad; | |||||
/* Create the child and assign its resources. */ | |||||
child = BUS_ADD_CHILD(parent, 0, driver->name, id ? id : -1); | |||||
bus_set_resource(child, SYS_RES_MEMORY, 0, baseaddr, sz); | |||||
Not Done Inline Actionschild can be NULL. Also what happens if the same id is given twice? jrtc27: child can be NULL. Also what happens if the same id is given twice? | |||||
Not Done Inline ActionsNo. child can't really be NULL. We're early enough in boot it will always succeed. imp: No. child can't really be NULL. We're early enough in boot it will always succeed. | |||||
bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); | |||||
device_set_driver(child, driver); | |||||
Not Done Inline ActionsThis will work on x86 I believe but not on INTRNG architectures, where the IRQ IDs are virtualised and come from intr_map_irq (normally indirected via acpi/ofw_bus_map_intr). On those architectures you'd have to synthesise something and call the appropriate one for how the platform was booted. This interface is just kind of crap, there's no way to specify which interrupt controller the IRQ is for, what it's polarity is or whether it's level or edge-triggered, all things ACPI and FDT can do. I don't know what Linux does in that case, but as it stands this is horribly broken for most architectures, you'll pick up some random meaningless IRQ, if one even exists with that internal ID. jrtc27: This will work on x86 I believe but not on INTRNG architectures, where the IRQ IDs are… | |||||
Not Done Inline ActionsThis still stands... ideally this would be marked broken for !INTRNG/!x86 as appropriate, otherwise you're building something that's known-broken jrtc27: This still stands... ideally this would be marked broken for !INTRNG/!x86 as appropriate… | |||||
Done Inline Actions
cperciva: > This still stands... ideally this would be marked broken for !INTRNG/!x86 as appropriate… | |||||
return; | |||||
bad: | |||||
printf("Error parsing virtio_mmio parameter: %s\n", arg); | |||||
} | |||||
static void | |||||
vtmmio_cmdline_identify(driver_t *driver, device_t parent) | |||||
{ | |||||
size_t n; | |||||
char name[] = "virtio_mmio.device_XXXX"; | |||||
char * val; | |||||
/* First variable just has its own name. */ | |||||
if ((val = kern_getenv("virtio_mmio.device")) == NULL) | |||||
return; | |||||
parsearg(driver, parent, val); | |||||
freeenv(val); | |||||
/* The rest have _%zu suffixes. */ | |||||
for (n = 1; n <= 9999; n++) { | |||||
sprintf(name, "virtio_mmio.device_%zu", n); | |||||
Not Done Inline ActionsThis is pretty sad. If they're guaranteed sequential you can avoid the upper limit. If not can you iterate over the environment instead? jrtc27: This is pretty sad. If they're guaranteed sequential you can avoid the upper limit. If not can… | |||||
Done Inline ActionsThe upper limit is there to guard against buffer overflow in case someone gives us an insane kernel environment. Note that we stop scanning as soon as we find a name which isn't in the kernel environment; we (normally!) won't scan all the way up to 9999. cperciva: The upper limit is there to guard against buffer overflow in case someone gives us an insane… | |||||
if ((val = kern_getenv(name)) == NULL) | |||||
return; | |||||
parsearg(driver, parent, val); | |||||
freeenv(val); | |||||
} | |||||
} | |||||
static device_method_t vtmmio_cmdline_methods[] = { | |||||
/* Device interface. */ | |||||
DEVMETHOD(device_identify, vtmmio_cmdline_identify), | |||||
DEVMETHOD(device_probe, vtmmio_probe), | |||||
DEVMETHOD_END | |||||
}; | |||||
DEFINE_CLASS_1(virtio_mmio, vtmmio_cmdline_driver, vtmmio_cmdline_methods, | |||||
sizeof(struct vtmmio_softc), vtmmio_driver); | |||||
DRIVER_MODULE(vtmmio_cmdline, nexus, vtmmio_cmdline_driver, 0, 0); |
No space (same for p below)