I forgot to do this in be0e1aaf8f.
This makes us actually install that header into the sysroot, unbreaking
ports like openssl, which previously failed to build with:
"fatal error: Kernel/API/POSIX/winsize.h: No such file or directory"
FreeBSD also makes it available from both sys/ioctl.h and termios.h.
glibc doesn't have the POSIX 2024 tc{set,get}winsize() wrappers yet,
so it doesn't even define it in termios.h at the moment.
LLVM relies on sys/ioctl.h defining struct winsize, so the llvm port and
the zig port failed to build after 312b4440bf.
POSIX requires it to be defined in this header:
https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/termios.h.html
"The <termios.h> header shall define the winsize structure, [...]"
This unbreaks the readline port after 8e34c970b5, which made it
use tc{get,set}winsize() instead of the ioctl.
Previously, it failed to compile with a -Wincompatible-pointer-types
warning:
"expected 'struct winsize *' but argument is of type 'struct winsize *'"
ioctl() takes the argument as a vararg, so it didn't trigger this
warning.
We already store these glPixelStorei() parameters in
m_unpacking_parameters/m_packing_parameters, but didn't do anything
with them.
Setting these parameters is equivalent to offsetting the input/output
texture data pointer by a constant offset.
Proxy textures don't have any image data. They can be used to determine
if a given texture configuration is supported by the implementation.
To support proxy textures in gl_tex_image_2d, we need to tweak the error
handling, since proxy textures don't report an error for unsupported
dimensions:
GL spec 1.5, 3.8.11 Texture State and Proxy State:
"When TexImage3D is executed with target specified as PROXY_TEXTURE_3D,
the three-dimensional proxy state values of the specified
level-of-detail are recomputed and updated. If the image array would
not be supported by TexImage3D called with target set to TEXTURE_3D,
no error is generated, but the proxy width, height, depth,
border width, and component resolutions are set to zero."
(The same also applies to TexImage2D.)
GL spec 1.5, 3.8.11 Texture State and Proxy State:
"Each initial texture array is null (zero width, height, and depth,
zero border width, internal format 1, with the compressed flag
set to FALSE, a zero compressed size, and zero-sized components)."
GL spec 1.5, 3.8.1 Texture Image Specification:
"An image with zero width, height, or depth indicates the null texture."
Previously, retrieving the texture size for an uninitialized texture
would have resulted in a crash. Since Texture2D initially has a null
device image and device images are not allowed to be 0x0 (GPU::Image
VERIFYs that), represent 0x0 textures by having a null device image.
Despite its name, this overlay isn't Linux-specific. It enables the v3d
node next to other things.
Raspberry Pi OS also enables this overlay by default.
This initial version of the driver only supports glClear{,Color,Depth}()
for now.
Since the only publicly available documentation for the VideoCore 3D
architecture (the VideoCore IV 3D Architecture Reference Guide) just
covers an older revision of the VideoCore 3D architecture, I used
Mesa's v3d_packet.xml as a reference to get definitions for the control
records of the VideoCore VII.
To determine which control records need to be generated for a minimal
example, I used Mesa's `V3D_DEBUG` environment variable and set it to
`cl` to make it dump the control lists it generates for a basic OpenGL
test program.
This workaround was erroneously removed in e5cd17eab1. The port was
still picking up my system libsamplerate and therefore failed to build.
This workaround was originally added in adf85c719a
The "64-bit System Address Support for V3" field was added in host
controller version 3.00. Since we only support version 2 and 3 host
controllers, this check was previously never true.
Once we support version 4.x in the future, we likely need to change this
code since Version 4.x introduced a new V4 mode. The exact meaning of
this capabilities register field differs between version 4.00 and 4.10
and later. In version 4.00 it means that both V3 and V4 mode is
supported, while in 4.10 and later it only means that V3 mode is
supported. Support for V4 mode is instead indicated by a separate field
in that version.
Remove the FIXMEs mentioning that 64-bit ADMA2 and the check for it
don't work in QEMU, since 64-bit ADMA2 appears to work fine in QEMU.
Note that 64-bit ADMA2 needs to be explicitly enabled by setting the
`sd-spec-version` `sdhci-pci` device parameter to `3` and `capareg`
to a value that has the "64-bit System Address Support for V3" field
set, like 0x157834B4.
Since we currently don't support devicetree drivers depending on other
devicetree drivers (like the SD host controller driver depending on
the SD power regulators), do this in the platform init function for now.
In the future, we should have proper devicetree drivers for
"regulator-gpio" and "regulator-fixed" and use them to enable the
regulators in the SD host controller driver.
Next to the GPIO controller in the RP1, the Pi 5 also has two GPIO
controllers in the SoC itself. The driver is named after their
devicetree compatible string "brcm,brcmstb-gpio".
I previously didn't notice this was necessary since I always use netboot
to boot the kernel on my Pi 5. When using netboot, the firmware resets
the PHY for us.
Similar to the fan driver, this code now hardcodes the reset pin
number. In the future, it might be a better idea to use the devicetree
for this. Upstream linux and the downstream Raspberry Pi fork
unfortunately have incompatible devicetree bindings, so we will have
choose one of them.
This currently uses the output (enable) override fields in the control
registers. There's also a dedicated Registered IO (RIO) interface
mentioned in the datasheet, which might be a better fit.
We previously accidentally relied on the firmware enabling output pads
for us.
Each GPIO pin in the RP1 has two output enables: one controlled by the
function and one controlled by the pad registers.
This requires us to now use also the pad registers next to the IO bank
registers.
This silences the following warning when building lagom with recent GCC
versions:
"error: writing 1 byte into a region of size 0
[-Werror=stringop-overflow=]"
It only seems to be reported when this function is inlined into
GIFLoader's usage of LzwDecompressor::get_output().
This driver is split into two main .cpp files: DeviceNode.cpp and
V3D.cpp. V3D is the core driver, while DeviceNode.cpp implements the
userspace API that exposes the device a device node (file).
Job submission is currently implemented in a very basic synchronous way
by simply using a Mutex.
The driver is currently not able to cancel or abort any jobs, meaning
that a GPU page fault or hang will currently make the GPU unusable until
rebooting the system. Similarly, EINTRs will also interrupt the code
waiting for job completion, but since we have no way of cancelling the
job, the GPU might be in some undefined state.
This is more useful than passing a reference to the Process, which
can be easily obtained with Process::current(). Only KCOVDevice
previously used this argument.
We will need this new argument in the next commit.
This solves an include cycle that would otherwise be caused by the next
commit:
Thread.h -> ThreadRegisters.h -> AddressSpace.h -> RegionTree.h
-> Random.h -> DeprecatedWaitQueue.h -> Thread.h
(a -> b means a includes b.)
This will be used for GPU virtual address ranges.
Use an explicit Self template parameter instead of just using
`VirtualRangeBase` as argument/return type to make different
subclasses intentionally incompatible with each other.
This will e.g. not allow comparing CPU virtual address ranges with GPU
virtual address ranges.
Don't put expand_to_page_boundaries() in the new base class since
this function assumes a CPU page size of 4K. Additionally, since this
is a static member function, deducing this would not work and we would
have to use CRTP for this function (but we could still use deducing
this for the other ones).
This will allow turning this class into a templated base class.
Don't move expand_to_page_boundaries() inline since it will only be
implemented by one subclass. Additionally, this function uses
page_round_up() from MemoryManager.h. Including this header in
VirtualRange.h would result in a circular include.
This will be used for GPU virtual addresses.
Use an explicit Self template parameter instead of just using
`VirtualAddressBase` as argument/return type to make different
subclasses intentionally incompatible with each other.
This will e.g. not allow comparing CPU virtual addresses with GPU
virtual addresses.
Don't put is_page_aligned() and page_base() in the new base class since
those functions assume a CPU page size of 4K.
`offset` is in bytes, not blocks.
Previously, if `BlockBasedFileSystem::read_block()` attempted to read at
a byte offset that is equal to the block count, `nread` would be 0, so
it wouldn't read any data. This led to a crash because
`BlockBasedFileSystem::read_block()` asserts that the returned byte
count is equal to the number of bytes it requested.
The same bug also applies to the `write()` function.
If `read()` or `write()` was called with an offset that is larger than
the block count, the first parameter to the `min()` call would underflow
to a large value, leading it to always use the second argument.
This makes bare metal serenity systems reachable by their hostname
on my local network, which uses a FRITZ!Box as the router, DNS server,
and DHCP server. It appears to use this DHCP option to add DNS records
for "$hostname" and "$hostname.fritz.box".
This means we now have ethernet support on the Raspberry Pi 5!
The generic Cadence Gigabit Ethernet MAC (GEM) driver only supports the
IP revision built into the RP1 for now, which is 1p09 according to the
datasheet.
Since the RP1 datasheet doesn't provide any detailed documentation about
this IP block, I referenced the AMD Zynq UltraScale+ docs,
as those devices also use the Cadence GEM (but a different version).
Without this commit, latency in the RP1 GEM Ethernet driver is very
high, most likely because we only ever dequeued packets when we hit the
500 ms timeout.
The new (properly synchronized) WaitQueue implementation appears to fix
these problems.
WaitQueue::notify_one() can also be called from code triggered by
deferred calls, like timers. Deferred calls are executed in
exit_trap() after clearing m_in_irq, so this would previously cause this
VERIFY to trip.
This will make us not crash in the next commit if the NetworkTask
timeout deferred call is called during an interrupt context.
Unlike condition variables, our WaitQueue implementation works correctly
without requiring a lock. Users of this overload just need to be sure
that the `should_wake` callback is done in an atomic way.
See https://read.seas.harvard.edu/cs1610/2026/doc/wait-queues/
(the site we used as an implementation reference) for a good explanation
why this is the case, especially the "Synchronization" section.
This allows us to remove a dummy spinlock from the WaitQueue in the
E1000 driver.
The old code had two bugs.
The first bug was caused by us jumping into high memory inside of a C++
function (pre_init). Since the compiler (rightfully so) assumes that
all global variables stay at the same address, it decided to load the
address of g_boot_info (which we pass to init) before we enable the MMU.
This caused the boot_info info argument to point to physical memory.
Normally, this should cause a crash when we try to access this argument
in the kernel itself, since unmap_identity_map() unmaps the
identity map, causing all accesses to low memory to fail.
However, unmap_identity_map() is also broken, since it assumes that all
physical memory is below 0x3f000000 (likely a leftover from when we
only supported the Rasberry Pi 3). This causes it to not unmap the
identity mapping if physical memory is above that address, like on QEMU.
We can easily fix both of these problems if we bootstrap the MMU like
we do on RISC-V. Instead of identity mapping the entire kernel range,
only identity map a single "bootstrap" page that contains the code to
enable the MMU and afterwards jump into virtual memory.
This means that we don't jump into high virtual memory in the middle of
a C++ function, so the compiler can't incorrectly load the g_boot_info
address before the MMU is enabled. Additionally, we only have to unmap
a single page, which is significantly easier than having to unmap some
arbitrary range of physical memory.
This fixes booting on AArch64 systems which have physical memory below
0x3f000000, like the Pi 5.
This regressed in d7f65d06d6.
In PIC mode, `la` is an alias for the `lga` (load global address)
pseudo-instruction, which generates a GOT load.
Since GOT loads aren't necessary in the kernel, use `lla` (load local
address) instead.
This causes GCC and Clang to generate relative loads when accessing
global variables instead of using the global offset table (GOT).
This saves one load for every global variable access.
We unfortunately have to do this by injecting a header into every
source file, since "-fvisibility=hidden" doesn't affect `extern`
declarations.
This additionally means that accessing global variables doesn't
depend on relocations anymore, so pre_init code no longer needs to
access variables through `adjust_by_mapping_base()`.
Next to this, some other small tweaks are required in pre_init because
global variables now have the "correct" addresses while we are still
identity mapped.
Being able to simplify pre_init like this was also the original
motivation for this patch. Clang trunk started optimizing
`adjust_by_mapping_base()` on AArch64 such that it avoids using the GOT
when we attempt to get the address of the global variable in high
memory. As a consequence, we now directly get the address in low memory
on Clang trunk, causing us to redundantly subtract the physical to link
time address offset and end up with an incorrect address.
With this patch, we no longer need to rely on the compiler not doing
such optimizations anymore.
The .got section still exists on non-x86 but only contains 2-3 entries:
- the start of the .dynamic section (I'm not sure why),
- the __stack_chk_guard (only actually used on RISC-V GCC),
- __clz_tab (only on RISC-V GCC).
Additionally, there are some other GOT entries caused by the RISC-V `la`
pseudo-instruction, which will be removed in the next commit.
It's a bit unfortunate that RISC-V GCC still uses the GOT to access the
stack protector (__stack_chk_guard), but that's not under our control.
RISC-V GCC and AArch64 GCC also still contain a .got.plt section, but
none of the other toolchain+architecture combinations.
I forgot to move the E1000E destructor into the E1000 driver in
e22c1f2df8. This should prevent UAFs when the E1000 driver is destroyed
(because initialization failed) after the MDIO handling task was already
spawned.
This prevents use-after-frees when the watcher tries to access the
inode after it has been freed.
Before fe5ca6ca27, the inode was correctly stored as a WeakPtr.
For some reason that commit changed it to a raw reference instead.
(This was before 11eee67b85, so before the old WeakPtr was renamed to
LockWeakPtr. Inode derives from LockWeakable, so we unfortunately have
to use LockWeakPtr here.)
We have to add a new WatchDescription member to store the
InodeIdentifier for the inode so that we can still remove the
inode_to_watches entry for inodes that have already been freed.
We otherwise can lose interrupts that occurred between setting the
interrupt mask bits and registering the interrupt handler and therefore
enabling its interrupt.
This was added in 97664fad60 without a reason. Reading the interrupt
cause here shouldn't have any benefit. It only means that we can
potentially acknowledge an interrupt without actually handling it.
We don't support jumbo frames, so the MTU is 1500. This means a buffer
size of 2048 is enough.
The motivation for this change is that the I211 requires using a
different register field for buffer sizes above 2048.
The underlying memory regions are currently contiguous, so we
alternatively could just use the regions' physical page 0 address and
add buffer_size * i to it.
This and the old approach will however work with non-contiguous regions
(under the constraint that PAGE_SIZE % buffer_size == 0),
if we decided to use scatter-gather transfers here in the future.
Unlike MSI-X, MSI vectors can't always be masked (it's an optional
feature that we don't even support), so we shouldn't enable it
immediately after reserving interrupt numbers.
This function will already be called by `Device::enable_interrupt()`
which in turn will be called by `PCI::IRQHandler::enable()`.
For MSI-X, this function unmasks the vector.
This removes quite a bit of duplicate code.
The only real remaining difference between the E1000 driver and its
subclassed E1000E driver is that the E1000E driver supports MDIO
access.
Keeping track of hardware differences in a m_hardware_features member
seems better than adding new subclasses.
I missed in 84d1758de3 that setup_link() already sets the SLU bit.
It seems like unconditionally setting it is fine:
"Software must set the Set Link Up bit in the Device Control register
(CTRL.SLU) before the MAC recognizes the link indication from the PHY
and can consider the link to be up."
(82574 datasheet, 3.2.3.1.1)
This doesn't seem to suggest that we should only set it after the PHY
says that the link is up.
The datasheet(s) say these receive address filter registers are
preloaded with the MAC address of the controller.
This means we don't need to access the EEPROM (or NVM on newer
controllers).
This fixes building the Jakt toolchain after c717b91897 added a
subdirectory in AK: AK/Math.
Previously, the "build jakt support libs" step failed because Jakt's
DirectoryIterator didn't treat symlinks to directories as directories.
This is the "intended" way to use the IRQHandler class.
This will allow us to support MSIs, since that requires calling the
`reserve_irqs()` method from the PCI::Device class.
The (PCI::)IRQHandler constructor needs the interrupt number, so we
can't call its constructor until we know the interrupt number, for which
we need a class deriving from PCI::Device.
Instead of only taking the lower bound of interrupt numbers into
account, we should also check that the interrupt number is below the
GSI end of the interrupt controller.
Previously, this incorrect check caused us to have all interrupts be
associated with the first IOAPIC, if the first IOAPIC is responsible
for the lower interrupt numbers.
ACPI global system interrupts (GSIs) are a global interrupt number
namespace that is mapped to interrupt controller-relative interrupt
numbers. To do so, we need to subtract gsi_base() from them.
We previously didn't do so in some places in the IOAPIC driver.
This function is only implemented by the PIC to return the value of its
ISR register. Since the previous commit, the only user of this function
(PICSpuriousInterruptHandler) refers to it directly using the PIC class,
so we don't need to implement it in the general IRQController class
anymore (even then we could have casted the IRQController to a PIC).
Previously, this class incorrectly had its m_responsible_irq_controller
temporarily set to the I/O APIC if the PICs were disabled and the
spurious interrupt handler only was registered to catch any stray
spurious interrupts that may still be caused by the PICs on buggy x86
systems.
Additionally, we can now remove one overload of the
get_responsible_irq_controller() function since we removed the only
user of it.
This class is now only used by the PIC.
Spurious interrupt vectors that can maybe actually not be spurious
are only a thing on x86 due to legacy reasons, so we don't need this
class on non-x86.
Both AArch64's and RISC-V's interrupt controller do also have a way
to communicate that there is no interrupt pending (anymore), but we
don't need a special class to handle this for these architectures,
so remove FIXMEs mentioning that SpuriousInterruptHandler should also
be used for them.
The old SpuriousInterruptHandler only works with PICs.
It assumes that it always has a parent IRQController, which isn't the
case for APICs (APICs aren't represented by an IRQController).
This didn't cause a crash because get_responsible_irq_controller()'s
logic of determining the correct IRQController is broken at the moment
and always returns the first I/O APIC. This bug will be fixed by a
future commit.
Additionally, SpuriousInterruptHandler always tries to use
IRQController::get_isr() in its handle_interrupt() function, which
only the PIC class implements.
We do support retrieving PCIe host controllers from the devicetree
on AArch64 and RISC-V now.
The microvm devicetree doesn't seem to be intended to be used by the OS,
only for EDK II. We seem to be supposed to use an AML interpreter to
call the _CBA method.
These boot info members are set to null if the prekernel didn't find
SMBIOS tables for us.
SMBIOS tables will most likely never be at physical address zero.
The 32-bit SMBIOS case was missing a null check. This caused KUBSAN
to trigger if neither a 64-bit SMBIOS nor a 32-bit SMBIOS configuration
table was present.