#Techflash OS
1 messages · Page 2 of 1
in fact
you could have that be a macro
ROUND_UP_TO_PAGE_ALIGNMENT or something
so its obvious what its doing
#define ALIGN_PAGE(x) ((x + 4095) / 4096)
?
yeah ig
alright, testing it now
also its lazy to have your small block allocator just allocate pages
hugely wasteful
but this will work for now
yeah, I know it is lol, but it works
that's more than I can say about that last one
https://godbolt.org/z/GPTYWzbcq none of em!
not looking to really mess with it until I can verify it works
it is turning the last one into mine but the cmovns surprised me a little and im guessing it has to do with signed division semantics
that's nothing like what mine generated, this is a lot of asm for 1 line, I should check my compiler settings, hang on
ffffffffffe046e5 <malloc>:
ffffffffffe046e5: 55 push %rbp
ffffffffffe046e6: 48 89 e5 mov %rsp,%rbp
ffffffffffe046e9: 48 83 ec 20 sub $0x20,%rsp
ffffffffffe046ed: 48 89 7d e8 mov %rdi,-0x18(%rbp)
ffffffffffe046f1: 48 8b 05 e8 99 00 00 mov 0x99e8(%rip),%rax # ffffffffffe0e0e0 <__stack_chk_guard>
ffffffffffe046f8: 48 89 45 f8 mov %rax,-0x8(%rbp)
ffffffffffe046fc: 31 c0 xor %eax,%eax
ffffffffffe046fe: 48 8b 45 e8 mov -0x18(%rbp),%rax
ffffffffffe04702: 48 05 ff 0f 00 00 add $0xfff,%rax
ffffffffffe04708: 48 c1 e8 0c shr $0xc,%rax
ffffffffffe0470c: 48 89 c7 mov %rax,%rdi
ffffffffffe0470f: e8 68 fa ff ff call ffffffffffe0417c <PMM_Alloc>
ffffffffffe04714: 48 8b 55 f8 mov -0x8(%rbp),%rdx
ffffffffffe04718: 48 2b 15 c1 99 00 00 sub 0x99c1(%rip),%rdx # ffffffffffe0e0e0 <__stack_chk_guard>
ffffffffffe0471f: 74 05 je ffffffffffe04726 <malloc+0x41>
ffffffffffe04721: e8 f7 e2 ff ff call ffffffffffe02a1d <__stack_chk_fail>
ffffffffffe04726: c9 leave
ffffffffffe04702: 48 05 ff 0f 00 00 add $0xfff,%rax
ffffffffffe04708: 48 c1 e8 0c shr $0xc,%rax
nope it did generate it
oh yeah lol it's on a debug build
no wonder
let me build the optimized one
wtf
it literally inlined malloc
lmao
the pains of LTO 😔
let me hunt around and see if I can find it in places that I know it should be
wtf it inlined like, everything
there's no way I'm finding it
pffffft I think it optimized a little too hard
as soon as it boots it just triple faults
that kind of LTO sounds like the kind of thing that everything was already doing in 1965 and then it took the *nix ecosystem until 2047 to reinvent it and proclaim it as a great innovation
yeah
I think it optimized out panic.....
because it looks like it gets decently far in the boot process, then just dies and panic doesn't catch it
and I mean that's certainly possible, but very unlikely
hmmm, yeah I don't like that
(gdb) hb malloc
Function "malloc" not defined.
Make hw breakpoint pending on future shared library load? (y or [n])
huh
it still triple faults
actually, I wonder if the issue is that I'm trying to initialize a region of memory that's super small (500K)
I'll just add a gate that says if the regions is <8M, subtract it's size from the total system memory, and don't initialize it
actually wait, what am I doing, I can just use serial to debug it
alright, I've moved some of the info to my logging function
that way I can actually see what's going on over serial
these sure are some logs
bruh
why is malloc starting at NULL and working it's way up
(it's staying at the same address because that's my malloc tester doing stuff there
that's test #1: essentially void *a = malloc(64); free(a);
later on (in test #2), it's void *a = malloc(64); void *b = malloc(64); free(a);, and leaks 64 bytes (or in this case, 4K)
and that's where it crashes
it starts trampling over something that the system really doesn't like
ohhhh I think I know why
it seems like it only supports 1 region
huh
no
it very much works with multiple
ah, I found why
it's because it just isn't designed to store the base address of the region
it just assumes that everything starts at 0
ok this is going to be very jank
but in theory it now keeps track of the base address of each region
and record a bunch of info that should let us figure out what the base address of any bit in the bitmap
key phrase: in theory
entirely untested
aand page fault
nice
aha
the return statement is.... dereferencing NULL
if (bitmapOff > regions[i].bitmapEnd) {
// it's the previous entry
return regions[i - 1].startAddr;
}
...huh
oh my god is it that fucking NULL bug again I swear to god
I will literally delete GCC from my system if it's that again
hmm
// it's the previous entry
volatile memRegion_t reg = regions[i - 1];
maybe something really is wrong here
sigh.... welp time to add way too many checks to make sure that the most basic functions of the OS aren't falling apart
also totally fuck it up probably because it's 11:44PM and I fell asleep at like... 3AM last night, getting ~5h of sleep
this is going to end very well
.... what
literally how, it's basic math
oh
so the initialization is completely broken
YOO
ok this is like, not actually that exciting, it's still broken
but it's close
oh wait
no it's actually not
how is it getting multipled
for context, the base address of the large region is 0x962000
and it should be straying far from that, since it puts the bitmap there
but hey, it's.... kinda close
actually that address should still be within valid address space
¯_(ツ)_/¯
Have u even figured out what address it's faulting on
huh, yeah the base address my function is returning is correct
Or where the page fault is happening
yeah something is dereferencing NULL
0x0E
not worried about that yet
Not worried about what yet
I need to get the address that the PMM_Alloc function is returning to actually be valid first
somehow it's getting created incorrectly
ok ok a little info on the jankiness that my allocator pulls
when you init a region of memory, you pass in a pointer to the memory, and the size of the memory
it isn't
it's supposed to be returning physical addresses
that's the point
something is becoming very wrong
Right and then what do you do with those physical addresses
I just do I/O on them, I haven't set up paging or anything yet
I just treat memory as separate chunks of contiguous memory that I can allocate blocks from
(these chunks come from the memory map, which we parse and then pass in the valid ones to the init function)
Paging really isn't a "oh maybe I'll do it later" thing tbh
the issue is that this allocator is designed to be handling addresses that start at 0, not at any set value, which I'm trying to hack in
like, this is really horrendous, I fill in one of these for every region I allocate from
typedef struct {
void *startAddr;
void *endAddr;
uint64_t bitmapEnd;
uint64_t numBlks;
} memRegion_t;
3 of these variables essentially give the same info
my current issue: math
return (void *)(i * 4 * 8 + j + baseAddr(i, j));
oh wait
no that's perfectly fine I bet
the issue is this void *addr = (void *)((uint_fast64_t)frame * BLOCK_SIZE);
oh no wait and that fucks up the bitmap
AAA
ok so for the bitmap to work correctly, findFree really does need to return the low value
and then I just need to shoehorn in the base address just before we return
sounds like nothing could possibly break at all from that
(I can already tell that freeing is just not going to work until I find some way to reverse the base address as well)
and since the entire base address logic is based around 2 specific variables from the findFree function, and I need to get them out of the function easily, I'm just gonna dump them in a global variable before we leave, then use them in the caller
.... holy shit
I did it
it's allocating
granted all of the logging means that I'll be dead before it finishes the whopping 9999 iteration long test
but it's allocating
aand malloc test #2 just exploded
fun
see I can't help but notice that both frame and addr are... well wrong
they're both overwriting the bitmap
so that makes sense
the malloc test writes zeros to it
so the PMM just thinks that everything is free because we're trampling over the entire bitmap
or, more precisely, the first 64 bytes of it
repeatedly
ohhhh I never fixed PMM_DeInit
that's why
it tries to de-init the zone with the bitmap
and that doesn't really handle my base addr thing
¯_(ツ)_/¯
I'll just make it die if you call it, and go back to the old pointer hack method of just telling the pmm that the zone with the bitmap doesn't exist
i think it just works now
it doesn't look like it's overwriting the bitmap anymore
sigh but it didn't help
it still returns nonsense
it still just returns the base address + 4 every single time
technically it should be leaking memory every single time, since free doesn't actually work yet
bangs head on desk
I think I'm done with this nonsense for today
It's already almost 1am
I'll pick this up tmrw
2 bugs to fix today:
- find out what in the world the random characters are that are getting printed on the newlines
- find out why the allocator is dead
ok
tons of work has been done
it now page faults by trying to set a bit in the bitmap
I am honestly so confused
idk but I think it's starting to go a little outside the range of the bitmap
well now I can catch the bug when it happens
no idea what to do about it though
the issue is that somehow it's being passed invalid values
well
actually no
it's valid
what
holy shit
it's running
WHAT
NO WAY
👀 it's doing the malloc test
holy shit
I think I did it
NO
wait
YES
THATS GOOD
some ancient error handling killed it
because the bit was being multiplied
so I set it to if it's > 4096, die
but now it triggered because it made it to 4096 allocs! 🥳
I'm gonna turn off all of the debug logging
and the strict error handling that restricts your memory
and re-enable the counter on the malloc tester
and watch in glory as it succeeds
actually wait
this would only end up using the final entry
I should really get free working
it breaks because it uses too much RAM i think
it worked when I only did the "Intentional leak" test
but when I added the "allocate then free" test, it died
uhh crap I think free works a little too well
also I can't comprehend where those stupid characters on the newlines are coming from but I am extremely angry at them
what
😭 it was working
now it's not
bruh I'm so confused
hold up
something's wrong here
actually, 2 things are
- why is it leaking memory in malloc test 1, that's the one where it shouldn't
- how does the pointer reset for malloc test 2, it shouldn't be able to do that
- how is it not leaking memory for malloc test 2
I'm really just chipping away at this allocator aren't I
first I had to hack the initialization to get the address to be page aligned
now it's randomly skipping....
17551360 bytes
oh it literally says that, why did I do that math
oh man it could be worse
on my optiplex it page faults trying to initialize the bitmap
hm
I think i broke the allocator
by allocating tons of huge chunks of memory, it just falls apart
the entire system is barely still running
it can get through exactly 24 iterations
then it dies
suspiciously very close to half of the availible system memory 
(each allocation is 10MB)
and then when i set the availible RAM it 1G, it dies at 49
iiiinteresting.....
I added a bunch of sanity checks to free
so I would imagine if it was failing outright I would know
the weird part is that it keeps going
it just absolutely slows to a crawl
and QEMU is absolutely hammering one of the cores
I'm having PIT troubles again
I'm always having PIT troubles...
I should really just abandon the PIT
I currently don't even try to initialize anything else, and I don't even check if the PIT is even there
it just yeets bytes into the I/O ports, and does no verification that it actually works
welp
looks like I'm implementing ACPI stuff
fun
cool
wait wtf
I swear this is QEMU, not bochs
unless the 1:13AM is actually starting to fuck up my brain and now I can't read the QEMU on the title bar of the window
I don't even have bochs on here
huh, well that's not good
the checksum? so not the "RSDT" string but the actual checksum field?
you cant trust that to be accurate
yup, the checksum field
idk it seems like it's wildly off
maybe I'll just print a warning about it and carry on
I guess some BIOS's just don't implement it
2048 might be your mistake too tbh
but also like
dont bother
yeah I do check it, and then I just print if it's valid or not, I might later implement some logic where like, if something seems REALLY suspiciously wrong, AND the checksum is invalid, don't trust it
but idk, I'll first get it working, then worry about that
nice
ugh
I just went to go test this on real hw
but I forgot that the memory manager somehow page faults on my optiplex
like, it page faults INSTANTLY
the very first write in order to set up the bitmap
good page fault or bad page fault
then it isnt present
dont take this the wrong way but i dont get how youve been working on debugging the same very small component for like a week now
ok but then why does the memory map say it is
you may have some problem in your methodology somewhere
there are two possibilities
- it isnt present
- faulty cpu
i think the former is much more likely
oh I thought you meant faulty RAM for a second I was just gonna say "what I just bought this RAM like 2 months ago"
actually it is possible that this CPU is faulty
it's a Core 2 Duo E8400
why would a page not being present be due to faulty ram
does it boot linux
does it boot windows
how does that mean its faulty
if it boots linux or it boots windows then its your fault
I haven't checked actually
let me try booting into windows xp, one sec
but I doubt the CPU is faulty
more than just the memory management stuff would be going wrong
and probably more than just my OS would start to die (BIOS, bootloader, etc)
there is like a 0.00001% chance its not your fault
I don't even touch anything paging related
the memory map provided by the bootloader lists that address until ~4G after it as being usable memory
ok so if its limine then theres a small chance its a limine bug
what have you done to determine for sure
that youre accessing the address you think you are
because you might not be
assume nothing
- it's not a limine bug because I'm not using limine
- I am, I literally log the entire memory map, and I can see in the panic logs and the logs of the memory map, that CR2 is equal to the starting address of that region of the address space
showe
one sec, it's hanging in the BIOS for some reason
was this that one NT clone kernel?
nope
my mental map of the projects of this server must be all wrong
I remember this one as the one which was written with Bootboot and everyone was lapidating the guy for it
lol, sounds about right
no wonder it doesnt work
- the logs, yes, it is in fact in the memory map
- the panic screen, register of note: CR2 (also RAX, presumably that's where it dereferenced the address from)
I may have asked this before but..
what's that weird blue line?
that's it trying it's hardest to read an image from the initrd
90% sure, let me check the spec
yes, up to a maximum of 16GB
bootboot gives you a lower half identity map? for shame
It is indeed possible that it's a bootboot bug
the x86_64 BIOS version isn't maintained
ah yes the most widely used platform isn't maintained
also dont be getting uppity im not the guy whos been debugging the same 500 lines for the second week straight
yes but I'm not rewriting my kernel to add support for it, not yet
it's big brain time
why not
this clearly doesnt work
no, the x86_64-efi version is
if your kernel is trivial enough that switching the bootloader requires a rewrite then youre not losing much
and unless you want to go fix bootboot
ohh
this is also a good point
well, not an entire rewrite
but I would need to rewrite tons of core stuff
I would need to gut the entire console driver for 1
like what?
why would you need to do that
bootboot and limine's framebuffers cant be that different
its like 500 lines at most
for everything
oh, limine gives you (easy) direct framebuffer access, nvm, it wouldn't be that hard then
as long as it's RGBA
its whatever it tells you it is
yes but my kernel just assumes it's rgba
even bootboot tells you what it is
but it's most commonly rgba and I just never got around to abstracting it to use anything different
anyway that really doesnt sound like you need to change much at all
no, the rest of it is fine, since it's all abstracted around the console driver mostly, very little stuff directly accesses the framebuffer
then use limine
but yeah I get a lot of info from the bootboot struct
lol
oh yeah also the entire kernel.c, and makefile are just heavily modified code from the example C kernel, so I still need to keep the copyright banner
even though there's practically nothing left, so idk if it even still applies
what language is that
stupid thumb
it also means "obtuse" if you're talking about angles
so you're saying that "boot boot boot" could literally mean "stupid obtuse thumb"
thats interesting since in english "obtuse" can mean stupid
well.. yeah. but does that even make sense?
in the literal word for word english translation it does
partially, kind of odd, but not complete nonsense imo
i mean the fact that you are calling a thumb "stupid"
i guess there are some cases it could make sense
well I mean it only makes sense if you cut through your thumb and make an obtuse angle with it
don't laugh but in Low German the word "butt" means "blunt, clumsy"
so it's an Indo-European thing ig
we went from discussion of a bootloader to linguistics
so basically around 3789 characters?
3781, but now it doesn't even compile, because I've started doing stuff with the code itself
so far just commenting out stuff that would be unnecessary for a simple build
well I'll be damned
I've managed to get it to build
while replacing (some, but not all) of the existing bootboot functionality with limine
why would you want to do that
?
to get at least some of it working
I need it working before I can experiment with getting the rest working
currently I'm trying to fix the ISO creation script
i've somehow caused it to go up in flames
let me rephrase
how would you use limine and bootboot together
do you know what a bootloader is?
but you just said
I'm migrating from bootboot to limine
part of the code
part of the kernel code that uses stuff that bootboot provided
not part of bootboot
lol
you didnt really make that clear
"bootboot functionality" i would take as being using bootboots protocol etc
ah, my bad
yeah, i meant as in part of the kernel code that uses the bootboot struct that it provides
heyyy it works!
now I just need to make a config file and it should work
that... doesn't quite look right
this is gonna be fun
no terminal output
it randomly resets
but if I cli; hlt as soon as the kernel start, it properly hangs
- the kernel loads ✅
- the kernel starts executing properly ✅
- literally anything else ❓
-no-shutdown -no-reboot, look at registers
now it doesn't even print anything 😔
hang on, let me try to figure out roughly where it's dying by just dumping a while (true) {} somewhere
aha
it dies extremely early on, trying to initialize SSE
For what do you need SSE in your kernel?
I don't, I just make use of it for performance, because it's a 64-bit kernel, and any AMD64 compatable CPU will have at least SSE 1 & 2
Look up the RIP and use addr2line
no like, QEMU doesn't tell me when it resets, it prints stuff about CPU reset on initial bootup, but it doesn't print anything after that
I suppose I could just use gdbstub though
Your qemu keeps resetting right?
yup
Include -no-shutdown and -no-reboot in your qemu flags
I do
now it doesn't
but like I said, it doesn't print info about what happened to the CPU
so I can't lookup RIP
that's why I'm setting up gdbstub
qemu ?commandline? => info registers
ah, the qemu monitor yeah
yup
too late ig
Thread 1 received signal SIGQUIT, Quit.
0xffffffff8000007a in .NoSSE3 ()
I wonder what's going on there
that's just checking if it supports any of the additional ones
I‘d really suggest not using SSE in the kernel, and just not do any floating point operations
it doesn't actually use anything more than SSE2
why?
it doesn't hurt anything, and since it's already a 64-bit only kernel, there are 0 cases where it's not supported
Its up to you of course, though you‘d always have to save sse states too in context switches
set a breakpoint to your kernel entry then, single step through your code if you want to find out
yeah, this kernel is nowhere near close to being able to run anything in userspace, so that doesn't matter in the slightest yet
if I ever get to that point, and it becomes a problem, then I can just disable it
(and make minor modifications to remove floating point stuff)
you can just avoid using floats, what do you need them for anyway?
¯_(ツ)_/¯
idk
I'm not going to go check rn
but again, I mainly just enable it so that GCC can optimize the code with it
why are you using sse
again, I can't see how that's a bad thing, a little extra overhead for context switches sure, but that's not going to even matter for an extremely long time yet
"it doesnt matter yet" is a bad way to write code
it seems to matter now, as your SSE code is broken anyway seemingly
it isn't
I think limine just enabled it already, since I've seen this same behavior when running under SMP systems, and not properly redirecting the other cores elsewhere (causing it to init SSE twice)
i dont think limine will enable sse
limine doesn‘t afaik
but anyway i would just -mno-sse(1,2...)
huh, idk then
try removing sse, maybe something else is really broken
no, I'm keeping SSE, I'm going to get it working, I don't get why yall are so against it, it's fine
because every single time you go into the kernel you would need to save another 16 or whatever registers
and restore them of course
like i said, "it doesnt matter yet" isnt a good way to go about things
again, when I get to that point, if I see that the performance hit of context switches is genuinely not worth it, then I'll disable it
And between kernel „tasks“ too, not only to some user programs but the entire kernel requires saving those registers
yep any context switch
which would be happening many many times per second
and you are effectively doubling the delay of each one by using sse in the kernel
yeah, I literally don't do any context switching at all yet, and once again, I won't for a while
or maybe even more than doubling
You don‘t get performance really
Right now atleast
saying that is a bit like saying "ill just use the weakest possible material for the walls of my house since its cheaper, ill worry about the roof crushing them under pressure later"
just because you arent doing something now, doesnt mean you cant think about the effects that thing may have
no, it's more like "I'll put up these thin wooden beams as the walls, because there is no roof to crush them, and there won't be for a long time"
yeah that sounds like a bad idea to me
I don‘t think you‘re doing heavy math, making use of SIMD or general FP operations
I actually do a decent amount with floats, not a ton but some
and I've seen in the disassembly before that GCC does a decent amount of optimizations with them
again
Again, why floats in your kernel?
if that becomes a problem, then I'll disable it
Where do you use them?
idk, I know that I do in some places, mainly stuff with logging
logging what
i don't know... I haven't worked on this project for many months, and I just came back to it at most a week ago
I don't remember everything about it
I just know that I DO use floats
and that the performance increase from SSE WAS noticeable on my Core 2 Duo Optiplex from 2007 (my main testing machine)
do you use kvm or tcg?
I use QEMU/KVM for quick tests, but for real hardware, I use that optiplex that I just mentioned above
Ah I see
oh, it looks like limine actually does enable SSE
but the next thing that my code does is to setup serial I/O
and that breaks because conventional memory isn't mapped when it tries to access the BIOS Data Area
so it page faults
HHDM exists in limine, though what do you mean with serial i/o?
like it tries to initialize the serial port
yes
I know
but I try to access the BDA
that way I can get the I/O port from the BIOS
because some map it to something weird
as a fallback if it gives us 0, it just tries 0x3F8
BIOS
are you sure
yes
I am absolutely sure
QEMU loads up SeaBIOS
but yeah I don't do anything limine specific to access it if that's the issue
I just try to read from low memory
Then just use the fallback for now, until you sort out your paging
… if you want to access memory it has to be mapped
So?
in bootboot everything was already mapped
I need to manually map everything in Limine?
Limine has a HHDM (not 100% sure on low mem ident mappings)
Yup
hang on, let me go look up how that works, because I have no idea what hhdm even is
Higher half data mapping
direct map*
oh right
oh so everything is mapped to the higher half of the address space
Yes
sigh that's gonna require a lot of rewriting
every physaddr is mapped to itself + hhdm_offset
is there any hack I can do to just make limine map everything at offset 0, or am I actually going to need to go rewrite anything that uses low memory
Hhdm is the first 4gb of memory identity mapped and possibly any other memory map entries above those 4gb mapped too
you want to do that anyway
or well
you will want to in the future but i guess you dont really care about the future
If you want to have some userspace stuff running in lower memory you can just keep everything in your hhdm and access that
I care about the future if it lines up with what I'm doing right now, or if what I'm doing now will make the future absurdly harder
that's pretty much it
this is the ideal method
crap
I mean yeah I know that userspace stuff goes in lower memory
but I'm talking about really low memory
like the 640K of conventional memory
(where the BDA lives)
limine also maps from 0x1000 onward (virtually)
its good practice to separate them exactly in half
that's what I need access to right now
use hhdm for that
sigh welp time to go rewrite anything that uses it then.....
make sure you use the hhdm offset request
HHDM address is not fixed, maybe keep that in mind
yeah I know
I'm requesting it, then setting the bda pointer address based on it
aand now it jumps to null somewhere
great
after a small amount of testing in GDB, yeah, that should work
(gdb) p/x *(hhdm.response->offset + 0x400)
$8 = 0x3f8
yup, and the bda looks valid
ok cool so it makes it through initThings
now I just need to make TTY_Init() actually set up a TTY
yoo I can make a line
yup, here's a case where I do floating point math
calculating the width and height o the terminal
TTY_Width = ((fb->width / font->width) * 0.885);
TTY_Height = ((fb->height / font->height) * 0.99);
and it dies
because SSE isn't initialized
yup, no other reason that this would have died 0xffffffff80006e93 <+99>: pxor %xmm0,%xmm0
yoo SERIAL TEST!!!!!!!
serial works
and I've gotten SSE working too
alright so serial works
but my crappy framebuffer console doesn't
and the limine terminal stuff is deprecated
what to do.....
as far as I see it, I have 3 options here:
- just use limine terminal and disable the deprecation warnings (will probably end up stuck on an old version of limine then once it does finally get removed)
- fix my broken framebuffer terminal (it's slow, and would make booting take way longer)
- have no early boot console / only serial (panic woul suck balls since it just prints the message down serial, no debugging info)
flanterm
doesn't work for early boot
no allocator
that was my plan all along for after the allocator starts
but currently it doesn't even get that far
bump allocator
wdym, like change my existing allocator to a bump allocator? I don't see how that would help, since it still isn't initialized until later in the boot process
bruh not even flanterm works what
yeah it just dies
I think I'm just gonna need to use limine terminal
I honestly don't know how else I could do this
Flanterm includes one
yeah I probably don't want 2 different allocators
unless I can deactivate the bump allocator as soon as I've verified that the current one works
it just seems like it would cause a ton of issues
You can do that
if i remember correctly the bump allocator just makes use of a compile time arena
just a char array
after an extremely long time of debugging, I've finally done it
Techflash OS finally boots enough on Limine to the point where I can see it die
only took completely rewriting the entire console driver, and having my friend send a perl script to convert a 16-bit VGA font into my custom format (C code)
oh, and fixing a weird bug in my PIC initialization routines
it was reading the address 0x04 rather than the value $0x04
but now, oddly enough, it dies in the ISRHandler
because reading the code at a proper higher-half address causes what I can only assume is a page fault (exception handlers aren't loaded yet (?))
wait no, they are
// Initialize some exception handlers
initExceptions();
// Initialize the physical memory manager
PMM_Init();
but yeah, this is what I got when I tried to check wtf was going on with GDB
Thread 1 received signal SIGQUIT, Quit.
0xffffffff80005259 in ISRHandler (regs=<unavailable>)
at hardware/CPU/interrupts/ISR.c:27
27 void ISRHandler(registers_t* regs) {
(gdb) disas
Dump of assembler code for function ISRHandler:
0xffffffff80005251 <+0>:
Cannot access memory at address 0xffffffff80005251
which is just.... weird
I also don't believe that it's possible the Limine isn't mapping the entire kernel or some BS like that, since it's only 239K
sigh
complicating this EVEN MORE, when I go to load it now on real hardware, Limine just doesn't load
sure, gimme a sec, recording a video
okay
as in the keyboard stops functioning, and the BIOS stays on screen forever, and the only thing I do is turn the PC off
well that's odd
but it doesn't necessarily seem like a Limine issue, but we'll see
alright, video recorded, compressing it so that I can actually send it now
btw I did this by dding an ISO to a USB HDD, I do have some spare CDs to test it like that too, but it would be a pain, because I would need to hijack SATA power from something else if I wanted to hook up an optical drive to this thing
are you sure that enter key even works
yes
it doesn't seem to
the HDD light flashes every time I do
yup
it could boot grub when I was using it in the exact same setup before
I bet if I use an old version that uses GRUB, it would still work
it's exactly how i used to boot my os
well that's certainly odd
first thing it makes me thing you didn't properly install Limine on the drive
how did you install it?
it works fine in QEMU when loading the ISO
and I wrote it to the drive with sudo dd if=bin/TFOS_ISO.iso of=/dev/sdb bs=512 status=progress
well that's not really what i have asked
if i am asking questions it is for a specific reason
you see, you could be loading the QEMU ISO using -cdrom which would go through the eltorito boot path which is different compared from the hdd boot path which the usb drive would take
if you would please answer the questions instead of trying jumping ahead that'd be nice for everyone :)
ah, sorry
but yeah, thats how ^
?
ohhh
sorry, misunderstood, let me go get the script I use
this is the portion which downloads the Limine files, and creates the ISO image
if ! [ -f limine/limine-bios.sys ]; then
wget https://github.com/limine-bootloader/limine/raw/v5.x-branch-binary/limine-bios.sys -O limine/limine-bios.sys
fi
if ! [ -f limine/limine-uefi-cd.bin ]; then
wget https://github.com/limine-bootloader/limine/raw/v5.x-branch-binary/limine-uefi-cd.bin -O limine/limine-uefi-cd.bin
fi
if ! [ -f limine/limine-bios-cd.bin ]; then
wget https://github.com/limine-bootloader/limine/raw/v5.x-branch-binary/limine-bios-cd.bin -O limine/limine-bios-cd.bin
fi
cp -r limine/ isodir/
xorriso -as mkisofs -b limine/limine-bios-cd.bin --no-emul-boot --boot-load-size 4 --boot-info-table --efi-boot limine/limine-uefi-cd.bin --efi-boot-part --efi-boot-image --protective-msdos-label isodir -o bin/TFOS_ISO.iso
ohh, thanks, I'll add that
well, now it boots on real hardware, but the entire reason that I was trying to run it on real hardware (intentionally slow down the TTY so I could see why the scrolling totally broke everything) didn't work. My new implementation is actually moderately good, so it's too fast for me to see what's going on 😔
I guess I could manually step through it with gdb
or you can add explicit delays
it's so early in the boot process (it has just barely initialized the PIC), so it has no timer
then move it later
I can't just move the TTY scrolling routines later.....
but yeah it looks like it's just entirely broken
yeah that.... doesn't quite look right
might just revert to the old code and see if i can fix it
whatever
I'm getting a huge headache
I'm just gonna go to bed
will keeping messing with it tomorrow
but I honestly have no idea what's wrong
why not? just initialize the timer then the terminal
Because this is the very early boot terminal
okay?
Specifically for, we'll, when nothing is initialized yet
whats the point of that
i dont init the terminal until very late(after apic, idt, gdt, tss, pmm, vmm, kernel heap, hpet, apic timer and probably a few others)
Because it literally never even gets to initlizing the memory map yet
It dies somehow because the code isn't mapped
But now is too late
I'm going to bed
see? it's too difficult and complicated, bzt really has a point
oh yeah
if you cannot run it without reading the documentation your software sucks
my bad
time to delete limine-bootloader/limine in shame
just do nothing 1000000000 times
No need for a timer to just delay for some time
that's what I ended up doing lol
well
before reverting the code
it's how I got ^^^
well... it's not "good", but it's at least better than I thought
ah, that's why
it has no ANSI
?
maybe
actually idk
I might just need to move the ANSI and cursor handling into the buffer wrapper rather then
I'm debating just restarting this
I'm also considering doing it in rust this time, I've heard tons of good stuff about it, and seen other people using it in their projects
never actually tried it
but I've heard that it's blazingly fast, and very safe
I would not learn a new language doing osdev
idk
not sure on it yet
I'm for sure restarting the project
not sure on if I want to do it in rust or just stick with C
nothing wrong with restarting, I think its a good idea from time to time. As long as you make use of the previous experience
yup, while in the process of making tfos (this iteration, #3), I learned a lot about my personal style of coding, and gained a decent amount of extra experience with C in general
not letting it all go to waste
I'm definitely archiving this branch so I can reflect on it
and I'm obv keeping the really basic stuff, unrelated to the kernel, like the build system and config files
this isn't going to be a completely new project
just wiping the slate clean for a better foundation
and I'm gonna try to get less ahead of myself this time
like trying to make progress bar on the boot screen before even getting interrupts, or like trying to make a crappy strcmp based shell built into the kernel
although one thing that I might actually keep is the console driver, after a few minor tweaks
it's actually pretty robust
it needs a bit of work, and some bug tests for sure
actually.
now I'm not really sure
ok here's my plan
- definetly save all of this code, as horrible as some of it is, a lot of it is actually pretty useful stuff, and the bad stuff can serve as a good example of what NOT to do
- start learning basic rust, I don't want to get too deep into it yet, since osdev on it is going to be pretty different, since there's no standard library
- start writing the kernel again in rust once I've gotten a good grasp on the fundamentals of rust, and how it can mix with existing C/asm code
the language wasn't the problem
rust will ultimately f rust rate u
more than continuing/rewriting in C would have
yeah, of course, there's nothing wrong with C
I love C
but it sure has it's issues
nothing's stopping you from doing really dumb stuff
(which is ultimately why my OS sucks)
if I can write a nice kernel in dragonfruit anyone can write a nice kernel in C
not a direct issue of C
rather an issue of me doing dumb things and nothing's stopping me
rust could help that
Rust doesn't stop you from dumbly structuring it or dumbly logic bugging it
no, of course not
that's the job of the programmer to catch, and in some cases, static analyzers
but I rarely make massive logic bugs like that, and when I do, I usually catch them
am I saying that never happens? no, of course
but rust could prevent the really stupid stuff
idk
I'm gonna try out rust a bit, if i find that it really doesn't help, then I'll give up
yeah I'm not really a fan of this tbh
I think I'll just use C
but i would preferably like to set up some kind of testing to ensure that I'm not doing really dumb stuff
a dream that I always had was to be able to have something that sits on the other end of the QEMU serial port, and gives the OS things to test
the OS would spit back out the raw results (not just a yes or no, to prevent issues with the OS fucking it up)
and it would give it a list of just random junk to test, like hammer the allocator and make sure it's returning valid addresses, or abuse printf and see if I can get it to fail, etc
obv that won't work for a very long time
but some kind of way to test it for simple issues, from outside of the OS itself would be really nice
ok well what started out as making a new kernel actually turned into making a new build system
as it turns out, good old
./clean.sh; ./all.sh --no-pull; ./usb.sh /dev/sdd just wasn't gonna cut it
so I'm now cramming all of this into a makefile
then write tests
Just use cargo
welp, first steps ig
the makefile is producing a kernel (currently just halts, and it doesn't even get put in the right place for limine to see it)
and it can make an ISO with limine, and do shit with mcopy in order to put all of limine-uefi-cd.bin's files directly on the ISO filesystem to make xorriso happy
first stages of kernel rewrite are beginning
things are much more organized this time
write design docs before working on major components
why?
- its cooler
- it helps u think things through
although i dont follow my own advice and i only did that with my IPC system
but i regret that somewhat
I do that to a limited degree
before I actually start writing code, I'll think of "ok, what exactly am I trying to accomplish with this specific block of code"
I agree thinking things through is good 👍... to a certain extent
I think overly planning stuff out, especially simple stuff isn't necessarily a good idea, it often leads to over engineering what should really just be a simple function
Now that part doesn't always apply, when doing really complicated stuff, then a full step by step plan is a great idea, e.g. memory manager, VFS, etc
for a very beginnings of the boot process (enabling serial for logging, then enabling SSE), like what I'm working on right now, you don't really need much of a plan for that, just a rough outline of what you're trying to accomplish, not a full plan of exactly how you're planning to accomplish them
memory manager doesnt need a big design
apart from maybe how the kernel provides memory to a process
eh, kind of on the edge, depending on how you design it, it could be complicated, or it could not be
but it's one case where a plan does indeed help
i dont really see where you have anywhere to design anything
pmm, vmm, kernel heap
all freelist or allocator of your choice
i beg to differ
well I finally got serial working, and discovered 2 bugs:
- I forgot to add the success case for enabling SSE, so it always fails
- I forgot that it uses KASLR, so it dies trying to load the address of the "failed" string
but yeah now it does this
No SSE Support!! Hanging...
VMM alone does need some thought
just pick a good algorithm and implement it
have u done that
yes
do u have it up somewhere
are you going to try to discredit my statement because of bad code or something
but sure
let me find the file
no im going to judge whether you even know what a vmm is
what is there that i havent done that is nontrivial
file mapping, shared memory, page cache, copy-on-write, page swapping
in no particular order
these are "simple" vmm things
A good vmm is arguably the hardest part of a kernel to write
not as hard as good docs 😔
True
currently fixing up all of the asm so that it can be position independant
and just noticed that my serial code doesn't work
fun
serial!
now I need to plan out some stuff that i should do next, I'm thinking the big ones:
- PMM
- Terminal
- Parse ACPI tables
- SMP
I already had put the SMP request in there for some reason, so I figured why not just print the info
also, my fairly bad plan for handling SMP:
- the secondary CPUs wait, halted, until waken up by an interrupt
- when awakened, they'll check smpFuncAddrs[myCoreNumber] and see if they have anything to do
- if yes, they go do it, NULL out the address, and go back to sleep. if they don't have anything to do, then they go back to sleep instantly
I'll still need to look into if any of that is even possible though
never really handled SMP yet
You're supposed to schedule threads on the cores
I let the scheduler schedule tasks after initializing the kernel, so exactly what you do on your main core you do on the other cores too
yeah I've never really handled scheduling either, my kernel has always just been a giant block of initializing stuff, and then spinlock forever (unless you count the shitty shell I made where the user could... I don't even remember anymore, I think they could reboot?)
and yeah that was terrible, so I killed it
I think I'm gonna go do some research
alright, I think I've decided how I want to set it up
Not what spinlock means
while (true) {}... unless I'm mistaken this is a spinlock, just with no way to.. well.. unlock it
hence, "forever"
you cant call it a spinlock because there is no lock its just an infinite loop
It's a spinlock if you're an idiot
yeah, I see that now that I actually think about it
but yeah, my plan is this
the console driver will have a configurable "write" function, which tells it how it's supposed to actually write characters, like to the framebuffer, VGA text mode, serial, port E9, etc
when the kernel initializes, it will have the following logic:
- Was serial tested to work?
- YES: Set the write function to log to serial
- NO: Set it to a stub that writes everything to a buffer in RAM, if the system comes up successfully, dump the buffer to the framebuffer
- Call `ARCH_Init();`, a wrapper that calls the appropriate function for the platform (e.g. `x86_64_Init();`, `i386_Init()`, `AArch64_Init()`, etc)
- Since x86_64 is the only platform supported currently, so nothing else really makes a difference
- Init GDT, IDT, Excpetion handlers
- Init anything else that belongs here that I'm not thinking of atm
- `PMM_Init()`, which will use the memory map from Limine, but it converts
it to a custom format first, in such a way that it's expandable to other initial memory map formats
- Init flanterm with the newly acquired memory
- Set the console write function to flanterm
- more goes on after here ofc, but this is all that I'm planning for now
this might not look any different from the last picture, because it isn't, but now it's going through my configurable TTY_Write wrapper
wtf, now randomly limine just stopped giving an SMP response
???? now it only gives a response if flags is 0
ig no X2APIC for me then.... weird.....
it's so weird, this works:
volatile struct limine_smp_request smpRequest = {
.id = LIMINE_SMP_REQUEST,
.revision = 0,
.flags = 0
};
and I get this back
SMP Response Info:
- Response revision: 0
- Number of CPUs: 2
- Bootstrap Processor Local APIC ID: 0
- X2APIC: Disabled
but when I try to enable the X2APIC with .flags = 1, what i get back is a NULL pointer:
if (smpRequest.response == NULL) {
puts("Limine didn't give us a valid SMP response...\r\n");
}
output:
Limine didn't give us a valid SMP response...
welp, never got X2APIC working, but I ported over my old interrupts code, cleaning it up a bit where I could, and now it works
(not messing with the GDT is probably gonna cause issues in the future, but it's useful for early boot, in case I ever need to call back into Limine code)
What's the point of the first message lol
to ensure that the function was actually getting called mainly
doesn't really serve much of a purpose now
this shouldn't happen
i will take a look at it
ah, alright
huh
ok, I can confirm that in Bochs, emulating a machine with no X2APIC support, it sets everything correctly, and says that the X2APIC is disabled
let me test in QEMU again (where I had issues before)
and there it's NULL
in case you want to test it quickly, my exact qemu command line is this, in the mean time, I'll see if I can get a minimal reproducible example on my machine
qemu-system-x86_64 -no-shutdown -no-reboot -m 1024M -smp 2 -cpu core2duo --enable-kvm -cdrom bin/TFOS_ISO.iso -display gtk -s -d cpu_reset,int,pcall,unimp $(EXTRAARGS) -serial stdio
here, this is pretty much the minimum to reproduce the issue
to build, make clean all. It will auto download all of the files it needs from the limine github repo
to test in QEMU, where it dies, make run after building
to test with bochs, where it works, ./bochs.sh after building, after it gets to the black screen, you can kill it and check log/COM1 for the serial output
when it works, it'll print the info, 2 cores, etc
when it doesn't work (limine gives NULL), it'll print that it didn't give a valid response
hm, also of note, after adjusting the QEMU command -cpu max rather than core2duo, it enables the X2APIC properly..... weird
so the bug is basically, under QEMU, if the CPU doesn't support X2APIC (or at least, is a core 2 duo, I didn't test anything else), rather than just saying that the X2APIC wasn't enabled in the flags, it just doesn't give a response at all
you're not supposed to use anything other than -cpu host with -enable-kvm
it's a qemu bug or general misfeature to allow it
You probably want to replace the GDT though
ah, I'll look into it more on other hardware then, see if it's just a weird QEMU issue causing that
Also yeah, I realized that now, nothing is working
I thought I could get away with it, because Limine's is pretty sane, but I guess not ¯_(ツ)_/¯
ooh fun, I love it when my existing code, that I intentionally went for because it just works, doesn't just work
ok, stepping through it with GDB, my GDT and IDT are loaded
then why does it explode
in x86_Exceptions_Init it seems.... huh
???? it calls an int3
I know i didn't intentionally put a breakpoint there or anything
void x86_64_Init() {
x86_GDT_Init();
x86_IDT_Init();
x86_Exceptions_Init();
}
Dump of assembler code for function x86_64_Init:
0xffffffff80001587 <+0>: push %rbp
0xffffffff80001588 <+1>: mov %rsp,%rbp
0xffffffff8000158b <+4>: call 0xffffffff80001be1 <x86_GDT_Init>
0xffffffff80001590 <+9>: call 0xffffffff80001dbe <x86_IDT_Init>
0xffffffff80001595 <+14>: call 0xffffffff80001868 <x86_Exceptions_Init>
=> 0xffffffff8000159a <+19>: int3
0xffffffff8000159b <+20>: pop %rbp
0xffffffff8000159c <+21>: ret
right before it cleans up the stack and gets out of there too, weird....
I wonder if it's actually there in the binary or if gdb is just being weird
no, it's just a nop
??
weird....
check your compiler warnings and fix them
oh, using the system's GCC? Yeah that's known to have some issue, I don't test it very often, I usually just use my cross compiler
I do
I enable way more warning than that
but there are no warnings when I use the cross compiler
well you could still be triggering UB hence the int3
yeah I checked, in the binary it was just a nop
it was something that gdb did
for some reason....
gdb doesn't do stuff though
atleast if you dont tell it to
picked up work on this again a bit
going back to school soon, so I'm gonna try to get as much done while I still can
WHAT???
lol, yeah that was a while ago, basically I had just discovered the __builtin_*x* functions and didn't check that they were actually what I thought they were, and got so confused nothing worked
oh... I just realized why SSE and floating points weren't working
I check for SSE, but I never actually enable it....
sigh now my panic code is all wrong
im sorry but how exactly do you mess up a print and a 1: hlt; jmp 1b? 
im not judging, no no, i would never do that... just curious :^) ive done worse things
the printing part
ah fair
I would need to look into it more
I got sidetracked with other things
I think my interrupt handler isn't proplerly pushing all of the registers on to the stack
which is weird because it worked before
weirdly enough, it works until it prints the error code
and then everything after that just craps itself
either completely invalid, or the wrong register
which makes 0 sense
yeah, this is the issue, it prints everything up until the error code correctly, then it somehow does:
- err is nothing from the QEMU log
- CR2 might be correct, but it's just 0, so no real way to tell
- RIP no idea, according to the backtrace, I was never at 0x00
- RBP is correct
- RSP is too generic to tell what it's actually pulling from
- CS is RIP
- SS is RSP
- flags is CS
looking at my struct, it looks like something is off by 1 variable
everything has just been shifted left by 1
error code doesnt get pushed at all if there is none
make sure you are handling that
yup, I do
well you clearly arent handling it correctly
yes, I know
the weird thing is that it worked before
I have no idea what I did that broke it
literally the only differences are renaming stuff
wtf
wtf.... by not handling it, it works
I must be going insane
oh great, now sprintf is #GPing at the very start, according to addrline, this is to blame (something in the setup ig?)
int sprintf(char* s, const char* format, ...) {
hang on, I'll disassemble it and see what the exact instruction is
ah, the first possible SSE instruction.... great
I love it when I enable SSE and it still doesn't work.....
I swear i'm so confused by this
I've never been able to get SSE working after switching to Limine
let me go back and check, but I don't remember seeing anything about SSE when looking at Limine docs
yeah, the only mention I can see of SSE in Limine is that there's no need to save SSE registers when using limine_terminal, so I doubt Limine is the issue... maybe I'm just going insane, I'll test it on a real PC
That can't be the issue
since sprintf works other times
wait no
it has a conditional branch just before this
so then, yeah SSE must just not be working, I can't think of any other reason this would cause a #GP
movaps %xmm0,-0x80(%rbp)
actually, I might have just found the issue, and it seems like it's actually an issue with GCC
When the source or destination operand is a memory operand, the operand must be aligned on a 16-byte (128-bit version), 32-byte (VEX.256 encoded version) or 64-byte (EVEX.512 encoded version) boundary or a general-protection exception (#GP) will be generated.
and %rbp was not 16-byte aligned 0xFFFF80003FF6EED8, causing the offset to not be 16-byte aligned, causing it to #GP
probably just using a broken version of GCC, let me just go rebuild my toolchain
Why are you using sse in the kernel
Compile with -mno-sse -mno-sse2 etc
not very helpful.... I've always used SSE and it's always worked
It shouldn't have
You absolutely do not want sse in the kernel
You'd need to manage the kernel state as well as thread states
Which leads to slowdowns
For what? a bit more performance
Even then if you're using sprintf before enabling sse then that won't work
So just compile with the proper arguments (-mgeneral-regs-only work too)
I don't, I enable SSE before even calling into C code
mainly performance, because I don't do any context switches yet, so it just makes testing faster
i can confirm that Limine has nothing to do with your SSE not working, btw
it cannot have anything to do with it, there isn't like
a way to permanently disable SSE or something
yeah, it's just weird that it worked under bootboot, but after switching to Limine it's the biggest issue i've had that just manifests in the most random places
what's even weirder, is that the code itself is fine
(since my code is written using a kind of libc, that's what made this so easy)
I ported the PMM memory map printing code, and the logging code, as well as the entire printf implementation into it's own program
I compiled it with the same flags that I did for the kernel (-march=core2, which enables SSE), and it works almost fine, printing the size of the range is kinda messed up, but it mostly works, and doesn't crash
yup, I can execute that instruction just fine on linux
so something is horribly wrong
I mean yeah you'd hope it works under Linux
I'll jump on the "why sse in the kernel" band wagon
But also there's a very specific set of steps for enabling sse, they're outlined in volume 3 of the sdm
Somewhere around chapter 15 iirc, not at a computer so i can't say for Sue
Yeah, just read through that section, and I was doing everything almost correct, I switched setting CR0 & CR4 around to match, however it still doesn't work....
I honestly wonder if GCC is just generating some funky code, since I remember reading that, should the operands be memory, it needs to be 16-byte aligned, however, when that code getting called, %rbp is not aligned
honestly I find it unlikely that GCC of all things is the issue. I would double check your sse init and your reasons for supporting it in the kernel.
again, I've enabled SSE before, and even if I just grab an exact copy of my old code, that I KNOW worked under bootboot, it just doesn't work....
also, it is entirely possible, my cross compiler is based off the latest GCC sources from git, because I never implemented a way to go find the latest stable tarball into the script, it just git clones the latest source code, so it's possible that something is bugged
between a whole month though? (07/16 was the old version, last one was compiled just yesterday, 08/03), with that idk if it's actually a GCC issue
but yeah, my old kernel could handle SSE just fine, but immediately after porting to Liminem, that was the first issue I had, that SSE was just.... not getting enabled somehow
so I'm writing from the perspecive of someone fairly familiar with the limine boot protocol. It makes no guartenees about SSE, so this sounds like you're relying on some accidental behaviour of bootboot and carrying that over to your limine implementation.
yes, that's... the entire point is that it makes no guarantees about SSE, and that's fine
bootboot didn't either
I tried to manually enable it, exactly like I do now, when I was using bootboot
but it actually worked there, and I have no idea why it isn't here
maybe you need to debug it :^)
@hexed citrus UEFI runs with SSE on by default, so while bootboot doesn't enable it, it's already on (and same for qemu -kernel iirc)
sounds like your SSE init has always been broken
no, because I remeber needing it, otherwise I would get #UD
I still have the code, I can go check
again I would ask you to ask whether SSE in the kernel is worth it
yeah, I still see no reason why not to
as I've mentioned multiples, if, like yall said, (if) once I ever get to the point of doing context switching, if it becomes a problem to need to save the additional registers, then I'll just disable it
until then, I'm going to continue trying to get what should be a very basic feature working
Lots of reasons why not to do it
You‘re somehow stuck on that very basic feature since a week or two
did you set MXCSR the way the compiler expects it to be?
if it expects it to be set up in the same way that then intel manual says, then yes
give me a sec and I'll check, juggling 3 things at once here
alright, i found a table online of the bitmask values for those bits, and if I'm not mistaken, that should end up as 0x1F80, but after moving that into memory, and running ldmxcsr on it, SSE still doesn't work
I'll go check out that relavent section of the SysV ABI and see if I'm missing something
also, for all of you saying to just disable SSE, for the heck of it, I tried it:
stdio/printf.c:342:15: error: SSE register return with SSE disabled
342 | static double applyScaling(double num, scalingFactor_t normalization) {
| ^~~~~~~~~~~~
doesn't even work
yeah, and that just doesn't work, I use floats in at least a few places
welp, back to trying to get SSE to work once again
get sse to work so that you have to remove it 5 seconds later when you need to do context switching
Don't use floats
also i think you will need to save sse registers in interrupts too if you enable it in kernel
if you replace "seconds" with "months" there, maybe
I'm going back to school in 2 more days, progress is going to grind to a halt
if only I could still print numbers with decimal point in them, and without entirely rewriting printf (where that error came from), I would do that
but there's just no way that I can think of, without making any time that I need to do anything with decimal numbers really paniful
And why do you need to print numbers with decimals?
why would you ever need to do anything with decimal numbers in kernel
And you can just use the remainder
also i dont think you need to rewrite the printf you are using, it has some #define settings to turn off all the float stuff
I would need to look into it, but I think that then, I would no longer be able to print floats, which like I said is fine, as long as I can mimic the functionality (to a certain extent at least), and it doesn't make the code super ugly by doing it
Now I haven't looked into using the remainder to determine the decimal place, and I have no idea if there is a more optimized way to do it, but what I imagine is that I would need to loop, doing division, until it can calculate every digit that it needs, and then pack each digit into an int
you dont need to make it so complicated