Last article got up through allocating the memory for the ACPI Tables. Now we want to populate them.
There are many lines between the allocation of the space for the tables and the finalization of the ACPI early initialization. Why? My guess is that things in-between can either lazy load entries or depend on the ACPI table without caring what is in it.
Here is a streamlined view of the __init function from the acpi initialization until the ending of the acpi initialization:
acpi_early_init(); if (late_time_init) late_time_init(); sched_clock_init(); calibrate_delay(); pid_idr_init(); anon_vma_init(); thread_stack_cache_init(); cred_init(); fork_init(); proc_caches_init(); uts_ns_init(); key_init(); security_init(); dbg_late_init(); net_ns_init(); vfs_caches_init(); pagecache_init(); signals_init(); seq_file_init(); proc_root_init(); nsfs_init(); cpuset_init(); cgroup_init(); taskstats_init_early(); delayacct_init(); poking_init(); check_bugs(); acpi_subsystem_init(); arch_post_acpi_subsys_init(); kcsan_init();
I can make some guesses what happens in between the resize and the init functions. We can’t really set up a memory manager if we don’t know the memory layout. More to the point that I am looking at: we need to know the CPU topology. The Ampere Computing Altra Max system that I am working on has 80 cores.
I think I want to look at what is done in two ACPI functions at the end, and then explore the code in between. The call chain goes to acpi_subsystem_init which is a thin wrapper around acpi_enable_subsystem. A couple comments in this function may tell why we had to waarch_post_acpi_subsys_initit this long to enable the ACPI code (although not why we needed the memory so early)
Initialize ACPI Event handling (Fixed and General Purpose)
Note1: We must have the hardware and events initialized before we can execute any control methods safely. Any control method can require ACPI hardware support, so the hardware must be fully initialized before any method execution!
Note2: Fixed events are initialized and enabled here. GPEs are initialized, but cannot be enabled until after the hardware is completely initialized (SCI and global_lock activated) and the various initialization control methods are run (_REG, _STA, _INI) on the entire namespace.https://elixir.bootlin.com/linux/latest/source/drivers/acpi/acpica/utxfinit.c#L152
Reading through the positive thread that these methods call I can see it is mostly, initialization of values in global structures. the ev_create_gpeblock functions are somewhat longer and have a bit of logic in them. I’m putting the links here so I can go back and look deeper at them later. I get the geernal gist of things; we have a bunch of global flags that are initially set to disable functionality. Once these data structures are initialized, we can enable it, and the multi-threaded nature of the Kernel can take over at any time after that.arch_post_acpi_subsys_init
The final call is to the architecture specific code: arch_post_acpi_subsys_init which is a no-op in ARM64. I d find it interesting that there is so much x86_64 specific code, and so little ARM64 specific code in this path. I suspect that shows the maturiy of ACPI on the x86_64 server platform, and we’ll see two things happen: more ARM64 specific code, and a reworking of the x86_64 specific code to be more platform agnostic, as it will also cover the ARM cases. We’ll see.
At this point, I wonder if I have traced enough code to start understanding the rationale for the initial patch: At a minimunm, can I trace when that code should be executed?
- The change is in the function secondary_start_kernel. What calls that? It looks like it is called from Architecture specific assembly code:
- Working backwards up a call stack we can see a handful of assembly which tops out with secondary_holding_pen.
- that is called by smp_spin_table_cpu_prepare which looks like it is an entry in a structure of function pointers:
- This is called from a few place; I note that smp_prepare_cpus looks like a good candidate and sure enough,
- it is called from init in the function kernel_init_freeable.
- that is called from kernel_init
- which is called from rest_init
- which is called from arch_call_rest_init…looks like architecture specific hook?
- Finally, we can see that is called just after the ACPI code we were looking at before.
With those broad strokes established, it is time to dig in more in to ACPI. What are these tables and the events, and the relationships between them? When do the tables get built? And we still don’t know why the tables are built so far before the the code that initializes ACPI. All this and more in future articles.